aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-03-26 22:17:47 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-04-11 16:56:56 -0400
commit28825b6a2f66b0329229185eb9cbd9004fae4b1b (patch)
treee9714efb41196c4492b68ff878974d01981708cd
parent7a400b065167beda0a1a9c642954e08581bc0c29 (diff)
downloadlibrambutan-28825b6a2f66b0329229185eb9cbd9004fae4b1b.tar.gz
librambutan-28825b6a2f66b0329229185eb9cbd9004fae4b1b.zip
Resurrect ADC support.
Standard refactoring: add series headers for F1 and F2, along with series adc.c files. There are some issues relating to adc_extsel_event to hammer out later, but this will do for now. We also add some new portability interfaces to libmaple/adc.h in order for Wirish to use the same code to initialize the ADCs at init() time. As usual, F1 is untested. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
-rw-r--r--libmaple/adc.c74
-rw-r--r--libmaple/include/libmaple/adc.h175
-rw-r--r--libmaple/rules.mk4
-rw-r--r--libmaple/stm32f1/adc.c110
-rw-r--r--libmaple/stm32f1/include/series/adc.h254
-rw-r--r--libmaple/stm32f1/rules.mk3
-rw-r--r--libmaple/stm32f2/adc.c84
-rw-r--r--libmaple/stm32f2/include/series/adc.h331
-rw-r--r--libmaple/stm32f2/rules.mk1
-rw-r--r--wirish/boards.cpp15
-rw-r--r--wirish/boards_private.h12
-rw-r--r--wirish/stm32f1/boards_setup.cpp39
-rw-r--r--wirish/stm32f2/boards_setup.cpp32
13 files changed, 920 insertions, 214 deletions
diff --git a/libmaple/adc.c b/libmaple/adc.c
index 40369e9..acce923 100644
--- a/libmaple/adc.c
+++ b/libmaple/adc.c
@@ -25,44 +25,15 @@
*****************************************************************************/
/**
- * @file adc.c
- *
+ * @file libmaple/adc.c
+ * @author Marti Bolivar <mbolivar@leaflabs.com>,
+ * Perry Hung <perry@leaflabs.com>
* @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 RM0008 for how to calculate this.
*/
+#include <libmaple/adc.h>
#include <libmaple/libmaple.h>
#include <libmaple/rcc.h>
-#include <libmaple/adc.h>
-
-static adc_dev adc1 = {
- .regs = ADC1_BASE,
- .clk_id = RCC_ADC1
-};
-/** ADC1 device. */
-const adc_dev *ADC1 = &adc1;
-
-static adc_dev adc2 = {
- .regs = ADC2_BASE,
- .clk_id = RCC_ADC2
-};
-/** ADC2 device. */
-const adc_dev *ADC2 = &adc2;
-
-#ifdef STM32_HIGH_DENSITY
-adc_dev adc3 = {
- .regs = ADC3_BASE,
- .clk_id = RCC_ADC3
-};
-/** ADC3 device. */
-const adc_dev *ADC3 = &adc3;
-#endif
/**
* @brief Initialize an ADC peripheral.
@@ -91,20 +62,10 @@ void adc_set_extsel(const adc_dev *dev, adc_extsel_event event) {
}
/**
- * @brief Call a function on all ADC devices.
- * @param fn Function to call on each ADC device.
- */
-void adc_foreach(void (*fn)(const adc_dev*)) {
- fn(ADC1);
- fn(ADC2);
-#ifdef STM32_HIGH_DENSITY
- fn(ADC3);
-#endif
-}
-
-/**
- * @brief Turn the given sample rate into values for ADC_SMPRx. Don't
- * call this during conversion.
+ * @brief Set the sample rate for all channels on an ADC device.
+ *
+ * Don't call this during conversion.
+ *
* @param dev adc device
* @param smp_rate sample rate to set
* @see adc_smp_rate
@@ -127,25 +88,8 @@ void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate) {
}
/**
- * @brief Calibrate an ADC peripheral
- * @param dev adc device
- */
-void adc_calibrate(const adc_dev *dev) {
- __io uint32 *rstcal_bit = bb_perip(&(dev->regs->CR2), 3);
- __io uint32 *cal_bit = bb_perip(&(dev->regs->CR2), 2);
-
- *rstcal_bit = 1;
- while (*rstcal_bit)
- ;
-
- *cal_bit = 1;
- while (*cal_bit)
- ;
-}
-
-/**
* @brief Perform a single synchronous software triggered conversion on a
- * channel.
+ * channel.
* @param dev ADC device to use for reading.
* @param channel channel to convert
* @return conversion result
diff --git a/libmaple/include/libmaple/adc.h b/libmaple/include/libmaple/adc.h
index d531d00..be29676 100644
--- a/libmaple/include/libmaple/adc.h
+++ b/libmaple/include/libmaple/adc.h
@@ -1,6 +1,7 @@
/******************************************************************************
* The MIT License
*
+ * Copyright (c) 2012 LeafLabs, LLC.
* Copyright (c) 2010 Perry Hung.
*
* Permission is hereby granted, free of charge, to any person
@@ -25,9 +26,10 @@
*****************************************************************************/
/**
- * @file adc.h
- *
- * @brief Analog-to-Digital Conversion (ADC) header.
+ * @file libmaple/adc.h
+ * @author Marti Bolivar <mbolivar@leaflabs.com>,
+ * Perry Hung <perry@leaflabs.com>
+ * @brief Analog-to-Digital Conversion (ADC) header.
*/
#ifndef _LIBMAPLE_ADC_H_
@@ -40,6 +42,12 @@ extern "C"{
#include <libmaple/libmaple.h>
#include <libmaple/bitband.h>
#include <libmaple/rcc.h>
+/* We include the series header below, after defining the register map
+ * and device structs. */
+
+/*
+ * Register map
+ */
/** ADC register map type. */
typedef struct adc_reg_map {
@@ -71,24 +79,30 @@ typedef struct adc_dev {
rcc_clk_id clk_id; /**< RCC clock information */
} adc_dev;
-extern const adc_dev *ADC1;
-extern const adc_dev *ADC2;
-#ifdef STM32_HIGH_DENSITY
-extern const adc_dev *ADC3;
-#endif
-
-/*
- * Register map base pointers
+/* Pull in the series header (which may need the above struct
+ * definitions).
+ *
+ * IMPORTANT: The series header must define the following:
+ *
+ * - enum adc_extsel_event (and typedef to adc_extsel_event): One per
+ * external event used to trigger start of conversion of a regular
+ * group. If two different series support the same event as a
+ * trigger, they must use the same token for the enumerator for that
+ * event. (The value of the enumerator is of course allowed to be
+ * different).
+ *
+ * - enum adc_smp_rate (and typedef to adc_smp_rate): One per
+ * available sampling time. These must be in the form ADC_SMPR_X_Y
+ * for X.Y cycles (e.g. ADC_SMPR_1_5 means 1.5 cycles), or
+ * ADC_SMPR_X for X cycles (e.g. ADC_SMPR_3 means 3 cycles).
+ *
+ * - enum adc_prescaler (and typedef): One per available prescaler,
+ * suitable for adc_set_prescaler. Series which have the same
+ * prescaler dividers (e.g. STM32F1 and STM32F2 both divide PCLK2 by
+ * 2, 4, 6, or 8) must provide the same tokens as enumerators, for
+ * portability.
*/
-
-/** ADC1 register map base pointer. */
-#define ADC1_BASE ((struct adc_reg_map*)0x40012400)
-/** ADC2 register map base pointer. */
-#define ADC2_BASE ((struct adc_reg_map*)0x40012800)
-#ifdef STM32_HIGH_DENSITY
-/** ADC3 register map base pointer. */
-#define ADC3_BASE ((struct adc_reg_map*)0x40013C00)
-#endif
+#include <series/adc.h>
/*
* Register bit definitions
@@ -136,31 +150,9 @@ extern const adc_dev *ADC3;
/* 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_JEXTTRIG_BIT 15
-#define ADC_CR2_EXTTRIG_BIT 20
-#define ADC_CR2_JSWSTART_BIT 21
-#define ADC_CR2_SWSTART_BIT 22
-#define ADC_CR2_TSEREFE_BIT 23
-
-#define ADC_CR2_ADON BIT(ADC_CR2_ADON_BIT)
-#define ADC_CR2_CONT BIT(ADC_CR2_CONT_BIT)
-#define ADC_CR2_CAL BIT(ADC_CR2_CAL_BIT)
-#define ADC_CR2_RSTCAL BIT(ADC_CR2_RSTCAL_BIT)
-#define ADC_CR2_DMA BIT(ADC_CR2_DMA_BIT)
-#define ADC_CR2_ALIGN BIT(ADC_CR2_ALIGN_BIT)
-#define ADC_CR2_JEXTSEL (0x7000)
-#define ADC_CR2_JEXTTRIG BIT(ADC_CR2_JEXTTRIG_BIT)
-#define ADC_CR2_EXTSEL (0xE0000)
-#define ADC_CR2_EXTTRIG BIT(ADC_CR2_EXTTRIG_BIT)
-#define ADC_CR2_JSWSTART BIT(ADC_CR2_JSWSTART_BIT)
-#define ADC_CR2_SWSTART BIT(ADC_CR2_SWSTART_BIT)
-#define ADC_CR2_TSEREFE BIT(ADC_CR2_TSEREFE_BIT)
+/* Because this register varies significantly by series (e.g. some
+ * bits moved and others disappeared in the F1->F2 transition), its
+ * definitions are in the series headers. */
/* Sample time register 1 */
@@ -245,68 +237,47 @@ extern const adc_dev *ADC3;
#define ADC_DR_ADC2DATA (0xFFFF << 16)
#define ADC_DR_DATA 0xFFFF
-void adc_init(const adc_dev *dev);
+/*
+ * Routines
+ */
/**
- * @brief External event selector for regular group conversion.
- * @see adc_set_extsel
+ * @brief Set the ADC prescaler.
+ *
+ * This determines the ADC clock for all devices.
*/
-typedef enum adc_extsel_event {
- ADC_ADC12_TIM1_CC1 = (0 << 17), /**< ADC1 and ADC2: Timer 1 CC1 event */
- ADC_ADC12_TIM1_CC2 = (1 << 17), /**< ADC1 and ADC2: Timer 1 CC2 event */
- ADC_ADC12_TIM1_CC3 = (2 << 17), /**< ADC1 and ADC2: Timer 1 CC3 event */
- ADC_ADC12_TIM2_CC2 = (3 << 17), /**< ADC1 and ADC2: Timer 2 CC2 event */
- ADC_ADC12_TIM3_TRGO = (4 << 17), /**< ADC1 and ADC2: Timer 3 TRGO event */
- ADC_ADC12_TIM4_CC4 = (5 << 17), /**< ADC1 and ADC2: Timer 4 CC4 event */
- ADC_ADC12_EXTI11 = (6 << 17), /**< ADC1 and ADC2: EXTI11 event */
-#ifdef STM32_HIGH_DENSITY
- ADC_ADC12_TIM8_TRGO = (6 << 17), /**< ADC1 and ADC2: Timer 8 TRGO
- event (high density only) */
-#endif
- ADC_ADC12_SWSTART = (7 << 17), /**< ADC1 and ADC2: Software start */
-#ifdef STM32_HIGH_DENSITY
- ADC_ADC3_TIM3_CC1 = (0 << 17), /**< ADC3: Timer 3 CC1 event
- (high density only) */
- ADC_ADC3_TIM2_CC3 = (1 << 17), /**< ADC3: Timer 2 CC3 event
- (high density only) */
- ADC_ADC3_TIM1_CC3 = (2 << 17), /**< ADC3: Timer 1 CC3 event
- (high density only) */
- ADC_ADC3_TIM8_CC1 = (3 << 17), /**< ADC3: Timer 8 CC1 event
- (high density only) */
- ADC_ADC3_TIM8_TRGO = (4 << 17), /**< ADC3: Timer 8 TRGO event
- (high density only) */
- ADC_ADC3_TIM5_CC1 = (5 << 17), /**< ADC3: Timer 5 CC1 event
- (high density only) */
- ADC_ADC3_TIM5_CC3 = (6 << 17), /**< ADC3: Timer 5 CC3 event
- (high density only) */
- ADC_ADC3_SWSTART = (7 << 17), /**< ADC3: Software start (high
- density only) */
-#endif
- ADC_SWSTART = (7 << 17) /**< ADC1, ADC2, ADC3: Software start */
-} adc_extsel_event;
+void adc_set_prescaler(adc_prescaler pre);
+void adc_init(const adc_dev *dev);
void adc_set_extsel(const adc_dev *dev, adc_extsel_event event);
+void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate);
+uint16 adc_read(const adc_dev *dev, uint8 channel);
+
+/**
+ * @brief Call a function on all ADC devices.
+ * @param fn Function to call on each ADC device.
+ */
void adc_foreach(void (*fn)(const adc_dev*));
/**
- * @brief ADC sample times, in ADC clock cycles
- *
- * These control the amount of time spent sampling the input voltage.
+ * @brief Configure a GPIO pin for ADC conversion.
+ * @param gdev GPIO device to configure.
+ * @param bit Bit on gdev to configure for ADC conversion.
*/
-typedef enum {
- 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;
+struct gpio_dev;
+void adc_gpio_cfg(struct gpio_dev *gdev, uint8 bit);
-void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate);
-void adc_calibrate(const adc_dev *dev);
-uint16 adc_read(const adc_dev *dev, uint8 channel);
+/**
+ * @brief Enable an ADC and configure it for single conversion mode.
+ *
+ * This function performs any initialization necessary to allow the
+ * ADC device to perform a single synchronous regular software
+ * triggered conversion, using adc_read().
+ *
+ * @param dev Device to enable.
+ * @see adc_read()
+ */
+void adc_enable_single_swstart(const adc_dev* dev);
/**
* @brief Set the regular channel sequence length.
@@ -325,16 +296,6 @@ static inline void adc_set_reg_seqlen(const adc_dev *dev, uint8 length) {
}
/**
- * @brief Set external trigger conversion mode event for regular channels
- * @param dev ADC device
- * @param enable If 1, conversion on external events is enabled; if 0,
- * disabled.
- */
-static inline void adc_set_exttrig(const adc_dev *dev, uint8 enable) {
- *bb_perip(&dev->regs->CR2, ADC_CR2_EXTTRIG_BIT) = !!enable;
-}
-
-/**
* @brief Enable an adc peripheral
* @param dev ADC device to enable
*/
diff --git a/libmaple/rules.mk b/libmaple/rules.mk
index cc562ea..d118b16 100644
--- a/libmaple/rules.mk
+++ b/libmaple/rules.mk
@@ -11,7 +11,8 @@ LIBMAPLE_PRIVATE_INCLUDES := -I$(LIBMAPLE_PATH)
CFLAGS_$(d) = $(LIBMAPLE_PRIVATE_INCLUDES) $(LIBMAPLE_INCLUDES) -Wall -Werror
# Local rules and targets
-cSRCS_$(d) := flash.c
+cSRCS_$(d) := adc.c
+cSRCS_$(d) += flash.c
cSRCS_$(d) += gpio.c
cSRCS_$(d) += iwdg.c
cSRCS_$(d) += nvic.c
@@ -23,7 +24,6 @@ cSRCS_$(d) += usart.c
cSRCS_$(d) += usart_private.c
cSRCS_$(d) += util.c
# These still need to be ported to F2:
-# cSRCS_$(d) += adc.c
# cSRCS_$(d) += dac.c
# cSRCS_$(d) += dma.c
# cSRCS_$(d) += exti.c
diff --git a/libmaple/stm32f1/adc.c b/libmaple/stm32f1/adc.c
new file mode 100644
index 0000000..8242520
--- /dev/null
+++ b/libmaple/stm32f1/adc.c
@@ -0,0 +1,110 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2012 LeafLabs, LLC.
+ * Copyright (c) 2010 Perry Hung.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+
+/**
+ * @file libmaple/stm32f1/include/series/adc.c
+ * @brief STM32F1 ADC header.
+ */
+
+#include <libmaple/adc.h>
+#include <libmaple/gpio.h>
+
+/*
+ * Devices
+ */
+
+static adc_dev adc1 = {
+ .regs = ADC1_BASE,
+ .clk_id = RCC_ADC1
+};
+/** ADC1 device. */
+const adc_dev *ADC1 = &adc1;
+
+static adc_dev adc2 = {
+ .regs = ADC2_BASE,
+ .clk_id = RCC_ADC2
+};
+/** ADC2 device. */
+const adc_dev *ADC2 = &adc2;
+
+#ifdef STM32_HIGH_DENSITY
+adc_dev adc3 = {
+ .regs = ADC3_BASE,
+ .clk_id = RCC_ADC3
+};
+/** ADC3 device. */
+const adc_dev *ADC3 = &adc3;
+#endif
+
+/*
+ * STM32F1 routines
+ */
+
+/**
+ * @brief Calibrate an ADC peripheral
+ * @param dev adc device
+ */
+void adc_calibrate(const adc_dev *dev) {
+ __io uint32 *rstcal_bit = bb_perip(&(dev->regs->CR2), 3);
+ __io uint32 *cal_bit = bb_perip(&(dev->regs->CR2), 2);
+
+ *rstcal_bit = 1;
+ while (*rstcal_bit)
+ ;
+
+ *cal_bit = 1;
+ while (*cal_bit)
+ ;
+}
+
+/*
+ * Common routines
+ */
+
+void adc_set_prescaler(adc_prescaler pre) {
+ rcc_set_prescaler(RCC_PRESCALER_ADC, (uint32)pre);
+}
+
+void adc_foreach(void (*fn)(const adc_dev*)) {
+ fn(ADC1);
+ fn(ADC2);
+#ifdef STM32_HIGH_DENSITY
+ fn(ADC3);
+#endif
+}
+
+void adc_gpio_cfg(gpio_dev *gdev, uint8 bit) {
+ gpio_set_mode(gdev, bit, GPIO_INPUT_ANALOG);
+}
+
+void adc_enable_single_swstart(const adc_dev *dev) {
+ adc_init(dev);
+ adc_set_extsel(dev, ADC_SWSTART);
+ adc_set_exttrig(dev, 1);
+ adc_enable(dev);
+ adc_calibrate(dev);
+}
diff --git a/libmaple/stm32f1/include/series/adc.h b/libmaple/stm32f1/include/series/adc.h
new file mode 100644
index 0000000..1798e55
--- /dev/null
+++ b/libmaple/stm32f1/include/series/adc.h
@@ -0,0 +1,254 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2012 LeafLabs, LLC.
+ * Copyright (c) 2010 Perry Hung.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+
+/**
+ * @file stm32f1/include/series/adc.h
+ * @author Marti Bolivar <mbolivar@leaflabs.com>,
+ * Perry Hung <perry@leaflabs.com>
+ * @brief STM32F1 ADC header.
+ */
+
+#ifndef _LIBMAPLE_STM32F1_ADC_H_
+#define _LIBMAPLE_STM32F1_ADC_H_
+
+#include <libmaple/rcc.h> /* For the prescalers */
+
+/*
+ * Devices
+ */
+
+extern const struct adc_dev *ADC1;
+extern const struct adc_dev *ADC2;
+#ifdef STM32_HIGH_DENSITY
+extern const struct adc_dev *ADC3;
+#endif
+
+/*
+ * Register map base pointers
+ */
+
+/** ADC1 register map base pointer. */
+#define ADC1_BASE ((struct adc_reg_map*)0x40012400)
+/** ADC2 register map base pointer. */
+#define ADC2_BASE ((struct adc_reg_map*)0x40012800)
+#ifdef STM32_HIGH_DENSITY
+/** ADC3 register map base pointer. */
+#define ADC3_BASE ((struct adc_reg_map*)0x40013C00)
+#endif
+
+/*
+ * Register bit definitions
+ */
+
+/* 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_JEXTTRIG_BIT 15
+#define ADC_CR2_EXTTRIG_BIT 20
+#define ADC_CR2_JSWSTART_BIT 21
+#define ADC_CR2_SWSTART_BIT 22
+#define ADC_CR2_TSEREFE_BIT 23
+
+#define ADC_CR2_ADON (1U << ADC_CR2_ADON_BIT)
+#define ADC_CR2_CONT (1U << ADC_CR2_CONT_BIT)
+#define ADC_CR2_CAL (1U << ADC_CR2_CAL_BIT)
+#define ADC_CR2_RSTCAL (1U << ADC_CR2_RSTCAL_BIT)
+#define ADC_CR2_DMA (1U << ADC_CR2_DMA_BIT)
+#define ADC_CR2_ALIGN (1U << ADC_CR2_ALIGN_BIT)
+#define ADC_CR2_JEXTSEL 0x7000
+#define ADC_CR2_JEXTTRIG (1U << ADC_CR2_JEXTTRIG_BIT)
+#define ADC_CR2_EXTSEL 0xE0000
+#define ADC_CR2_EXTTRIG (1U << ADC_CR2_EXTTRIG_BIT)
+#define ADC_CR2_JSWSTART (1U << ADC_CR2_JSWSTART_BIT)
+#define ADC_CR2_SWSTART (1U << ADC_CR2_SWSTART_BIT)
+#define ADC_CR2_TSEREFE (1U << ADC_CR2_TSEREFE_BIT)
+
+/*
+ * Other types
+ */
+
+/**
+ * @brief STM32F1 external event selectors for regular group
+ * conversion.
+ *
+ * Some external events are only available on ADCs 1 and 2, others
+ * only on ADC3, while others are available on all three ADCs.
+ * Additionally, some events are only available on high- and
+ * XL-density STM32F1 MCUs, as they use peripherals only available on
+ * those MCU densities.
+ *
+ * For ease of use, each event selector is given along with the ADCs
+ * it's available on, along with any other availability restrictions.
+ *
+ * @see adc_set_extsel()
+ */
+typedef enum adc_extsel_event {
+ /* TODO: Smarten this up a bit, as follows.
+ *
+ * The EXTSEL bits on F1 are a little brain-damaged in that the
+ * TIM8 TRGO event has different bits depending on whether you're
+ * using ADC1/2 or ADC3. We route around this by declaring two
+ * enumerators, ADC_EXT_EV_ADC12_TIM8_TRGO and
+ * ADC_EXT_EV_ADC3_TIM8_TRGO.
+ *
+ * The right thing to do is to provide a single
+ * ADC_EXT_EV_TIM8_TRGO enumerator and override adc_set_extsel on
+ * STM32F1 to handle this situation correctly. We can do that
+ * later, though, and change the per-ADC enumerator values to
+ * ADC_EXT_EV_TIM8_TRGO to preserve compatibility. */
+
+ /* ADC1 and ADC2 only: */
+ ADC_EXT_EV_TIM1_CC1 = 0x00000, /**< ADC1, ADC2: Timer 1 CC1 event */
+ ADC_EXT_EV_TIM1_CC2 = 0x20000, /**< ADC1, ADC2: Timer 1 CC2 event */
+ ADC_EXT_EV_TIM2_CC2 = 0x60000, /**< ADC1, ADC2: Timer 2 CC2 event */
+ ADC_EXT_EV_TIM3_TRGO = 0x80000, /**< ADC1, ADC2: Timer 3 TRGO event */
+ ADC_EXT_EV_TIM4_CC4 = 0xA0000, /**< ADC1, ADC2: Timer 4 CC4 event */
+ ADC_EXT_EV_EXTI11 = 0xC0000, /**< ADC1, ADC2: EXTI11 event */
+
+ /* Common: */
+ ADC_EXT_EV_TIM1_CC3 = 0x40000, /**< ADC1, ADC2, ADC3: Timer 1 CC3 event */
+ ADC_EXT_EV_SWSTART = 0xE0000, /**< ADC1, ADC2, ADC3: Software start */
+
+ /* HD only: */
+ ADC_EXT_EV_TIM3_CC1 = 0x00000, /**<
+ * ADC3: Timer 3 CC1 event
+ * Availability: high- and XL-density. */
+ ADC_EXT_EV_TIM2_CC3 = 0x20000, /**<
+ * ADC3: Timer 2 CC3 event
+ * Availability: high- and XL-density. */
+ ADC_EXT_EV_TIM8_CC1 = 0x60000, /**<
+ * ADC3: Timer 8 CC1 event
+ * Availability: high- and XL-density. */
+ ADC_EXT_EV_ADC3_TIM8_TRGO = 0x80000, /**<
+ * ADC3: Timer 8 TRGO event
+ * Availability: high- and XL-density. */
+ ADC_EXT_EV_TIM5_CC1 = 0xA0000, /**<
+ * ADC3: Timer 5 CC1 event
+ * Availability: high- and XL-density. */
+ ADC_EXT_EV_ADC12_TIM8_TRGO = 0xC0000, /**<
+ * ADC1, ADC2: Timer 8 TRGO event
+ * Availability: high- and XL-density. */
+ ADC_EXT_EV_TIM5_CC3 = 0xC0000, /**<
+ * ADC3: Timer 5 CC3 event
+ * Availability: high- and XL-density. */
+} adc_extsel_event;
+
+/* We'll keep these old adc_extsel_event enumerators around for a
+ * while, for backwards compatibility: */
+/** Deprecated. Use ADC_EXT_EV_TIM1_CC1 instead. */
+#define ADC_ADC12_TIM1_CC1 ADC_EXT_EV_TIM1_CC1
+/** Deprecated. Use ADC_EXT_EV_TIM1_CC2 instead. */
+#define ADC_ADC12_TIM1_CC2 ADC_EXT_EV_TIM1_CC2
+/** Deprecated. Use ADC_EXT_EV_TIM1_CC3 instead. */
+#define ADC_ADC12_TIM1_CC3 ADC_EXT_EV_TIM1_CC3
+/** Deprecated. Use ADC_EXT_EV_TIM2_CC2 instead. */
+#define ADC_ADC12_TIM2_CC2 ADC_EXT_EV_TIM2_CC2
+/** Deprecated. Use ADC_EXT_EV_TIM3_TRGO instead. */
+#define ADC_ADC12_TIM3_TRGO ADC_EXT_EV_TIM3_TRGO
+/** Deprecated. Use ADC_EXT_EV_TIM4_CC4 instead. */
+#define ADC_ADC12_TIM4_CC4 ADC_EXT_EV_TIM4_CC4
+/** Deprecated. Use ADC_EXT_EV_EXTI11 instead. */
+#define ADC_ADC12_EXTI11 ADC_EXT_EV_EXTI11
+/** Deprecated. Use ADC_EXT_EV_ADC12_TIM8_TRGO instead. */
+#define ADC_ADC12_TIM8_TRGO ADC_EXT_EV_ADC12_TIM8_TRGO
+/** Deprecated. Use ADC_EXT_EV_SWSTART instead. */
+#define ADC_ADC12_SWSTART ADC_EXT_EV_SWSTART
+/** Deprecated. Use ADC_EXT_EV_TIM1_CC1 instead. */
+#define ADC_ADC3_TIM3_CC1 ADC_EXT_EV_TIM1_CC1
+/** Deprecated. Use ADC_EXT_EV_TIM1_CC2 instead. */
+#define ADC_ADC3_TIM2_CC3 ADC_EXT_EV_TIM1_CC2
+/** Deprecated. Use ADC_EXT_EV_TIM1_CC3 instead. */
+#define ADC_ADC3_TIM1_CC3 ADC_EXT_EV_TIM1_CC3
+/** Deprecated. Use ADC_EXT_EV_TIM2_CC2 instead. */
+#define ADC_ADC3_TIM8_CC1 ADC_EXT_EV_TIM2_CC2
+/** Deprecated. Use ADC_EXT_EV_TIM3_TRGO instead. */
+#define ADC_ADC3_TIM8_TRGO ADC_EXT_EV_TIM3_TRGO
+/** Deprecated. Use ADC_EXT_EV_TIM4_CC4 instead. */
+#define ADC_ADC3_TIM5_CC1 ADC_EXT_EV_TIM4_CC4
+/** Deprecated. Use ADC_EXT_EV_EXTI11 instead. */
+#define ADC_ADC3_TIM5_CC3 ADC_EXT_EV_EXTI11
+/** Deprecated. Use ADC_EXT_EV_TIM8_TRGO instead. */
+#define ADC_ADC3_SWSTART ADC_EXT_EV_TIM8_TRGO
+/** Deprecated. Use ADC_EXT_EV_SWSTART instead. */
+#define ADC_SWSTART ADC_EXT_EV_SWSTART
+
+/**
+ * @brief STM32F1 sample times, in ADC clock cycles.
+ *
+ * These control the amount of time spent sampling the input voltage.
+ *
+ * IMPORTANT: maximum external impedance must be below 0.4kOhms for
+ * 1.5 cycle sampling time. At 55.5 cycles/sample, the external input
+ * impedance must be at most 50kOhms. See your device's datasheet for
+ * more information.
+ */
+typedef enum adc_smp_rate {
+ 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;
+
+/**
+ * @brief STM32F1 ADC prescalers, as divisors of PCLK2.
+ */
+typedef enum adc_prescaler {
+ ADC_PRE_PCLK2_DIV_2 = RCC_ADCPRE_PCLK_DIV_2, /** PCLK2 divided by 2 */
+ ADC_PRE_PCLK2_DIV_4 = RCC_ADCPRE_PCLK_DIV_4, /** PCLK2 divided by 4 */
+ ADC_PRE_PCLK2_DIV_6 = RCC_ADCPRE_PCLK_DIV_6, /** PCLK2 divided by 6 */
+ ADC_PRE_PCLK2_DIV_8 = RCC_ADCPRE_PCLK_DIV_8, /** PCLK2 divided by 8 */
+} adc_prescaler;
+
+/*
+ * Routines
+ */
+
+void adc_calibrate(const adc_dev *dev);
+
+/**
+ * @brief Set external trigger conversion mode event for regular channels
+ *
+ * Availability: STM32F1.
+ *
+ * @param dev ADC device
+ * @param enable If 1, conversion on external events is enabled; if 0,
+ * disabled.
+ */
+static inline void adc_set_exttrig(const adc_dev *dev, uint8 enable) {
+ *bb_perip(&dev->regs->CR2, ADC_CR2_EXTTRIG_BIT) = !!enable;
+}
+
+#endif
diff --git a/libmaple/stm32f1/rules.mk b/libmaple/stm32f1/rules.mk
index bdff00e..c0d6344 100644
--- a/libmaple/stm32f1/rules.mk
+++ b/libmaple/stm32f1/rules.mk
@@ -11,7 +11,8 @@ CFLAGS_$(d) = -I$(d) $(LIBMAPLE_PRIVATE_INCLUDES) $(LIBMAPLE_INCLUDES) -Wall -We
sSRCS_$(d) := isrs_performance.S
sSRCS_$(d) += vector_table_performance.S
-cSRCS_$(d) := bkp.c
+cSRCS_$(d) := adc.c
+cSRCS_$(d) += bkp.c
cSRCS_$(d) += fsmc.c
cSRCS_$(d) += gpio.c
cSRCS_$(d) += rcc.c
diff --git a/libmaple/stm32f2/adc.c b/libmaple/stm32f2/adc.c
new file mode 100644
index 0000000..fbe1618
--- /dev/null
+++ b/libmaple/stm32f2/adc.c
@@ -0,0 +1,84 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2012 LeafLabs, LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+
+/**
+ * @file libmaple/stm32f2/include/series/adc.c
+ * @brief STM32F2 ADC header.
+ */
+
+#include <libmaple/adc.h>
+#include <libmaple/gpio.h>
+
+/*
+ * Devices
+ */
+
+static adc_dev adc1 = {
+ .regs = ADC1_BASE,
+ .clk_id = RCC_ADC1,
+};
+/** ADC1 device. */
+const adc_dev *ADC1 = &adc1;
+
+static adc_dev adc2 = {
+ .regs = ADC2_BASE,
+ .clk_id = RCC_ADC2,
+};
+/** ADC2 device. */
+const adc_dev *ADC2 = &adc2;
+
+adc_dev adc3 = {
+ .regs = ADC3_BASE,
+ .clk_id = RCC_ADC3,
+};
+/** ADC3 device. */
+const adc_dev *ADC3 = &adc3;
+
+/*
+ * Common routines
+ */
+
+void adc_set_prescaler(adc_prescaler pre) {
+ uint32 ccr = ADC_COMMON_BASE->CCR;
+ ccr &= ~ADC_CCR_ADCPRE;
+ ccr |= (uint32)pre;
+ ADC_COMMON_BASE->CCR = ccr;
+}
+
+void adc_foreach(void (*fn)(const adc_dev*)) {
+ fn(ADC1);
+ fn(ADC2);
+ fn(ADC3);
+}
+
+void adc_gpio_cfg(gpio_dev *gdev, uint8 bit) {
+ gpio_set_modef(gdev, bit, GPIO_MODE_ANALOG, GPIO_MODEF_PUPD_NONE);
+}
+
+void adc_enable_single_swstart(const adc_dev *dev) {
+ adc_init(dev);
+ adc_enable(dev);
+}
diff --git a/libmaple/stm32f2/include/series/adc.h b/libmaple/stm32f2/include/series/adc.h
new file mode 100644
index 0000000..4f0cd6b
--- /dev/null
+++ b/libmaple/stm32f2/include/series/adc.h
@@ -0,0 +1,331 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2012 LeafLabs, LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+
+/**
+ * @file stm32f2/include/series/adc.h
+ * @author Marti Bolivar <mbolivar@leaflabs.com>,
+ * @brief STM32F2 ADC header.
+ */
+
+#ifndef _LIBMAPLE_STM32F2_ADC_H_
+#define _LIBMAPLE_STM32F2_ADC_H_
+
+#include <libmaple/libmaple_types.h>
+
+/*
+ * Devices
+ */
+
+extern const struct adc_dev *ADC1;
+extern const struct adc_dev *ADC2;
+extern const struct adc_dev *ADC3;
+
+/*
+ * Common register map
+ */
+
+/** ADC common register map type */
+typedef struct adc_common_reg_map {
+ __io uint32 CSR; /**< Common status register */
+ __io uint32 CCR; /**< Common control register */
+ __io uint32 CDR; /**<
+ * @brief Common regular data register
+ * for dual and triple modes */
+} adc_common_reg_map;
+
+/*
+ * Register map base pointers
+ */
+
+/** ADC1 register map base pointer. */
+#define ADC1_BASE ((struct adc_reg_map*)0x40012000)
+/** ADC2 register map base pointer. */
+#define ADC2_BASE ((struct adc_reg_map*)0x40012100)
+/** ADC3 register map base pointer. */
+#define ADC3_BASE ((struct adc_reg_map*)0x40012200)
+/** ADC common register map base pointer. */
+#define ADC_COMMON_BASE ((struct adc_common_reg_map*)0x40012300)
+
+/*
+ * Register bit definitions
+ */
+
+/* Status register */
+
+/** Overrun bit. */
+#define ADC_SR_OVR_BIT 5
+/** Overrun. */
+#define ADC_SR_OVR (1U << ADC_SR_OVR_BIT)
+
+/* Control register 1 */
+
+/** Overrun interrupt enable bit. */
+#define ADC_CR1_OVRIE_BIT 26
+
+/** Overrun interrupt error enable. */
+#define ADC_CR1_OVRIE (1U << ADC_CR1_OVRIE_BIT)
+/** Conversion resolution. */
+#define ADC_CR1_RES (0x3U << 24)
+/** Conversion resolution: 12 bit (at least 15 ADCCLK cycles). */
+#define ADC_CR1_RES_12BIT (0x0U << 24)
+/** Conversion resolution: 10 bit (at least 13 ADCCLK cycles). */
+#define ADC_CR1_RES_10BIT (0x1U << 24)
+/** Conversion resolution: 8 bit (at least 11 ADCCLK cycles). */
+#define ADC_CR1_RES_8BIT (0x2U << 24)
+/** Conversion resolution: 6 bit (at least 9 ADCCLK cycles). */
+#define ADC_CR1_RES_6BIT (0x3U << 24)
+
+/* Control register 2 */
+
+#define ADC_CR2_SWSTART_BIT 30
+#define ADC_CR2_JSWSTART_BIT 22
+#define ADC_CR2_ALIGN_BIT 11
+#define ADC_CR2_EOCS_BIT 10
+#define ADC_CR2_DDS_BIT 9
+#define ADC_CR2_DMA_BIT 8
+#define ADC_CR2_CONT_BIT 1
+#define ADC_CR2_ADON_BIT 0
+
+#define ADC_CR2_SWSTART (1U << ADC_CR2_SWSTART_BIT)
+#define ADC_CR2_EXTEN (0x3 << 28)
+#define ADC_CR2_EXTEN_DISABLED (0x0 << 28)
+#define ADC_CR2_EXTEN_RISE (0x1 << 28)
+#define ADC_CR2_EXTEN_FALL (0x2 << 28)
+#define ADC_CR2_EXTEN_RISE_FALL (0x3 << 28)
+#define ADC_CR2_EXTSEL (0xF << 24)
+#define ADC_CR2_EXTSEL_TIM1_CC1 (0x0 << 24)
+#define ADC_CR2_EXTSEL_TIM1_CC2 (0x1 << 24)
+#define ADC_CR2_EXTSEL_TIM1_CC3 (0x2 << 24)
+#define ADC_CR2_EXTSEL_TIM2_CC2 (0x3 << 24)
+#define ADC_CR2_EXTSEL_TIM2_CC3 (0x4 << 24)
+#define ADC_CR2_EXTSEL_TIM2_CC4 (0x5 << 24)
+#define ADC_CR2_EXTSEL_TIM1_TRGO (0x6 << 24)
+#define ADC_CR2_EXTSEL_TIM3_CC1 (0x7 << 24)
+#define ADC_CR2_EXTSEL_TIM3_TRGO (0x8 << 24)
+#define ADC_CR2_EXTSEL_TIM4_CC4 (0x9 << 24)
+#define ADC_CR2_EXTSEL_TIM5_CC1 (0xA << 24)
+#define ADC_CR2_EXTSEL_TIM5_CC2 (0xB << 24)
+#define ADC_CR2_EXTSEL_TIM5_CC3 (0xC << 24)
+#define ADC_CR2_EXTSEL_TIM8_CC1 (0xD << 24)
+#define ADC_CR2_EXTSEL_TIM8_TRGO (0xE << 24)
+#define ADC_CR2_EXTSEL_TIM1_EXTI11 (0xF << 24)
+#define ADC_CR2_JSWSTART (1U << ADC_CR2_JSWSTART_BIT)
+#define ADC_CR2_JEXTEN (0x3 << 20)
+#define ADC_CR2_JEXTEN_DISABLED (0x0 << 20)
+#define ADC_CR2_JEXTEN_RISE (0x1 << 20)
+#define ADC_CR2_JEXTEN_FALL (0x2 << 20)
+#define ADC_CR2_JEXTEN_RISE_FALL (0x3 << 20)
+#define ADC_CR2_JEXTSEL (0xF << 16)
+#define ADC_CR2_JEXTSEL_TIM1_CC4 (0x0 << 16)
+#define ADC_CR2_JEXTSEL_TIM1_TRGO (0x1 << 16)
+#define ADC_CR2_JEXTSEL_TIM2_CC1 (0x2 << 16)
+#define ADC_CR2_JEXTSEL_TIM2_TRGO (0x3 << 16)
+#define ADC_CR2_JEXTSEL_TIM3_CC2 (0x4 << 16)
+#define ADC_CR2_JEXTSEL_TIM3_CC4 (0x5 << 16)
+#define ADC_CR2_JEXTSEL_TIM4_CC1 (0x6 << 16)
+#define ADC_CR2_JEXTSEL_TIM4_CC2 (0x7 << 16)
+#define ADC_CR2_JEXTSEL_TIM4_CC3 (0x8 << 16)
+#define ADC_CR2_JEXTSEL_TIM4_TRGO (0x9 << 16)
+#define ADC_CR2_JEXTSEL_TIM5_CC4 (0xA << 16)
+#define ADC_CR2_JEXTSEL_TIM5_TRGO (0xB << 16)
+#define ADC_CR2_JEXTSEL_TIM8_CC2 (0xC << 16)
+#define ADC_CR2_JEXTSEL_TIM8_CC3 (0xD << 16)
+#define ADC_CR2_JEXTSEL_TIM8_CC4 (0xE << 16)
+#define ADC_CR2_JEXTSEL_TIM1_EXTI15 (0xF << 16)
+#define ADC_CR2_ALIGN (1U << ADC_CR2_ALIGN_BIT)
+#define ADC_CR2_ALIGN_RIGHT (0U << ADC_CR2_ALIGN_BIT)
+#define ADC_CR2_ALIGN_LEFT (1U << ADC_CR2_ALIGN_BIT)
+#define ADC_CR2_EOCS (1U << ADC_CR2_EOCS_BIT)
+#define ADC_CR2_EOCS_SEQUENCE (0U << ADC_CR2_EOCS_BIT)
+#define ADC_CR2_EOCS_CONVERSION (1U << ADC_CR2_EOCS_BIT)
+#define ADC_CR2_DDS (1U << ADC_CR2_DDS_BIT)
+#define ADC_CR2_DMA (1U << ADC_CR2_DMA_BIT)
+#define ADC_CR2_CONT (1U << ADC_CR2_CONT_BIT)
+#define ADC_CR2_ADON (1U << ADC_CR2_ADON_BIT)
+
+/* Common status register */
+
+#define ADC_CSR_OVR3_BIT 21
+#define ADC_CSR_STRT3_BIT 20
+#define ADC_CSR_JSTRT3_BIT 19
+#define ADC_CSR_JEOC3_BIT 18
+#define ADC_CSR_EOC3_BIT 17
+#define ADC_CSR_AWD3_BIT 16
+#define ADC_CSR_OVR2_BIT 13
+#define ADC_CSR_STRT2_BIT 12
+#define ADC_CSR_JSTRT2_BIT 11
+#define ADC_CSR_JEOC2_BIT 10
+#define ADC_CSR_EOC2_BIT 9
+#define ADC_CSR_AWD2_BIT 8
+#define ADC_CSR_OVR1_BIT 5
+#define ADC_CSR_STRT1_BIT 4
+#define ADC_CSR_JSTRT1_BIT 3
+#define ADC_CSR_JEOC1_BIT 2
+#define ADC_CSR_EOC1_BIT 1
+#define ADC_CSR_AWD1_BIT 0
+
+#define ADC_CSR_OVR3 (1U << ADC_CSR_OVR3_BIT)
+#define ADC_CSR_STRT3 (1U << ADC_CSR_STRT3_BIT)
+#define ADC_CSR_JSTRT3 (1U << ADC_CSR_JSTRT3_BIT)
+#define ADC_CSR_JEOC3 (1U << ADC_CSR_JEOC3_BIT)
+#define ADC_CSR_EOC3 (1U << ADC_CSR_EOC3_BIT)
+#define ADC_CSR_AWD3 (1U << ADC_CSR_AWD3_BIT)
+#define ADC_CSR_OVR2 (1U << ADC_CSR_OVR2_BIT)
+#define ADC_CSR_STRT2 (1U << ADC_CSR_STRT2_BIT)
+#define ADC_CSR_JSTRT2 (1U << ADC_CSR_JSTRT2_BIT)
+#define ADC_CSR_JEOC2 (1U << ADC_CSR_JEOC2_BIT)
+#define ADC_CSR_EOC2 (1U << ADC_CSR_EOC2_BIT)
+#define ADC_CSR_AWD2 (1U << ADC_CSR_AWD2_BIT)
+#define ADC_CSR_OVR1 (1U << ADC_CSR_OVR1_BIT)
+#define ADC_CSR_STRT1 (1U << ADC_CSR_STRT1_BIT)
+#define ADC_CSR_JSTRT1 (1U << ADC_CSR_JSTRT1_BIT)
+#define ADC_CSR_JEOC1 (1U << ADC_CSR_JEOC1_BIT)
+#define ADC_CSR_EOC1 (1U << ADC_CSR_EOC1_BIT)
+#define ADC_CSR_AWD1 (1U << ADC_CSR_AWD1_BIT)
+
+/* Common control register */
+
+#define ADC_CCR_TSVREFE_BIT 23
+#define ADC_CCR_VBATE_BIT 22
+#define ADC_CCR_DDS_BIT 13
+
+#define ADC_CCR_TSVREFE (1U << ADC_CCR_TSVREFE_BIT)
+#define ADC_CCR_VBATE (1U << ADC_CCR_VBATE_BIT)
+#define ADC_CCR_ADCPRE (0x3 << 16)
+#define ADC_CCR_ADCPRE_PCLK2_DIV_2 (0x0 << 16)
+#define ADC_CCR_ADCPRE_PCLK2_DIV_4 (0x1 << 16)
+#define ADC_CCR_ADCPRE_PCLK2_DIV_6 (0x2 << 16)
+#define ADC_CCR_ADCPRE_PCLK2_DIV_8 (0x3 << 16)
+#define ADC_CCR_DMA (0x3 << 14)
+#define ADC_CCR_DMA_DIS (0x0 << 14)
+#define ADC_CCR_DMA_MODE_1 (0x1 << 14)
+#define ADC_CCR_DMA_MODE_2 (0x2 << 14)
+#define ADC_CCR_DMA_MODE_3 (0x3 << 14)
+#define ADC_CCR_DDS (1U << ADC_CCR_DDS_BIT)
+#define ADC_CCR_DELAY (0xF << 8)
+#define ADC_CCR_DELAY_5 (0x0 << 8)
+#define ADC_CCR_DELAY_6 (0x1 << 8)
+#define ADC_CCR_DELAY_7 (0x2 << 8)
+#define ADC_CCR_DELAY_8 (0x3 << 8)
+#define ADC_CCR_DELAY_9 (0x4 << 8)
+#define ADC_CCR_DELAY_10 (0x5 << 8)
+#define ADC_CCR_DELAY_11 (0x6 << 8)
+#define ADC_CCR_DELAY_12 (0x7 << 8)
+#define ADC_CCR_DELAY_13 (0x8 << 8)
+#define ADC_CCR_DELAY_14 (0x9 << 8)
+#define ADC_CCR_DELAY_15 (0xA << 8)
+#define ADC_CCR_DELAY_16 (0xB << 8)
+#define ADC_CCR_DELAY_17 (0xC << 8)
+#define ADC_CCR_DELAY_18 (0xD << 8)
+#define ADC_CCR_DELAY_19 (0xE << 8)
+#define ADC_CCR_DELAY_20 (0xF << 8)
+/** Multi ADC mode selection. */
+#define ADC_CCR_MULTI 0x1F
+/** All ADCs independent. */
+#define ADC_CCR_MULTI_INDEPENDENT 0x0
+/** Dual mode: combined regular simultaneous/injected simultaneous. */
+#define ADC_CCR_MULTI_DUAL_REG_SIM_INJ_SIM 0x1
+/** Dual mode: combined regular simultaneous/alternate trigger. */
+#define ADC_CCR_MULTI_DUAL_REG_SIM_ALT_TRIG 0x2
+/** Dual mode: injected simultaneous mode only. */
+#define ADC_CCR_MULTI_DUAL_INJ_SIM 0x5
+/** Dual mode: regular simultaneous mode only. */
+#define ADC_CCR_MULTI_DUAL_REG_SIM 0x6
+/** Dual mode: interleaved mode only. */
+#define ADC_CCR_MULTI_DUAL_INTER 0x7
+/** Dual mode: alternate trigger mode only. */
+#define ADC_CCR_MULTI_DUAL_ALT_TRIG 0x9
+/** Triple mode: combined regular simultaneous/injected simultaneous. */
+#define ADC_CCR_MULTI_TRIPLE_REG_SIM_INJ_SIM 0x10
+/** Triple mode: combined regular simultaneous/alternate trigger. */
+#define ADC_CCR_MULTI_TRIPLE_REG_SIM_ALT_TRIG 0x11
+/** Triple mode: injected simultaneous mode only. */
+#define ADC_CCR_MULTI_TRIPLE_INJ_SIM 0x12
+/** Triple mode: regular simultaneous mode only. */
+#define ADC_CCR_MULTI_TRIPLE_REG_SIM 0x15
+/** Triple mode: interleaved mode only. */
+#define ADC_CCR_MULTI_TRIPLE_INTER 0x17
+/** Triple mode: alternate trigger mode only. */
+#define ADC_CCR_MULTI_TRIPLE_ALT_TRIG 0x19
+
+/* Common regular data register for dual and triple modes */
+
+#define ADC_CDR_DATA2 0xFFFF0000
+#define ADC_CDR_DATA1 0xFFFF
+
+/*
+ * Other types
+ */
+
+/**
+ * @brief STM32F2 external event selectors for regular group
+ * conversion.
+ * @see adc_set_extsel()
+ */
+typedef enum adc_extsel_event {
+ ADC_EXT_EV_TIM1_CC1 = ADC_CR2_EXTSEL_TIM1_CC1,
+ ADC_EXT_EV_TIM1_CC2 = ADC_CR2_EXTSEL_TIM1_CC2,
+ ADC_EXT_EV_TIM1_CC3 = ADC_CR2_EXTSEL_TIM1_CC3,
+ ADC_EXT_EV_TIM2_CC2 = ADC_CR2_EXTSEL_TIM2_CC2,
+ ADC_EXT_EV_TIM2_CC3 = ADC_CR2_EXTSEL_TIM2_CC3,
+ ADC_EXT_EV_TIM2_CC4 = ADC_CR2_EXTSEL_TIM2_CC4,
+ ADC_EXT_EV_TIM1_TRGO = ADC_CR2_EXTSEL_TIM1_TRGO,
+ ADC_EXT_EV_TIM3_CC1 = ADC_CR2_EXTSEL_TIM3_CC1,
+ ADC_EXT_EV_TIM3_TRGO = ADC_CR2_EXTSEL_TIM3_TRGO,
+ ADC_EXT_EV_TIM4_CC4 = ADC_CR2_EXTSEL_TIM4_CC4,
+ ADC_EXT_EV_TIM5_CC1 = ADC_CR2_EXTSEL_TIM5_CC1,
+ ADC_EXT_EV_TIM5_CC2 = ADC_CR2_EXTSEL_TIM5_CC2,
+ ADC_EXT_EV_TIM5_CC3 = ADC_CR2_EXTSEL_TIM5_CC3,
+ ADC_EXT_EV_TIM8_CC1 = ADC_CR2_EXTSEL_TIM8_CC1,
+ ADC_EXT_EV_TIM8_TRGO = ADC_CR2_EXTSEL_TIM8_TRGO,
+ ADC_EXT_EV_TIM1_EXTI11 = ADC_CR2_EXTSEL_TIM1_EXTI11,
+} adc_extsel_event;
+
+/**
+ * @brief STM32F2 sample times, in ADC clock cycles.
+ */
+typedef enum adc_smp_rate {
+ ADC_SMPR_3, /**< 3 ADC cycles */
+ ADC_SMPR_15, /**< 15 ADC cycles */
+ ADC_SMPR_28, /**< 28 ADC cycles */
+ ADC_SMPR_56, /**< 56 ADC cycles */
+ ADC_SMPR_84, /**< 84 ADC cycles */
+ ADC_SMPR_112, /**< 112 ADC cycles */
+ ADC_SMPR_144, /**< 144 ADC cycles */
+ ADC_SMPR_480, /**< 480 ADC cycles */
+} adc_smp_rate;
+
+/**
+ * @brief STM32F2 ADC prescalers, as divisors of PCLK2.
+ */
+typedef enum adc_prescaler {
+ ADC_PRE_PCLK2_DIV_2 = ADC_CCR_ADCPRE_PCLK2_DIV_2, /** PCLK2 divided by 2 */
+ ADC_PRE_PCLK2_DIV_4 = ADC_CCR_ADCPRE_PCLK2_DIV_4, /** PCLK2 divided by 4 */
+ ADC_PRE_PCLK2_DIV_6 = ADC_CCR_ADCPRE_PCLK2_DIV_6, /** PCLK2 divided by 6 */
+ ADC_PRE_PCLK2_DIV_8 = ADC_CCR_ADCPRE_PCLK2_DIV_8, /** PCLK2 divided by 8 */
+} adc_prescaler;
+
+#endif
diff --git a/libmaple/stm32f2/rules.mk b/libmaple/stm32f2/rules.mk
index a46f8d1..5951b94 100644
--- a/libmaple/stm32f2/rules.mk
+++ b/libmaple/stm32f2/rules.mk
@@ -11,6 +11,7 @@ CFLAGS_$(d) = -I$(d) $(LIBMAPLE_INCLUDES) $(LIBMAPLE_PRIVATE_INCLUDES) -Wall -We
sSRCS_$(d) := isrs.S
sSRCS_$(d) += vector_table.S
+cSRCS_$(d) := adc.c
cSRCS_$(d) += fsmc.c
cSRCS_$(d) += gpio.c
cSRCS_$(d) += rcc.c
diff --git a/wirish/boards.cpp b/wirish/boards.cpp
index 04b359f..54807d3 100644
--- a/wirish/boards.cpp
+++ b/wirish/boards.cpp
@@ -54,6 +54,7 @@
static void setup_flash(void);
static void setup_clocks(void);
static void setup_nvic(void);
+static void setup_adcs(void);
/*
* Exported functions
@@ -65,7 +66,7 @@ void init(void) {
setup_nvic();
systick_init(SYSTICK_RELOAD_VAL);
wirish::priv::board_setup_gpio();
- wirish::priv::board_setup_adc();
+ setup_adcs();
wirish::priv::board_setup_timers();
wirish::priv::board_setup_usb();
boardInit();
@@ -124,7 +125,7 @@ static void setup_clocks(void) {
// Configure AHBx, APBx, etc. prescalers and the main PLL.
wirish::priv::board_setup_clock_prescalers();
- rcc_configure_pll(&wirish::priv::board_pll_cfg);
+ rcc_configure_pll(&wirish::priv::w_board_pll_cfg);
// Enable the PLL, and wait until it's ready.
rcc_turn_on_clk(RCC_CLK_PLL);
@@ -146,3 +147,13 @@ static void setup_nvic(void) {
#error "You must select a base address for the vector table."
#endif
}
+
+static void adc_default_config(const adc_dev *dev) {
+ adc_enable_single_swstart(dev);
+ adc_set_sample_rate(dev, wirish::priv::w_adc_smp);
+}
+
+static void setup_adcs(void) {
+ adc_set_prescaler(wirish::priv::w_adc_pre);
+ adc_foreach(adc_default_config);
+}
diff --git a/wirish/boards_private.h b/wirish/boards_private.h
index a4101c9..e32f298 100644
--- a/wirish/boards_private.h
+++ b/wirish/boards_private.h
@@ -28,11 +28,18 @@
* @file wirish/boards_private.h
* @author Marti Bolivar <mbolivar@leaflabs.com>
* @brief Private board support header.
+ *
+ * This file declares chip-specific variables and functions which
+ * determine how init() behaves. It is not part of the public Wirish
+ * API, and can change without notice.
*/
#ifndef _WIRISH_BOARDS_PRIVATE_H_
#define _WIRISH_BOARDS_PRIVATE_H_
+#include <libmaple/rcc.h>
+#include <libmaple/adc.h>
+
namespace wirish {
namespace priv {
@@ -40,7 +47,9 @@ namespace wirish {
* Chip-specific initialization data
*/
- extern rcc_pll_cfg board_pll_cfg;
+ extern rcc_pll_cfg w_board_pll_cfg;
+ extern adc_prescaler w_adc_pre;
+ extern adc_smp_rate w_adc_smp;
/*
* Chip-specific initialization routines and helper functions.
@@ -49,7 +58,6 @@ namespace wirish {
void board_reset_pll(void);
void board_setup_clock_prescalers(void);
void board_setup_gpio(void);
- void board_setup_adc(void);
void board_setup_timers(void);
void board_setup_usb(void);
diff --git a/wirish/stm32f1/boards_setup.cpp b/wirish/stm32f1/boards_setup.cpp
index 4ee292a..8e16009 100644
--- a/wirish/stm32f1/boards_setup.cpp
+++ b/wirish/stm32f1/boards_setup.cpp
@@ -28,11 +28,15 @@
* @file wirish/stm32f1/boards_setup.cpp
* @author Marti Bolivar <mbolivar@leaflabs.com>
* @brief STM32F1 chip setup.
+ *
+ * This file controls how init() behaves on the STM32F1. Be very
+ * careful when changing anything here. Many of these values depend
+ * upon each other.
*/
-#include <libmaple/rcc.h>
+#include "boards_private.h"
+
#include <libmaple/gpio.h>
-#include <libmaple/adc.h>
#include <libmaple/timer.h>
#include <libmaple/usb_cdcacm.h>
@@ -45,12 +49,11 @@ namespace wirish {
namespace priv {
static stm32f1_rcc_pll_data pll_data = {RCC_PLLMUL_9};
- rcc_pll_cfg board_pll_cfg = {RCC_PLLSRC_HSE, &pll_data};
+ rcc_pll_cfg w_board_pll_cfg = {RCC_PLLSRC_HSE, &pll_data};
+ adc_prescaler w_adc_pre = ADC_PRE_PCLK2_DIV_6;
+ adc_smp_rate w_adc_smp = ADC_SMPR_55_5;
-#if 0
- static void config_adc(const adc_dev* dev);
static void config_timer(timer_dev*);
-#endif
void board_reset_pll(void) {
// TODO
@@ -69,17 +72,8 @@ namespace wirish {
afio_init();
}
- void board_setup_adc(void) {
-#if 0
- rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_6);
- adc_foreach(config_adc);
-#endif
- }
-
void board_setup_timers(void) {
-#if 0
timer_foreach(config_timer);
-#endif
}
void board_setup_usb(void) {
@@ -92,19 +86,8 @@ namespace wirish {
* Auxiliary routines
*/
-#if 0
- static void config_adc(const adc_dev *dev) {
- adc_init(dev);
-
- adc_set_extsel(dev, ADC_SWSTART);
- adc_set_exttrig(dev, true);
-
- adc_enable(dev);
- adc_calibrate(dev);
- adc_set_sample_rate(dev, ADC_SMPR_55_5);
- }
-
static void config_timer(timer_dev *dev) {
+#if 0
timer_adv_reg_map *regs = (dev->regs).adv;
const uint16 full_overflow = 0xFFFF;
const uint16 half_duty = 0x8FFF;
@@ -135,7 +118,7 @@ namespace wirish {
}
timer_resume(dev);
- }
#endif
+ }
}
}
diff --git a/wirish/stm32f2/boards_setup.cpp b/wirish/stm32f2/boards_setup.cpp
index b3c690c..e1bf1fd 100644
--- a/wirish/stm32f2/boards_setup.cpp
+++ b/wirish/stm32f2/boards_setup.cpp
@@ -28,11 +28,18 @@
* @file wirish/stm32f2/boards_setup.cpp
* @author Marti Bolivar <mbolivar@leaflabs.com>
* @brief STM32F2 chip setup.
+ *
+ * This file controls how init() behaves on the STM32F2. Be very
+ * careful when changing anything here. Many of these values depend
+ * upon each other.
*/
-#include <libmaple/rcc.h>
+#include "boards_private.h"
+
#include <libmaple/gpio.h>
+#include <wirish/wirish_types.h>
+// PLL configuration for 25 MHz external oscillator --> 120 MHz SYSCLK.
#define PLL_Q 5
#define PLL_P 2
#define PLL_N 240
@@ -41,7 +48,18 @@ static stm32f2_rcc_pll_data pll_data = {PLL_Q, PLL_P, PLL_N, PLL_M};
namespace wirish {
namespace priv {
- rcc_pll_cfg board_pll_cfg = {RCC_PLLSRC_HSE, &pll_data};
+ // PLL clocked off of HSE, with above configuration data.
+ rcc_pll_cfg w_board_pll_cfg = {RCC_PLLSRC_HSE, &pll_data};
+ // As f_APB2 = 60 MHz (see board_setup_clock_prescalers),
+ // we need f_ADC = f_PCLK2 / 2 to get the (maximum)
+ // f_ADC = 30 MHz.
+ adc_prescaler w_adc_pre = ADC_PRE_PCLK2_DIV_2;
+ // With clocks as specified here (i.e. f_ADC = 30 MHz), this
+ // ADC sample rate allows for error less than 1/4 LSB with a
+ // 50 KOhm input impedance, assuming an internal sample and
+ // hold capacitance C_ADC at most 8.8 pF. See Equation 1 and
+ // Table 61 in the F2 datasheet for more details.
+ adc_smp_rate w_adc_smp = ADC_SMPR_144;
void board_reset_pll(void) {
// Set PLLCFGR to its reset value.
@@ -49,8 +67,13 @@ namespace wirish {
}
void board_setup_clock_prescalers(void) {
+ // With f_SYSCLK = 120 MHz (as determined by board_pll_cfg),
+ //
+ // f_AHB = f_SYSCLK / 1 = 120 MHz
rcc_set_prescaler(RCC_PRESCALER_AHB, RCC_AHB_SYSCLK_DIV_1);
+ // f_APB1 = f_AHB / 4 = 30 MHz
rcc_set_prescaler(RCC_PRESCALER_APB1, RCC_APB1_HCLK_DIV_4);
+ // f_APB2 = f_AHB / 2 = 60 MHz
rcc_set_prescaler(RCC_PRESCALER_APB2, RCC_APB2_HCLK_DIV_2);
}
@@ -58,10 +81,6 @@ namespace wirish {
gpio_init_all();
}
- void board_setup_adc(void) {
- // TODO
- }
-
void board_setup_timers(void) {
// TODO
}
@@ -72,4 +91,3 @@ namespace wirish {
}
}
-