aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple
diff options
context:
space:
mode:
authorPerry Hung <iperry@gmail.com>2011-02-27 03:07:31 -0500
committerPerry Hung <iperry@gmail.com>2011-02-27 04:58:43 -0500
commitdef4173e683c3538388aabceeb08e5336c2bdadf (patch)
tree54e2571fb12b31d6730eb1a1b33d09fc53e003a8 /libmaple
parentc8ca7a2eb7528462677c80497854b940931eab16 (diff)
downloadlibrambutan-def4173e683c3538388aabceeb08e5336c2bdadf.tar.gz
librambutan-def4173e683c3538388aabceeb08e5336c2bdadf.zip
libmaple: Refactor ADC routines
ADC routines rewritten, support for ADC2, 3, added. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
Diffstat (limited to 'libmaple')
-rw-r--r--libmaple/adc.c140
-rw-r--r--libmaple/adc.h193
-rw-r--r--libmaple/libmaple.h5
-rw-r--r--libmaple/rcc.c1
-rw-r--r--libmaple/rcc.h9
-rw-r--r--libmaple/util.c2
-rw-r--r--libmaple/util.h3
7 files changed, 232 insertions, 121 deletions
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))