aboutsummaryrefslogtreecommitdiffstats
path: root/src/hc32f460/adc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hc32f460/adc.c')
-rw-r--r--src/hc32f460/adc.c155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/hc32f460/adc.c b/src/hc32f460/adc.c
new file mode 100644
index 00000000..ef1e58fb
--- /dev/null
+++ b/src/hc32f460/adc.c
@@ -0,0 +1,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);