diff options
Diffstat (limited to 'libmaple/adc.c')
-rw-r--r-- | libmaple/adc.c | 142 |
1 files changed, 82 insertions, 60 deletions
diff --git a/libmaple/adc.c b/libmaple/adc.c index 3e6818c..cd71118 100644 --- a/libmaple/adc.c +++ b/libmaple/adc.c @@ -24,84 +24,88 @@ /** * @brief Analog to digital converter routines + * + * IMPORTANT: maximum external impedance must be below 0.4kOhms for 1.5 + * sample conversion time. + * + * At 55.5 cycles/sample, the external input impedance < 50kOhms. + * + * See stm32 manual RM008 for how to calculate this. */ #include "libmaple.h" #include "rcc.h" #include "adc.h" -/* The ADC input clock is generated from PCLK2/APB2 divided by a prescaler - * and it must not exceed 14MHz. - * - * ADC1 and ADC2 are clocked by APB2 - * - * 1) Power on by setting ADON in ADC_CR2 - * Conversion starts when ADON is set for a second time after some - * time t > t_stab. - * - * Up to 16 selected conversion must be selected in ADC_SQRx - * - * Single conversion mode: - * Set the ADON bit in the ADC_CR2 register - * Once the conversion is complete: - * Converted data is stored in ADC_DR - * EOC flag is set - * Interrupt is generated if EOCIE is set - * - * Calibration: - * Calibration is started by setting the CAL bit in the ADC_CR2 register. - * Once calibration is over, the CAL bit is reset by hardware and normal - * conversion can be performed. Calibrate at power-on. - * - * ALIGN in ADC_CR2 selects the alignment of data - * - * IMPORTANT: maximum external impedance must be below 0.4kOhms for 1.5 - * sample conversion time. - * - * At 55.5 cycles/sample, the external input impedance < 50kOhms*/ +adc_dev adc1 = { + .regs = ADC1_BASE, + .clk_id = RCC_ADC1 +}; +const adc_dev *ADC1 = &adc1; -void set_adc_smprx(adc_smp_rate smp_rate); +adc_dev adc2 = { + .regs = ADC2_BASE, + .clk_id = RCC_ADC2 +}; +const adc_dev *ADC2 = &adc2; -void adc_init(adc_smp_rate smp_rate) { - rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_6); - rcc_clk_enable(RCC_ADC1); - rcc_reset_dev(RCC_ADC1); +#ifdef STM32_HIGH_DENSITY +adc_dev adc3 = { + .regs = ADC3_BASE, + .clk_id = RCC_ADC3 +}; +const adc_dev *ADC3 = &adc3; +#endif - ADC_CR1 = 0; - /* Software triggers conversions */ - ADC_CR2 = CR2_EXTSEL_SWSTART | CR2_EXTTRIG; - ADC_SQR1 = 0; +static void adc_calibrate(const adc_dev *dev); - /* Set the sample conversion time. See note above for impedance - requirements. */ - adc_set_sample_rate(smp_rate); +/** + * @brief Initialize an ADC peripheral. Only supports software triggered + * conversions. + * @param dev ADC peripheral to initialize + * @param flags unused + */ +void adc_init(const adc_dev *dev, uint32 flags) { + /* Spin up the clocks */ + rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_6); + rcc_clk_enable(dev->clk_id); + rcc_reset_dev(dev->clk_id); - /* Enable the ADC */ - CR2_ADON_BIT = 1; + /* Software triggers conversions, conversion on external events */ + adc_set_extsel(dev, 7); + adc_set_exttrig(dev, 1); - /* Reset the calibration registers and then perform a reset */ - CR2_RSTCAL_BIT = 1; - while(CR2_RSTCAL_BIT) - ; + /* Enable the ADC */ + adc_enable(dev); - CR2_CAL_BIT = 1; - while(CR2_CAL_BIT) - ; + /* Calibrate ADC */ + adc_calibrate(dev); } - -void adc_disable(void) { - CR2_ADON_BIT = 0; +/** + * @brief Set external event select for regular group + * @param dev adc device + * @param trigger event to select. See ADC_CR2 EXTSEL[2:0] bits. + */ +void adc_set_extsel(const adc_dev *dev, uint8 trigger) { + uint32 cr2 = dev->regs->CR2; + cr2 &= ~ADC_CR2_EXTSEL; + cr2 |= (trigger & 0x7) << 17; + dev->regs->CR2 = cr2; } -/* Turn the given sample rate into values for ADC_SMPRx. (Don't - * precompute in order to avoid wasting space). - * - * Don't call this during conversion! + +/** + * @brief Turn the given sample rate into values for ADC_SMPRx. Don't + * call this during conversion. + * @param dev adc device + * @param smp_rate sample rate to set + * @see adc_smp_rate */ -void adc_set_sample_rate(adc_smp_rate smp_rate) { +void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate) { uint32 adc_smpr1_val = 0, adc_smpr2_val = 0; int i; + for (i = 0; i < 10; i++) { if (i < 8) { /* ADC_SMPR1 determines sample time for channels [10,17] */ @@ -110,6 +114,24 @@ void adc_set_sample_rate(adc_smp_rate smp_rate) { /* ADC_SMPR2 determines sample time for channels [0,9] */ adc_smpr2_val |= smp_rate << (i * 3); } - ADC_SMPR1 = adc_smpr1_val; - ADC_SMPR2 = adc_smpr2_val; + + dev->regs->SMPR1 = adc_smpr1_val; + dev->regs->SMPR2 = adc_smpr2_val; +} + +/** + * @brief Calibrate an ADC peripheral + * @param dev adc device + */ +static void adc_calibrate(const adc_dev *dev) { + __io uint32 *rstcal_bit = (__io uint32*)BITBAND_PERI(&(dev->regs->CR2), 3); + __io uint32 *cal_bit = (__io uint32*)BITBAND_PERI(&(dev->regs->CR2), 2); + + *rstcal_bit = 1; + while (*rstcal_bit) + ; + + *cal_bit = 1; + while (*cal_bit) + ; } |