aboutsummaryrefslogtreecommitdiffstats
path: root/src/stm32/stm32h7_adc.c
diff options
context:
space:
mode:
authorD4SK <konstantin.vogel@gmx.net>2021-05-30 01:19:39 +0100
committerKevin O'Connor <kevin@koconnor.net>2021-10-06 18:20:29 -0400
commit0a55489e2c8899b2a5cff75dbbd40d7ed5791e22 (patch)
tree2855536ca3554470cef04f11955ef6ebf59c68a1 /src/stm32/stm32h7_adc.c
parent28b3c9e88c2103e67e0aaeeecd79eb4b827fecb0 (diff)
downloadkutter-0a55489e2c8899b2a5cff75dbbd40d7ed5791e22.tar.gz
kutter-0a55489e2c8899b2a5cff75dbbd40d7ed5791e22.tar.xz
kutter-0a55489e2c8899b2a5cff75dbbd40d7ed5791e22.zip
stm32: Add initial support for stm32h7
Signed-off-by: Konstantin Vogel <konstantin.vogel@gmx.net> Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src/stm32/stm32h7_adc.c')
-rw-r--r--src/stm32/stm32h7_adc.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/src/stm32/stm32h7_adc.c b/src/stm32/stm32h7_adc.c
new file mode 100644
index 00000000..792a3335
--- /dev/null
+++ b/src/stm32/stm32h7_adc.c
@@ -0,0 +1,234 @@
+// ADC functions on STM32H7
+//
+// Copyright (C) 2020 Konstantin Vogel <konstantin.vogel@gmx.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "board/irq.h" // irq_save
+#include "board/misc.h" // timer_from_us
+#include "command.h" // shutdown
+#include "compiler.h" // ARRAY_SIZE
+#include "generic/armcm_timer.h" // udelay
+#include "gpio.h" // gpio_adc_setup
+#include "internal.h" // GPIO
+#include "sched.h" // sched_shutdown
+
+// Number of samples is 2^OVERSAMPLES_EXPONENT (exponent can be 0-10)
+#define OVERSAMPLES_EXPONENT 3
+#define OVERSAMPLES (1 << OVERSAMPLES_EXPONENT)
+
+// LDORDY registers are missing from CMSIS (only available on revision V!)
+#define ADC_ISR_LDORDY_Pos (12U)
+#define ADC_ISR_LDORDY_Msk (0x1UL << ADC_ISR_LDORDY_Pos)
+#define ADC_ISR_LDORDY ADC_ISR_LDORDY_Msk
+
+DECL_CONSTANT("ADC_MAX", 4095);
+
+// GPIOs like A0_C are not covered!
+// This always gives the pin connected to the positive channel
+static const uint8_t adc_pins[] = {
+ // ADC1
+ 0, // PA0_C ADC12_INP0
+ 0, // PA1_C ADC12_INP1
+ GPIO('F', 11), // ADC1_INP2
+ GPIO('A', 6), // ADC12_INP3
+ GPIO('C', 4), // ADC12_INP4
+ GPIO('B', 1), // ADC12_INP5
+ GPIO('F', 12), // ADC1_INP6
+ GPIO('A', 7), // ADC12_INP7
+ GPIO('C', 5), // ADC12_INP8
+ GPIO('B', 0), // ADC12_INP9
+ GPIO('C', 0), // ADC123_INP10
+ GPIO('C', 1), // ADC123_INP11
+ GPIO('C', 2), // ADC123_INP12
+ GPIO('C', 3), // ADC12_INP13
+ GPIO('A', 2), // ADC12_INP14
+ GPIO('A', 3), // ADC12_INP15
+ GPIO('A', 0), // ADC1_INP16
+ GPIO('A', 1), // ADC1_INP17
+ GPIO('A', 4), // ADC12_INP18
+ GPIO('A', 5), // ADC12_INP19
+ // ADC2
+ 0, // PA0_C ADC12_INP0
+ 0, // PA1_C ADC12_INP1
+ GPIO('F', 13), // ADC2_INP2
+ GPIO('A', 6), // ADC12_INP3
+ GPIO('C', 4), // ADC12_INP4
+ GPIO('B', 1), // ADC12_INP5
+ GPIO('F', 14), // ADC2_INP6
+ GPIO('A', 7), // ADC12_INP7
+ GPIO('C', 5), // ADC12_INP8
+ GPIO('B', 0), // ADC12_INP9
+ GPIO('C', 0), // ADC123_INP10
+ GPIO('C', 1), // ADC123_INP11
+ GPIO('C', 2), // ADC123_INP12
+ GPIO('C', 3), // ADC12_INP13
+ GPIO('A', 2), // ADC12_INP14
+ GPIO('A', 3), // ADC12_INP15
+ 0, // dac_out1
+ 0, // dac_out2
+ GPIO('A', 4), // ADC12_INP18
+ GPIO('A', 5), // ADC12_INP19
+ // ADC3
+ 0, // PC2_C ADC3_INP0
+ 0, // PC3_C ADC3_INP1
+ GPIO('F', 9) , // ADC3_INP2
+ GPIO('F', 7), // ADC3_INP3
+ GPIO('F', 5), // ADC3_INP4
+ GPIO('F', 3), // ADC3_INP5
+ GPIO('F', 10), // ADC3_INP6
+ GPIO('F', 8), // ADC3_INP7
+ GPIO('F', 6), // ADC3_INP8
+ GPIO('F', 4), // ADC3_INP9
+ GPIO('C', 0), // ADC123_INP10
+ GPIO('C', 1), // ADC123_INP11
+ GPIO('C', 2), // ADC123_INP12
+ GPIO('H', 2), // ADC3_INP13
+ GPIO('H', 3), // ADC3_INP14
+ GPIO('H', 4), // ADC3_INP15
+ GPIO('H', 5), // ADC3_INP16
+ 0, // Vbat/4
+ 0, // VSENSE
+ 0, // VREFINT
+};
+
+
+// ADC timing:
+// ADC clock=30Mhz, Tconv=6.5, Tsamp=64.5, total=2.3666us*OVERSAMPLES
+
+struct gpio_adc
+gpio_adc_setup(uint32_t pin)
+{
+ // Find pin in adc_pins table
+ int chan;
+ for (chan=0; ; chan++) {
+ if (chan >= ARRAY_SIZE(adc_pins))
+ shutdown("Not a valid ADC pin");
+ if (adc_pins[chan] == pin)
+ break;
+ }
+
+ // Determine which ADC block to use, enable peripheral clock
+ // (SYSCLK 480Mhz) /HPRE(2) /CKMODE divider(4) /additional divider(2)
+ // (ADC clock 30Mhz)
+ ADC_TypeDef *adc;
+ if (chan >= 40){
+ adc = ADC3;
+ enable_pclock(ADC3_BASE);
+ MODIFY_REG(ADC3_COMMON->CCR, ADC_CCR_CKMODE_Msk,
+ 0b11 << ADC_CCR_CKMODE_Pos);
+ } else if (chan >= 20){
+ adc = ADC2;
+ RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN;
+ MODIFY_REG(ADC12_COMMON->CCR, ADC_CCR_CKMODE_Msk,
+ 0b11 << ADC_CCR_CKMODE_Pos);
+ } else{
+ adc = ADC1;
+ RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN;
+ MODIFY_REG(ADC12_COMMON->CCR, ADC_CCR_CKMODE_Msk,
+ 0b11 << ADC_CCR_CKMODE_Pos);
+ }
+ chan %= 20;
+
+ // Enable the ADC
+ if (!(adc->CR & ADC_CR_ADEN)){
+ // Pwr
+ // Exit deep power down
+ MODIFY_REG(adc->CR, ADC_CR_DEEPPWD_Msk, 0);
+ // Switch on voltage regulator
+ adc->CR |= ADC_CR_ADVREGEN;
+ while(!(adc->ISR & ADC_ISR_LDORDY))
+ ;
+ // Set Boost mode for 25Mhz < ADC clock <= 50Mhz
+ MODIFY_REG(adc->CR, ADC_CR_BOOST_Msk, 0b11 << ADC_CR_BOOST_Pos);
+
+ // Calibration
+ // Set calibration mode to Single ended (not differential)
+ MODIFY_REG(adc->CR, ADC_CR_ADCALDIF_Msk, 0);
+ // Enable linearity calibration
+ MODIFY_REG(adc->CR, ADC_CR_ADCALLIN_Msk, ADC_CR_ADCALLIN);
+ // Start the calibration
+ MODIFY_REG(adc->CR, ADC_CR_ADCAL_Msk, ADC_CR_ADCAL);
+ while(adc->CR & ADC_CR_ADCAL)
+ ;
+
+ // Enable ADC
+ // "Clear the ADRDY bit in the ADC_ISR register by writing ‘1’"
+ adc->ISR |= ADC_ISR_ADRDY;
+ adc->CR |= ADC_CR_ADEN;
+ while(!(adc->ISR & ADC_ISR_ADRDY))
+ ;
+
+ // Set 64.5 ADC clock cycles sample time for every channel
+ // (Reference manual pg.940)
+ uint32_t aticks = 0b101;
+ // Channel 0-9
+ adc->SMPR1 = (aticks | (aticks << 3) | (aticks << 6)
+ | (aticks << 9) | (aticks << 12) | (aticks << 15)
+ | (aticks << 18) | (aticks << 21) | (aticks << 24)
+ | (aticks << 27));
+ // Channel 10-19
+ adc->SMPR2 = (aticks | (aticks << 3) | (aticks << 6)
+ | (aticks << 9) | (aticks << 12) | (aticks << 15)
+ | (aticks << 18) | (aticks << 21) | (aticks << 24)
+ | (aticks << 27));
+ // Disable Continuous Mode
+ MODIFY_REG(adc->CFGR, ADC_CFGR_CONT_Msk, 0);
+ // Set to 12 bit
+ MODIFY_REG(adc->CFGR, ADC_CFGR_RES_Msk, 0b110 << ADC_CFGR_RES_Pos);
+ // Set hardware oversampling
+ MODIFY_REG(adc->CFGR2, ADC_CFGR2_ROVSE_Msk, ADC_CFGR2_ROVSE);
+ MODIFY_REG(adc->CFGR2, ADC_CFGR2_OVSR_Msk,
+ (OVERSAMPLES - 1) << ADC_CFGR2_OVSR_Pos);
+ MODIFY_REG(adc->CFGR2, ADC_CFGR2_OVSS_Msk,
+ OVERSAMPLES_EXPONENT << ADC_CFGR2_OVSS_Pos);
+ }
+ gpio_peripheral(pin, GPIO_ANALOG, 0);
+ // Preselect (connect) channel
+ adc->PCSEL |= (1 << chan);
+ return (struct gpio_adc){ .adc = 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)
+{
+ ADC_TypeDef *adc = g.adc;
+ // Conversion ready
+ // EOC flag is cleared by hardware when reading DR
+ // the channel condition only works if this ist the only channel
+ // on the sequence and length set to 1 (ADC_SQR1_L = 0000)
+ if (adc->ISR & ADC_ISR_EOC && adc->SQR1 == (g.chan << 6))
+ return 0;
+ // Conversion started but not ready or wrong channel
+ if (adc->CR & ADC_CR_ADSTART)
+ return timer_from_us(10);
+ // Start sample
+ adc->SQR1 = (g.chan << 6);
+ adc->CR |= ADC_CR_ADSTART;
+ // Should take 2.3666us, add 1us for clock synchronisation etc.
+ return timer_from_us(1 + 2.3666*OVERSAMPLES);
+}
+
+// Read a value; use only after gpio_adc_sample() returns zero
+uint16_t
+gpio_adc_read(struct gpio_adc g)
+{
+ ADC_TypeDef *adc = g.adc;
+ return adc->DR;
+}
+
+// Cancel a sample that may have been started with gpio_adc_sample()
+void
+gpio_adc_cancel_sample(struct gpio_adc g)
+{
+ ADC_TypeDef *adc = g.adc;
+ irqstatus_t flag = irq_save();
+ // ADSTART is not as long true as SR_STRT on stm32f4
+ if ((adc->CR & ADC_CR_ADSTART || adc->ISR & ADC_ISR_EOC)
+ && adc->SQR1 == (g.chan << 6))
+ gpio_adc_read(g);
+ irq_restore(flag);
+}