aboutsummaryrefslogtreecommitdiffstats
path: root/src/hc32f460/adc.c
blob: ef1e58fb2a94902e03f033dc7545ae2d27a65da1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// ADC functions on Huada HC32F460
//
// Copyright (C) 2022  Steven Gotthardt <gotthardt@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

#include "generic/misc.h"   // timer_from_us
#include "command.h"        // shutdown
#include "board/gpio.h"     // gpio_adc_setup
#include "board/internal.h" // GPIO
#include "sched.h"          // sched_shutdown

// library
#include "hc32f460_adc.h"
#include "hc32f460_pwc.h"
#include "hc32f460_gpio.h"

#define ADC_RESOLUTION_12BIT        (12u)
#define ADC_RESOLUTION_10BIT        (10u)
#define ADC_RESOLUTION_8BIT         (8u)

#define ADC1_RESOLUTION             (ADC_RESOLUTION_12BIT)
#define ADC1_PRECISION              (1ul << ADC1_RESOLUTION)

#if ADC1_RESOLUTION == ADC_RESOLUTION_12BIT
#define AdcResolution   AdcResolution_12Bit
#elif ADC1_RESOLUTION == ADC_RESOLUTION_10BIT
#define AdcResolution   AdcResolution_10Bit
#else
#define AdcResolution   AdcResolution_8Bit
#endif


/* Timeout value definitions. Found in example code */
#define TIMEOUT_VAL     (30u)

DECL_CONSTANT("ADC_MAX", ADC1_PRECISION-1);

// These pins can be used for ADC
static const uint8_t adc_gpio[] = {
    GPIO('A', 0),   // Chan 0
    GPIO('A', 1),   // Chan 1
    GPIO('A', 2),   // Chan 2
    GPIO('A', 3),   // Chan 3
    GPIO('A', 4),   // Chan 4
    GPIO('A', 5),   // Chan 5
    GPIO('A', 6),   // Chan 6
    GPIO('A', 7),   // Chan 7
    GPIO('B', 0),   // Chan 8
    GPIO('B', 1),   // Chan 9
    GPIO('C', 0),   // Chan 10  // TBed  on TriGorilla
    GPIO('C', 1),   // Chan 11  // THead on TriGorilla
    GPIO('C', 2),   // Chan 12
    GPIO('C', 3),   // Chan 13
    GPIO('C', 4),   // Chan 14  // TBed  on aquilla
    GPIO('C', 5),   // Chan 15  // THead on aquilla
};


struct gpio_adc
gpio_adc_setup(uint32_t gpio)
{
    // validate pin in adc_pins table
    int chan;
    for (chan=0; ; chan++)
    {
        if (chan >= ARRAY_SIZE(adc_gpio))
        {
            shutdown("Not a valid ADC pin");
        }
        if (adc_gpio[chan] == gpio)
        {
            break;
        }
    }

    // set as analog
    gpio_peripheral(gpio, Pin_Mode_Ana, 0);

    uint8_t sampleTime[ARRAY_SIZE(adc_gpio)] = { TIMEOUT_VAL };   // all chans
    stc_adc_ch_cfg_t stcAdcChan;
    stcAdcChan.u32Channel   = 1 << chan;
    stcAdcChan.u8Sequence   = ADC_SEQ_A;    // all conversions are in SEQ A
    stcAdcChan.pu8SampTime  = sampleTime;
    ADC_AddAdcChannel(M4_ADC1, &stcAdcChan);

    return (struct gpio_adc){ .chan = chan };
}


// Try to sample a value. Returns zero if sample ready, otherwise
// returns the number of clock ticks the caller should wait before
// retrying this function.
uint32_t
gpio_adc_sample(struct gpio_adc g)
{
    // true if the sequence is finished
    if (ADC_GetEocFlag(M4_ADC1, ADC_SEQ_A))
    {
         // all conversions are done - clear the flag
        ADC_ClrEocFlag(M4_ADC1, ADC_SEQ_A);
        return 0;
    }
    else if (M4_ADC1->STR & 1)
    {
        // running but not done yet
        return timer_from_us(TIMEOUT_VAL/2);
    }
    else
    {
        // not running - so start
        ADC_StartConvert(M4_ADC1);
    }

    return timer_from_us(TIMEOUT_VAL);
}


// Read a value; use only after gpio_adc_sample() returns zero
uint16_t
gpio_adc_read(struct gpio_adc g)
{
    // return the one we want...
    return ADC_GetValue(M4_ADC1, g.chan);
}


// Cancel a sample that may have been started with gpio_adc_sample()
void
gpio_adc_cancel_sample(struct gpio_adc g)
{
    ADC_StopConvert(M4_ADC1);
}


// The clocks are already set by the loader.
// There is ADC1 and ADC2. Sequences do all channels at once.
void
adc_init(void)
{
    //  PCLK2 (ADC clock) is 'divide by 4', Max ADC clock is 60MHz
    stc_adc_init_t stcAdcInit = {0};
    stcAdcInit.enResolution = AdcResolution;  // see define above
    stcAdcInit.enDataAlign  = AdcDataAlign_Right;
    stcAdcInit.enAutoClear  = AdcClren_Disable;
    stcAdcInit.enScanMode   = AdcMode_SAOnce;

    // power-on ADC
    PWC_Fcg3PeriphClockCmd(PWC_FCG3_PERIPH_ADC1, Enable);

    // only using ADC1
    ADC_Init(M4_ADC1, &stcAdcInit);
}

DECL_INIT(adc_init);