From def4173e683c3538388aabceeb08e5336c2bdadf Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Sun, 27 Feb 2011 03:07:31 -0500 Subject: libmaple: Refactor ADC routines ADC routines rewritten, support for ADC2, 3, added. Signed-off-by: Marti Bolivar --- libmaple/adc.c | 140 +++++++++++++++++++++---------------- libmaple/adc.h | 193 +++++++++++++++++++++++++++++++++++++--------------- libmaple/libmaple.h | 5 ++ libmaple/rcc.c | 1 + libmaple/rcc.h | 9 ++- libmaple/util.c | 2 +- libmaple/util.h | 3 +- 7 files changed, 232 insertions(+), 121 deletions(-) (limited to 'libmaple') diff --git a/libmaple/adc.c b/libmaple/adc.c index 3e6818c..adadc63 100644 --- a/libmaple/adc.c +++ b/libmaple/adc.c @@ -24,82 +24,85 @@ /** * @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 = (adc_reg_map*)ADC1_BASE, + .clk_id = RCC_ADC1 +}; +const adc_dev *ADC1 = &adc1; -void set_adc_smprx(adc_smp_rate smp_rate); +adc_dev adc2 = { + .regs = (adc_reg_map*)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); +#if NR_ADCS >= 3 +adc_dev adc3 = { + .regs = (adc_reg_map*)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(adc_reg_map *regs); - /* 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->regs, 7); + adc_set_exttrig(dev->regs, 1); - /* Reset the calibration registers and then perform a reset */ - CR2_RSTCAL_BIT = 1; - while(CR2_RSTCAL_BIT) - ; + /* Enable the ADC */ + adc_enable(dev->regs); - CR2_CAL_BIT = 1; - while(CR2_CAL_BIT) - ; + /* Calibrate ADC */ + adc_calibrate(dev->regs); } - -void adc_disable(void) { - CR2_ADON_BIT = 0; +/** + * @brief Set external event select for regular group + * @param regs adc register map + * @param trigger event to select. See ADC_CR2 EXTSEL[2:0] bits. + */ +void adc_set_extsel(adc_reg_map *regs, uint8 trigger) { + uint32 cr2 = regs->CR2; + cr2 &= ~ADC_CR2_EXTSEL; + cr2 |= (trigger & 0x7) << 17; + 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 regs adc register map + * @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(adc_reg_map *regs, adc_smp_rate smp_rate) { uint32 adc_smpr1_val = 0, adc_smpr2_val = 0; int i; for (i = 0; i < 10; i++) { @@ -110,6 +113,23 @@ 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; + regs->SMPR1 = adc_smpr1_val; + regs->SMPR2 = adc_smpr2_val; +} + +/** + * @brief Calibrate an ADC peripheral + * @param regs adc register map + */ +static void adc_calibrate(adc_reg_map *regs) { + __io uint32 *rstcal_bit = (__io uint32*)BITBAND_PERI(&(regs->CR2), 3); + __io uint32 *cal_bit = (__io uint32*)BITBAND_PERI(&(regs->CR2), 2); + + *rstcal_bit = 1; + while (*rstcal_bit) + ; + + *cal_bit = 1; + while (*cal_bit) + ; } diff --git a/libmaple/adc.h b/libmaple/adc.h index 976986f..4997d14 100644 --- a/libmaple/adc.h +++ b/libmaple/adc.h @@ -25,87 +25,168 @@ /** * @file adc.h * - * @brief Analog-to-Digital Conversion (ADC) routines. + * @brief Analog-to-Digital Conversion (ADC) header. */ #ifndef _ADC_H_ #define _ADC_H_ #include "util.h" +#include "rcc.h" + #ifdef __cplusplus extern "C"{ #endif -/* Notes: - * The maximum input impedance on each channel MUST be below .4kohms, - * or face the wrath of incorrect readings... - * This can be changed at the expense of sample time... see datasheet - * - * Need to up the sample time if otherwise... see datasheet */ +typedef struct adc_reg_map { + __io uint32 SR; ///< Status register + __io uint32 CR1; ///< Control register 1 + __io uint32 CR2; ///< Control register 2 + __io uint32 SMPR1; ///< Sample time register 1 + __io uint32 SMPR2; ///< Sample time register 2 + __io uint32 JOFR1; ///< Injected channel data offset register 1 + __io uint32 JOFR2; ///< Injected channel data offset register 2 + __io uint32 JOFR3; ///< Injected channel data offset register 3 + __io uint32 JOFR4; ///< Injected channel data offset register 4 + __io uint32 HTR; ///< Watchdog high threshold register + __io uint32 LTR; ///< Watchdog low threshold register + __io uint32 SQR1; ///< Regular sequence register 1 + __io uint32 SQR2; ///< Regular sequence register 2 + __io uint32 SQR3; ///< Regular sequence register 3 + __io uint32 JSQR; ///< Injected sequence register + __io uint32 JDR1; ///< Injected data register 1 + __io uint32 JDR2; ///< Injected data register 2 + __io uint32 JDR3; ///< Injected data register 3 + __io uint32 JDR4; ///< Injected data register 4 + __io uint32 DR; ///< Regular data register +} adc_reg_map; + +typedef struct adc_dev { + adc_reg_map *regs; + rcc_clk_id clk_id; +} adc_dev; + +extern const adc_dev *ADC1; +extern const adc_dev *ADC2; +#if NR_ADCS >= 3 +extern const adc_dev *ADC3; +#endif -/* TODO: We'll only use ADC1 for now. See page 41 of the manual for - ADC2 and ADC3's real addresses. */ +/* + * ADC peripheral base addresses + */ #define ADC1_BASE 0x40012400 -#define ADC2_BASE 0x40012400 -#define ADC3_BASE 0x40012400 - -#define ADC_SR *(volatile uint32*)(ADC1_BASE + 0) -#define ADC_CR1 *(volatile uint32*)(ADC1_BASE + 0x4) -#define ADC_CR2 *(volatile uint32*)(ADC1_BASE + 0x8) -#define ADC_SMPR1 *(volatile uint32*)(ADC1_BASE + 0xC) -#define ADC_SMPR2 *(volatile uint32*)(ADC1_BASE + 0x10) -#define ADC_SQR1 *(volatile uint32*)(ADC1_BASE + 0x2C) -#define ADC_SQR3 *(volatile uint32*)(ADC1_BASE + 0x34) -#define ADC_DR *(volatile uint32*)(ADC1_BASE + 0x4C) - -#define CR2_EXTSEL_SWSTART (0xE << 16) -#define CR2_RSTCAL (BIT(3)) -#define CR2_EXTTRIG (BIT(20)) - -/* Bit banded bits */ -#define CR2_ADON_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0x8, 0)) -#define CR2_CAL_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0x8, 2)) -#define CR2_RSTCAL_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0x8, 3)) -#define CR2_SWSTART_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0x8, 22)) -#define SR_EOC_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0, 1)) -/* (NR_ANALOG_PINS is board specific) */ +#define ADC2_BASE 0x40012800 +#define ADC3_BASE 0x40013C00 + +/* + * Register bit definitions + */ + +/* Status register */ +#define ADC_SR_AWD BIT(0) +#define ADC_SR_EOC BIT(1) +#define ADC_SR_JEOC BIT(2) +#define ADC_SR_JSTRT BIT(3) +#define ADC_SR_STRT BIT(4) + +/* Control register 1 */ +#define ADC_CR1_AWDCH (0x1F) +#define ADC_CR1_EOCIE BIT(5) +#define ADC_CR1_AWDIE BIT(6) +#define ADC_CR1_JEOCIE BIT(7) +#define ADC_CR1_SCAN BIT(8) +#define ADC_CR1_AWDSGL BIT(9) +#define ADC_CR1_JAUTO BIT(10) +#define ADC_CR1_DISCEN BIT(11) +#define ADC_CR1_JDISCEN BIT(12) +#define ADC_CR1_DISCNUM (0xE000) +#define ADC_CR1_JAWDEN BIT(22) +#define ADC_CR1_AWDEN BIT(23) + +/* Control register 2 */ +#define ADC_CR2_ADON BIT(0) +#define ADC_CR2_CONT BIT(1) +#define ADC_CR2_CAL BIT(2) +#define ADC_CR2_RSTCAL BIT(3) +#define ADC_CR2_DMA BIT(8) +#define ADC_CR2_ALIGN BIT(11) +#define ADC_CR2_JEXTSEL (0x7000) +#define ADC_CR2_JEXTTRIG BIT(15) +#define ADC_CR2_EXTSEL (0xE0000) +#define ADC_CR2_EXTTRIG BIT(20) +#define ADC_CR2_JSWSTART BIT(21) +#define ADC_CR2_SWSTART BIT(22) +#define ADC_CR2_TSEREFE BIT(23) + +void adc_init(const adc_dev *dev, uint32 flags); +void adc_set_extsel(adc_reg_map *regs, uint8 trigger); /** ADC per-sample conversion times, in ADC clock cycles */ typedef enum { - ADC_SMPR_1_5, - ADC_SMPR_7_5, - ADC_SMPR_13_5, - ADC_SMPR_28_5, - ADC_SMPR_41_5, - ADC_SMPR_55_5, - ADC_SMPR_71_5, - ADC_SMPR_239_5 + ADC_SMPR_1_5, ///< 1.5 ADC cycles + ADC_SMPR_7_5, ///< 7.5 ADC cycles + ADC_SMPR_13_5, ///< 13.5 ADC cycles + ADC_SMPR_28_5, ///< 28.5 ADC cycles + ADC_SMPR_41_5, ///< 41.5 ADC cycles + ADC_SMPR_55_5, ///< 55.5 ADC cycles + ADC_SMPR_71_5, ///< 71.5 ADC cycles + ADC_SMPR_239_5 ///< 239.5 ADC cycles } adc_smp_rate; -/** Initialize ADC1 to do one-shot conversions at the given sample - rate. */ -void adc_init(adc_smp_rate smp_rate); - -void adc_set_sample_rate(adc_smp_rate smp_rate); - -void adc_disable(void); +void adc_set_sample_rate(adc_reg_map *regs, adc_smp_rate smp_rate); /** - * Perform a single conversion on ADC[0-15]. - * PRECONDITIONS: - * adc initialized */ -static inline int adc_read(int channel) { - /* Set channel */ - ADC_SQR3 = channel; + * @brief Perform a single synchronous software triggered conversion on a + * channel + * @param regs ADC register map + * @param channel channel to convert + * @return conversion result + */ +static inline uint32 adc_read(adc_reg_map *regs, uint8 channel) { + /* Set target channel */ + regs->SQR3 = channel; /* Start the conversion */ - CR2_SWSTART_BIT = 1; + regs->CR2 |= ADC_CR2_SWSTART; /* Wait for it to finish */ - while(SR_EOC_BIT == 0) + while((regs->SR & ADC_SR_EOC) == 0) ; - return ADC_DR; + return regs->DR; +} + +/** + * @brief Set external trigger conversion mode event for regular channels + * @param regs adc register map + * @param enable if 1, conversion on external events is enabled, 0 to disable + */ +static inline void adc_set_exttrig(adc_reg_map *regs, uint8 enable) { + __write(BITBAND_PERI(&(regs->CR2), 20), enable); +} + +/** + * @brief Enable an adc peripheral + * @param regs register map of peripheral to enable + */ +static inline void adc_enable(adc_reg_map *regs) { + __write(BITBAND_PERI(&(regs->CR2), 0), 1); +} + +/** + * @brief Disable an adc peripheral + * @param regs register map of peripheral to disable + */ +static inline void adc_disable(adc_reg_map *regs) { + __write(BITBAND_PERI(&(regs->CR2), 0), 0); +} + +/** + * @brief Disable all ADCs + */ +static inline void adc_disable_all(void) { + adc_disable(ADC1->regs); } #ifdef __cplusplus diff --git a/libmaple/libmaple.h b/libmaple/libmaple.h index 02e27d3..4f9a71a 100644 --- a/libmaple/libmaple.h +++ b/libmaple/libmaple.h @@ -61,6 +61,9 @@ /* Number of USART ports */ #define NR_USART 3 + /* Number of ADCs */ + #define NR_ADCS 2 + /* Has an FSMC bus? */ #define NR_FSMC 0 @@ -109,6 +112,7 @@ #define NR_BKP_REGS 42 /* TODO test on Native */ #define NR_TIMERS 8 #define NR_USART 5 /* NB: 4 and 5 are UART only */ + #define NR_ADCS 3 #define NR_FSMC 1 #define NR_DAC_PINS 2 @@ -147,6 +151,7 @@ #define NR_BKP_REGS 10 /* TODO test on Mini */ #define NR_TIMERS 4 #define NR_USART 3 + #define NR_ADCS 2 #define NR_FSMC 0 #define NR_DAC_PINS 0 diff --git a/libmaple/rcc.c b/libmaple/rcc.c index 6905c22..a9d7c96 100644 --- a/libmaple/rcc.c +++ b/libmaple/rcc.c @@ -54,6 +54,7 @@ static const struct rcc_dev_info rcc_dev_table[] = { [RCC_AFIO] = { .clk_domain = APB2, .line_num = 0 }, [RCC_ADC1] = { .clk_domain = APB2, .line_num = 9 }, [RCC_ADC2] = { .clk_domain = APB2, .line_num = 10 }, + [RCC_ADC3] = { .clk_domain = APB2, .line_num = 15 }, [RCC_USART1] = { .clk_domain = APB2, .line_num = 14 }, [RCC_USART2] = { .clk_domain = APB1, .line_num = 17 }, [RCC_USART3] = { .clk_domain = APB1, .line_num = 18 }, diff --git a/libmaple/rcc.h b/libmaple/rcc.h index 4b1f35d..5daca57 100644 --- a/libmaple/rcc.h +++ b/libmaple/rcc.h @@ -144,8 +144,10 @@ enum { RCC_PRESCALER_ADC }; -// RCC Devices -enum { +/* + * Identifies bus and clock line for a device + */ +typedef enum { RCC_GPIOA, RCC_GPIOB, RCC_GPIOC, @@ -156,6 +158,7 @@ enum { RCC_AFIO, RCC_ADC1, RCC_ADC2, + RCC_ADC3, RCC_USART1, RCC_USART2, RCC_USART3, @@ -175,7 +178,7 @@ enum { RCC_DAC, // High-density devices only (Maple Native) RCC_DMA1, RCC_DMA2, // High-density devices only (Maple Native) -}; +} rcc_clk_id; void rcc_clk_init(uint32 sysclk_src, uint32 pll_src, uint32 pll_mul); diff --git a/libmaple/util.c b/libmaple/util.c index 135f005..11f9b34 100644 --- a/libmaple/util.c +++ b/libmaple/util.c @@ -48,7 +48,7 @@ void _fail(const char* file, int line, const char* exp) { timer_disable_all(); /* Turn off ADC */ - adc_disable(); + adc_disable_all(); /* Turn off all usarts */ usart_disable_all(); diff --git a/libmaple/util.h b/libmaple/util.h index 64782d9..63427cc 100644 --- a/libmaple/util.h +++ b/libmaple/util.h @@ -44,7 +44,8 @@ /* Convert SRAM address */ #define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE+(a-BITBAND_SRAM_REF)*32+(b*4))) /* Convert PERI address */ -#define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE+(a-BITBAND_PERI_REF)*32+(b*4))) +#define BITBAND_PERI(a, b) ((BITBAND_PERI_BASE + \ + ((uint32)a - BITBAND_PERI_REF) * 32 + (b * 4))) #define REG_SET(reg, val) (*(volatile uint32*)(reg) = (val)) #define REG_SET_BIT(reg, bit) (*(volatile uint32*)(reg) |= BIT(bit)) -- cgit v1.2.3