aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple
diff options
context:
space:
mode:
Diffstat (limited to 'libmaple')
-rw-r--r--libmaple/adc.c171
-rw-r--r--libmaple/adc.h368
-rw-r--r--libmaple/bitband.h110
-rw-r--r--libmaple/bkp.c114
-rw-r--r--libmaple/bkp.h106
-rw-r--r--libmaple/dac.c97
-rw-r--r--libmaple/dac.h150
-rw-r--r--libmaple/delay.h24
-rw-r--r--libmaple/dma.c399
-rw-r--r--libmaple/dma.h449
-rw-r--r--libmaple/exc.S70
-rw-r--r--libmaple/exti.c292
-rw-r--r--libmaple/exti.h158
-rw-r--r--libmaple/flash.c39
-rw-r--r--libmaple/flash.h98
-rw-r--r--libmaple/fsmc.c144
-rw-r--r--libmaple/fsmc.h315
-rw-r--r--libmaple/gpio.c165
-rw-r--r--libmaple/gpio.h419
-rw-r--r--libmaple/i2c.c446
-rw-r--r--libmaple/i2c.h261
-rw-r--r--libmaple/iwdg.c41
-rw-r--r--libmaple/iwdg.h86
-rw-r--r--libmaple/libmaple.h165
-rw-r--r--libmaple/libmaple_types.h3
-rw-r--r--libmaple/nvic.c79
-rw-r--r--libmaple/nvic.h220
-rw-r--r--libmaple/pwr.c42
-rw-r--r--libmaple/pwr.h63
-rw-r--r--libmaple/rcc.c155
-rw-r--r--libmaple/rcc.h632
-rw-r--r--libmaple/ring_buffer.h118
-rw-r--r--libmaple/rules.mk6
-rw-r--r--libmaple/scb.h60
-rw-r--r--libmaple/spi.c318
-rw-r--r--libmaple/spi.h492
-rw-r--r--libmaple/stm32.h22
-rw-r--r--libmaple/systick.c56
-rw-r--r--libmaple/systick.h46
-rw-r--r--libmaple/timer.c443
-rw-r--r--libmaple/timer.h1011
-rw-r--r--libmaple/timers.c530
-rw-r--r--libmaple/timers.h435
-rw-r--r--libmaple/usart.c344
-rw-r--r--libmaple/usart.h359
-rw-r--r--libmaple/usb/descriptors.c73
-rw-r--r--libmaple/usb/descriptors.h2
-rw-r--r--libmaple/usb/usb.c27
-rw-r--r--libmaple/usb/usb.h4
-rw-r--r--libmaple/usb/usb_callbacks.c2
-rw-r--r--libmaple/usb/usb_config.h77
-rw-r--r--libmaple/usb/usb_hardware.c41
-rw-r--r--libmaple/usb/usb_hardware.h36
-rw-r--r--libmaple/usb/usb_lib/usb_mem.c4
-rw-r--r--libmaple/usb/usb_lib/usb_mem.h2
-rw-r--r--libmaple/util.c106
-rw-r--r--libmaple/util.h68
57 files changed, 7448 insertions, 3115 deletions
diff --git a/libmaple/adc.c b/libmaple/adc.c
index 3e6818c..73dce0a 100644
--- a/libmaple/adc.c
+++ b/libmaple/adc.c
@@ -23,85 +23,92 @@
*****************************************************************************/
/**
- * @brief Analog to digital converter routines
- */
-
-#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.
+ * @file adc.c
*
- * 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
+ * @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*/
-
-void set_adc_smprx(adc_smp_rate smp_rate);
+ * At 55.5 cycles/sample, the external input impedance < 50kOhms.
+ *
+ * See STM32 manual RM0008 for how to calculate this.
+ */
-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);
+#include "libmaple.h"
+#include "rcc.h"
+#include "adc.h"
- ADC_CR1 = 0;
- /* Software triggers conversions */
- ADC_CR2 = CR2_EXTSEL_SWSTART | CR2_EXTTRIG;
- ADC_SQR1 = 0;
+adc_dev adc1 = {
+ .regs = ADC1_BASE,
+ .clk_id = RCC_ADC1
+};
+const adc_dev *ADC1 = &adc1;
- /* Set the sample conversion time. See note above for impedance
- requirements. */
- adc_set_sample_rate(smp_rate);
+adc_dev adc2 = {
+ .regs = ADC2_BASE,
+ .clk_id = RCC_ADC2
+};
+const adc_dev *ADC2 = &adc2;
- /* Enable the ADC */
- CR2_ADON_BIT = 1;
+#ifdef STM32_HIGH_DENSITY
+adc_dev adc3 = {
+ .regs = ADC3_BASE,
+ .clk_id = RCC_ADC3
+};
+const adc_dev *ADC3 = &adc3;
+#endif
- /* Reset the calibration registers and then perform a reset */
- CR2_RSTCAL_BIT = 1;
- while(CR2_RSTCAL_BIT)
- ;
-
- CR2_CAL_BIT = 1;
- while(CR2_CAL_BIT)
- ;
+/**
+ * @brief Initialize an ADC peripheral.
+ *
+ * Initializes the RCC clock line for the given peripheral, using ADC
+ * prescaler RCC_ADCPRE_PCLK_DIV_6. Resets ADC device registers.
+ *
+ * @param dev ADC peripheral to initialize
+ */
+void adc_init(const adc_dev *dev) {
+ rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_6);
+ rcc_clk_enable(dev->clk_id);
+ rcc_reset_dev(dev->clk_id);
}
+/**
+ * @brief Set external event select for regular group
+ * @param dev ADC device
+ * @param event Event used to trigger the start of conversion.
+ * @see adc_extsel_event
+ */
+void adc_set_extsel(const adc_dev *dev, adc_extsel_event event) {
+ uint32 cr2 = dev->regs->CR2;
+ cr2 &= ~ADC_CR2_EXTSEL;
+ cr2 |= event;
+ dev->regs->CR2 = cr2;
+}
-void adc_disable(void) {
- CR2_ADON_BIT = 0;
+/**
+ * @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
}
-/* 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 +117,44 @@ 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
+ */
+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.
+ * @param dev ADC device to use for reading.
+ * @param channel channel to convert
+ * @return conversion result
+ */
+uint16 adc_read(const adc_dev *dev, uint8 channel) {
+ adc_reg_map *regs = dev->regs;
+
+ adc_set_reg_seqlen(dev, 1);
+
+ regs->SQR3 = channel;
+ regs->CR2 |= ADC_CR2_SWSTART;
+ while(!(regs->SR & ADC_SR_EOC))
+ ;
+
+ return (uint16)(regs->DR & ADC_DR_DATA);
}
diff --git a/libmaple/adc.h b/libmaple/adc.h
index 976986f..c6a67a0 100644
--- a/libmaple/adc.h
+++ b/libmaple/adc.h
@@ -25,91 +25,335 @@
/**
* @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 "libmaple.h"
+#include "bitband.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 */
-
-/* TODO: We'll only use ADC1 for now. See page 41 of the manual for
- ADC2 and ADC3's real 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) */
+/** ADC register map type. */
+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;
+
+/** ADC device type. */
+typedef struct adc_dev {
+ adc_reg_map *regs; /**< Register map */
+ rcc_clk_id clk_id; /**< RCC clock information */
+} adc_dev;
+
+/** ADC1 device. */
+extern const adc_dev *ADC1;
+/** ADC2 device. */
+extern const adc_dev *ADC2;
+#ifdef STM32_HIGH_DENSITY
+/** ADC3 device. */
+extern const adc_dev *ADC3;
+#endif
+
+/*
+ * Register map base pointers
+ */
+
+/** ADC1 register map base pointer. */
+#define ADC1_BASE ((adc_reg_map*)0x40012400)
+/** ADC2 register map base pointer. */
+#define ADC2_BASE ((adc_reg_map*)0x40012800)
+/** ADC3 register map base pointer. */
+#define ADC3_BASE ((adc_reg_map*)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
+
+#define ADC_SR_AWD BIT(ADC_SR_AWD_BIT)
+#define ADC_SR_EOC BIT(ADC_SR_EOC_BIT)
+#define ADC_SR_JEOC BIT(ADC_SR_JEOC_BIT)
+#define ADC_SR_JSTRT BIT(ADC_SR_JSTRT_BIT)
+#define ADC_SR_STRT BIT(ADC_SR_STRT_BIT)
+
+/* Control register 1 */
+
+#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_JAWDEN_BIT 22
+#define ADC_CR1_AWDEN_BIT 23
+
+#define ADC_CR1_AWDCH (0x1F)
+#define ADC_CR1_EOCIE BIT(ADC_CR1_EOCIE_BIT)
+#define ADC_CR1_AWDIE BIT(ADC_CR1_AWDIE_BIT)
+#define ADC_CR1_JEOCIE BIT(ADC_CR1_JEOCIE_BIT)
+#define ADC_CR1_SCAN BIT(ADC_CR1_SCAN_BIT)
+#define ADC_CR1_AWDSGL BIT(ADC_CR1_AWDSGL_BIT)
+#define ADC_CR1_JAUTO BIT(ADC_CR1_JAUTO_BIT)
+#define ADC_CR1_DISCEN BIT(ADC_CR1_DISCEN_BIT)
+#define ADC_CR1_JDISCEN BIT(ADC_CR1_JDISCEN_BIT)
+#define ADC_CR1_DISCNUM (0xE000)
+#define ADC_CR1_JAWDEN BIT(ADC_CR1_JAWDEN_BIT)
+#define ADC_CR1_AWDEN BIT(ADC_CR1_AWDEN_BIT)
+
+/* 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)
+
+/* Sample time register 1 */
+
+#define ADC_SMPR1_SMP17 (0x7 << 21)
+#define ADC_SMPR1_SMP16 (0x7 << 18)
+#define ADC_SMPR1_SMP15 (0x7 << 15)
+#define ADC_SMPR1_SMP14 (0x7 << 12)
+#define ADC_SMPR1_SMP13 (0x7 << 9)
+#define ADC_SMPR1_SMP12 (0x7 << 6)
+#define ADC_SMPR1_SMP11 (0x7 << 3)
+#define ADC_SMPR1_SMP10 0x7
+
+/* Sample time register 2 */
+
+#define ADC_SMPR2_SMP9 (0x7 << 27)
+#define ADC_SMPR2_SMP8 (0x7 << 24)
+#define ADC_SMPR2_SMP7 (0x7 << 21)
+#define ADC_SMPR2_SMP6 (0x7 << 18)
+#define ADC_SMPR2_SMP5 (0x7 << 15)
+#define ADC_SMPR2_SMP4 (0x7 << 12)
+#define ADC_SMPR2_SMP3 (0x7 << 9)
+#define ADC_SMPR2_SMP2 (0x7 << 6)
+#define ADC_SMPR2_SMP1 (0x7 << 3)
+#define ADC_SMPR2_SMP0 0x7
+
+/* Injected channel data offset register */
+
+#define ADC_JOFR_JOFFSET 0x3FF
+
+/* Watchdog high threshold register */
+
+#define ADC_HTR_HT 0x3FF
+
+/* Watchdog low threshold register */
+
+#define ADC_LTR_LT 0x3FF
+
+/* Regular sequence register 1 */
+
+#define ADC_SQR1_L (0x1F << 20)
+#define ADC_SQR1_SQ16 (0x1F << 15)
+#define ADC_SQR1_SQ15 (0x1F << 10)
+#define ADC_SQR1_SQ14 (0x1F << 5)
+#define ADC_SQR1_SQ13 0x1F
+
+/* Regular sequence register 2 */
+
+#define ADC_SQR2_SQ12 (0x1F << 25)
+#define ADC_SQR2_SQ11 (0x1F << 20)
+#define ADC_SQR2_SQ10 (0x1F << 16)
+#define ADC_SQR2_SQ9 (0x1F << 10)
+#define ADC_SQR2_SQ8 (0x1F << 5)
+#define ADC_SQR2_SQ7 0x1F
+
+/* Regular sequence register 3 */
+
+#define ADC_SQR3_SQ6 (0x1F << 25)
+#define ADC_SQR3_SQ5 (0x1F << 20)
+#define ADC_SQR3_SQ4 (0x1F << 16)
+#define ADC_SQR3_SQ3 (0x1F << 10)
+#define ADC_SQR3_SQ2 (0x1F << 5)
+#define ADC_SQR3_SQ1 0x1F
+
+/* Injected sequence register */
+
+#define ADC_JSQR_JL (0x3 << 20)
+#define ADC_JSQR_JL_1CONV (0x0 << 20)
+#define ADC_JSQR_JL_2CONV (0x1 << 20)
+#define ADC_JSQR_JL_3CONV (0x2 << 20)
+#define ADC_JSQR_JL_4CONV (0x3 << 20)
+#define ADC_JSQR_JSQ4 (0x1F << 15)
+#define ADC_JSQR_JSQ3 (0x1F << 10)
+#define ADC_JSQR_JSQ2 (0x1F << 5)
+#define ADC_JSQR_JSQ1 0x1F
+
+/* Injected data registers */
+
+#define ADC_JDR_JDATA 0xFFFF
+
+/* Regular data register */
+
+#define ADC_DR_ADC2DATA (0xFFFF << 16)
+#define ADC_DR_DATA 0xFFFF
+
+void adc_init(const adc_dev *dev);
+
+/**
+ * @brief External event selector for regular group conversion.
+ * @see adc_set_extsel
+ */
+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_extsel(const adc_dev *dev, adc_extsel_event event);
+void adc_foreach(void (*fn)(const adc_dev*));
/** 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(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);
-void adc_set_sample_rate(adc_smp_rate smp_rate);
-
-void adc_disable(void);
+/**
+ * @brief Set the regular channel sequence length.
+ *
+ * Defines the total number of conversions in the regular channel
+ * conversion sequence.
+ *
+ * @param dev ADC device.
+ * @param length Regular channel sequence length, from 1 to 16.
+ */
+static inline void adc_set_reg_seqlen(const adc_dev *dev, uint8 length) {
+ uint32 tmp = dev->regs->SQR1;
+ tmp &= ~ADC_SQR1_L;
+ tmp |= (length - 1) << 20;
+ dev->regs->SQR1 = tmp;
+}
/**
- * Perform a single conversion on ADC[0-15].
- * PRECONDITIONS:
- * adc initialized */
-static inline int adc_read(int channel) {
- /* Set channel */
- ADC_SQR3 = channel;
+ * @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;
+}
- /* Start the conversion */
- CR2_SWSTART_BIT = 1;
+/**
+ * @brief Enable an adc peripheral
+ * @param dev ADC device to enable
+ */
+static inline void adc_enable(const adc_dev *dev) {
+ *bb_perip(&dev->regs->CR2, ADC_CR2_ADON_BIT) = 1;
+}
- /* Wait for it to finish */
- while(SR_EOC_BIT == 0)
- ;
+/**
+ * @brief Disable an ADC peripheral
+ * @param dev ADC device to disable
+ */
+static inline void adc_disable(const adc_dev *dev) {
+ *bb_perip(&dev->regs->CR2, ADC_CR2_ADON_BIT) = 0;
+}
- return ADC_DR;
+/**
+ * @brief Disable all ADC peripherals.
+ */
+static inline void adc_disable_all(void) {
+ adc_foreach(adc_disable);
}
#ifdef __cplusplus
} // extern "C"
#endif
-#endif
+#endif
diff --git a/libmaple/bitband.h b/libmaple/bitband.h
new file mode 100644
index 0000000..870abe9
--- /dev/null
+++ b/libmaple/bitband.h
@@ -0,0 +1,110 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2011 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 bitband.h
+ *
+ * @brief Bit-banding utility functions
+ */
+
+#ifndef _BITBAND_H_
+#define _BITBAND_H_
+
+#define BB_SRAM_REF 0x20000000
+#define BB_SRAM_BASE 0x22000000
+#define BB_PERI_REF 0x40000000
+#define BB_PERI_BASE 0x42000000
+
+static inline __io uint32* __bb_addr(__io void*, uint32, uint32, uint32);
+
+/**
+ * @brief Obtain a pointer to the bit-band address corresponding to a
+ * bit in a volatile SRAM address.
+ * @param address Address in the bit-banded SRAM region
+ * @param bit Bit in address to bit-band
+ */
+static inline __io uint32* bb_sramp(__io void *address, uint32 bit) {
+ return __bb_addr(address, bit, BB_SRAM_BASE, BB_SRAM_REF);
+}
+
+/**
+ * @brief Get a bit from an address in the SRAM bit-band region.
+ * @param address Address in the SRAM bit-band region to read from
+ * @param bit Bit in address to read
+ * @return bit's value in address.
+ */
+static inline uint8 bb_sram_get_bit(__io void *address, uint32 bit) {
+ return *bb_sramp(address, bit);
+}
+
+/**
+ * @brief Set a bit in an address in the SRAM bit-band region.
+ * @param address Address in the SRAM bit-band region to write to
+ * @param bit Bit in address to write to
+ * @param val Value to write for bit, either 0 or 1.
+ */
+static inline void bb_sram_set_bit(__io void *address, uint32 bit, uint8 val) {
+ *bb_sramp(address, bit) = val;
+}
+
+/**
+ * @brief Obtain a pointer to the bit-band address corresponding to a
+ * bit in a peripheral address.
+ * @param address Address in the bit-banded peripheral region
+ * @param bit Bit in address to bit-band
+ */
+static inline __io uint32* bb_perip(__io void *address, uint32 bit) {
+ return __bb_addr(address, bit, BB_PERI_BASE, BB_PERI_REF);
+}
+
+/**
+ * @brief Get a bit from an address in the peripheral bit-band region.
+ * @param address Address in the peripheral bit-band region to read from
+ * @param bit Bit in address to read
+ * @return bit's value in address.
+ */
+static inline uint8 bb_peri_get_bit(__io void *address, uint32 bit) {
+ return *bb_perip(address, bit);
+}
+
+/**
+ * @brief Set a bit in an address in the peripheral bit-band region.
+ * @param address Address in the peripheral bit-band region to write to
+ * @param bit Bit in address to write to
+ * @param val Value to write for bit, either 0 or 1.
+ */
+static inline void bb_peri_set_bit(__io void *address, uint32 bit, uint8 val) {
+ *bb_perip(address, bit) = val;
+}
+
+static inline __io uint32* __bb_addr(__io void *address,
+ uint32 bit,
+ uint32 bb_base,
+ uint32 bb_ref) {
+ return (__io uint32*)(bb_base + ((uint32)address - bb_ref) * 32 + bit * 4);
+}
+
+#endif /* _BITBAND_H_ */
diff --git a/libmaple/bkp.c b/libmaple/bkp.c
index e89abd0..aaccb1f 100644
--- a/libmaple/bkp.c
+++ b/libmaple/bkp.c
@@ -24,63 +24,101 @@
* SOFTWARE.
*****************************************************************************/
-#include "libmaple.h"
#include "bkp.h"
#include "pwr.h"
#include "rcc.h"
-#include "util.h"
+#include "bitband.h"
-/* Data register memory layout is not contiguous. It's split up from
- 1--NR_LOW_DRS, beginning at BKP_LOW_OFFSET, through
- (NR_LOW_DRS+1)--NR_DRS, beginning at BKP_HIGH_OFFSET. */
-#define NR_LOW_DRS 10
-#define BKP_LOW_OFFSET 0x4 /* start offset for data registers 1--10 */
-#define BKP_HIGH_OFFSET 0x40 /* start offset for data registers 11--42 */
+static inline __io uint32* data_register(uint8 reg);
-inline volatile uint16* reg_addr(uint8 reg) {
- if (1 <= reg) {
- if (reg <= NR_LOW_DRS) {
- return (volatile uint16*)(BKP_BASE + BKP_LOW_OFFSET +
- (reg - 1) * 4);
- } else if (reg <= NR_BKP_REGS) {
- return (volatile uint16*)(BKP_BASE + BKP_HIGH_OFFSET +
- (reg - NR_LOW_DRS - 1) * 4);
- }
- }
- return 0;
-}
+bkp_dev bkp = {
+ .regs = BKP_BASE,
+};
-void bkp_init(void) {
- /* Set PWREN (28) and BKPEN (27) bits */
- __set_bits(RCC_APB1ENR, BIT(28) | BIT(27));
-}
+const bkp_dev *BKP = &bkp;
-void bkp_disable(void) {
- __clear_bits(RCC_APB1ENR, BIT(28) | BIT(27));
+/**
+ * @brief Initialize backup interface.
+ *
+ * Enables the power and backup interface clocks, and resets the
+ * backup device.
+ */
+void bkp_init(void) {
+ /* Don't call pwr_init(), or you'll reset the device. We just
+ * need the clock. */
+ rcc_clk_enable(RCC_PWR);
+ rcc_clk_enable(RCC_BKP);
+ rcc_reset_dev(RCC_BKP);
}
+/**
+ * Enable write access to the backup registers. Backup interface must
+ * be initialized for subsequent register writes to work.
+ * @see bkp_init()
+ */
void bkp_enable_writes(void) {
- /* Set the DBP bit in PWR_CR */
- __write(BITBAND_PERI(PWR_CR, PWR_CR_DBP), 1);
+ *bb_perip(&PWR_BASE->CR, PWR_CR_DBP) = 1;
}
+/**
+ * Disable write access to the backup registers.
+ */
void bkp_disable_writes(void) {
- __write(BITBAND_PERI(PWR_CR, PWR_CR_DBP), 0);
+ *bb_perip(&PWR_BASE->CR, PWR_CR_DBP) = 0;
}
+/**
+ * Read a value from given backup data register.
+ * @param reg Data register to read, from 1 to BKP_NR_DATA_REGS (10 on
+ * medium-density devices, 42 on high-density devices).
+ */
uint16 bkp_read(uint8 reg) {
- volatile uint16* addr = reg_addr(reg);
- if (addr != 0) {
- return *addr;
+ __io uint32* dr = data_register(reg);
+ if (!dr) {
+ ASSERT(0); /* nonexistent register */
+ return 0;
}
- ASSERT(0); /* nonexistent register */
- return 0;
+ return (uint16)*dr;
}
+/**
+ * @brief Write a value to given data register.
+ *
+ * Write access to backup registers must be enabled.
+ *
+ * @param reg Data register to write, from 1 to BKP_NR_DATA_REGS (10
+ * on medium-density devices, 42 on high-density devices).
+ * @param val Value to write into the register.
+ * @see bkp_enable_writes()
+ */
void bkp_write(uint8 reg, uint16 val) {
- volatile uint16* addr = reg_addr(reg);
- if (addr != 0) {
- *addr = val;
+ __io uint32* dr = data_register(reg);
+ if (!dr) {
+ ASSERT(0); /* nonexistent register */
+ return;
+ }
+ *dr = (uint32)val;
+}
+
+/*
+ * Data register memory layout is not contiguous. It's split up from
+ * 1--NR_LOW_DRS, beginning at BKP_BASE->DR1, through to
+ * (NR_LOW_DRS+1)--BKP_NR_DATA_REGS, beginning at BKP_BASE->DR11.
+ */
+#define NR_LOW_DRS 10
+
+static inline __io uint32* data_register(uint8 reg) {
+ if (reg < 1 || reg > BKP_NR_DATA_REGS) {
+ return 0;
+ }
+
+#if BKP_NR_DATA_REGS == NR_LOW_DRS
+ return (uint32*)BKP_BASE + reg;
+#else
+ if (reg <= NR_LOW_DRS) {
+ return (uint32*)BKP_BASE + reg;
+ } else {
+ return (uint32*)&(BKP_BASE->DR11) + (reg - NR_LOW_DRS - 1);
}
- ASSERT(0); /* nonexistent register */
+#endif
}
diff --git a/libmaple/bkp.h b/libmaple/bkp.h
index 9ad4c41..97edd2a 100644
--- a/libmaple/bkp.h
+++ b/libmaple/bkp.h
@@ -32,51 +32,87 @@
#ifndef _BKP_H_
#define _BKP_H_
+#include "libmaple.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-#define BKP_BASE 0x40006C00
-#define BKP_RTCCR (BKP_BASE + 0x2C)
-#define BKP_CR (BKP_BASE + 0x30)
-#define BKP_CSR (BKP_BASE + 0x34)
+#if defined(STM32_MEDIUM_DENSITY)
+#define BKP_NR_DATA_REGS 10
+#elif defined(STM32_HIGH_DENSITY)
+#define BKP_NR_DATA_REGS 42
+#endif
-/**
- * Initialize backup interface. This function enables the power and
- * backup interface clocks. It does not enable write access to the
- * backup registers.
- */
-void bkp_init(void);
+/** Backup peripheral register map type. */
+typedef struct bkp_reg_map {
+ const uint32 RESERVED1; ///< Reserved
+ __io uint32 DR1; ///< Data register 1
+ __io uint32 DR2; ///< Data register 2
+ __io uint32 DR3; ///< Data register 3
+ __io uint32 DR4; ///< Data register 4
+ __io uint32 DR5; ///< Data register 5
+ __io uint32 DR6; ///< Data register 6
+ __io uint32 DR7; ///< Data register 7
+ __io uint32 DR8; ///< Data register 8
+ __io uint32 DR9; ///< Data register 9
+ __io uint32 DR10; ///< Data register 10
+ __io uint32 RTCCR; ///< RTC control register
+ __io uint32 CR; ///< Control register
+ __io uint32 CSR; ///< Control and status register
+#ifdef STM32_HIGH_DENSITY
+ const uint32 RESERVED2; ///< Reserved
+ const uint32 RESERVED3; ///< Reserved
+ __io uint32 DR11; ///< Data register 11
+ __io uint32 DR12; ///< Data register 12
+ __io uint32 DR13; ///< Data register 13
+ __io uint32 DR14; ///< Data register 14
+ __io uint32 DR15; ///< Data register 15
+ __io uint32 DR16; ///< Data register 16
+ __io uint32 DR17; ///< Data register 17
+ __io uint32 DR18; ///< Data register 18
+ __io uint32 DR19; ///< Data register 19
+ __io uint32 DR20; ///< Data register 20
+ __io uint32 DR21; ///< Data register 21
+ __io uint32 DR22; ///< Data register 22
+ __io uint32 DR23; ///< Data register 23
+ __io uint32 DR24; ///< Data register 24
+ __io uint32 DR25; ///< Data register 25
+ __io uint32 DR26; ///< Data register 26
+ __io uint32 DR27; ///< Data register 27
+ __io uint32 DR28; ///< Data register 28
+ __io uint32 DR29; ///< Data register 29
+ __io uint32 DR30; ///< Data register 30
+ __io uint32 DR31; ///< Data register 31
+ __io uint32 DR32; ///< Data register 32
+ __io uint32 DR33; ///< Data register 33
+ __io uint32 DR34; ///< Data register 34
+ __io uint32 DR35; ///< Data register 35
+ __io uint32 DR36; ///< Data register 36
+ __io uint32 DR37; ///< Data register 37
+ __io uint32 DR38; ///< Data register 38
+ __io uint32 DR39; ///< Data register 39
+ __io uint32 DR40; ///< Data register 40
+ __io uint32 DR41; ///< Data register 41
+ __io uint32 DR42; ///< Data register 42
+#endif
+} bkp_reg_map;
-/** Disable power and backup interface clocks. */
-void bkp_disable(void);
+/** Backup peripheral register map base pointer. */
+#define BKP_BASE ((bkp_reg_map*)0x40006C00)
-/**
- * Enable write access to the backup registers. Backup interface must
- * be initialized for subsequent register writes to work.
- * @see bkp_init()
- */
-void bkp_enable_writes(void);
+/** Backup peripheral device type. */
+typedef struct bkp_dev {
+ bkp_reg_map *regs; /**< Register map */
+} bkp_dev;
-/**
- * Disable write access to the backup registers. Does not disable
- * backup interface clocks.
- */
-void bkp_disable_writes(void);
+/** Backup device. */
+extern const bkp_dev *BKP;
-/**
- * Read a value from given backup data register.
- * @param reg Data register to read, from 1 to NR_BKP_REGS (10 on Maple).
- */
+void bkp_init(void);
+void bkp_enable_writes(void);
+void bkp_disable_writes(void);
uint16 bkp_read(uint8 reg);
-
-/**
- * Write a value to given data register. Backup interface must have
- * been previously initialized, and write access to backup registers
- * must be enabled.
- * @param reg Data register to write, from 1 to NR_BKP_REGS (10 on Maple).
- * @param val Value to write into the register.
- */
void bkp_write(uint8 reg, uint16 val);
#ifdef __cplusplus
diff --git a/libmaple/dac.c b/libmaple/dac.c
index 63a96ac..1726d19 100644
--- a/libmaple/dac.c
+++ b/libmaple/dac.c
@@ -23,43 +23,96 @@
*****************************************************************************/
#include "libmaple.h"
-#include "rcc.h"
#include "gpio.h"
#include "dac.h"
+#ifdef STM32_HIGH_DENSITY
+
/**
* @brief DAC peripheral routines.
*/
-/* This numbering follows the registers (1-indexed) */
-#define DAC_CH1 1
-#define DAC_CH2 2
-
-DAC_Map *dac = (DAC_Map*)(DAC_BASE);
+dac_dev dac = {
+ .regs = DAC_BASE,
+};
+const dac_dev *DAC = &dac;
-/* Sets up the DAC peripheral */
-void dac_init(void) {
+/**
+ * @brief Initialize the digital to analog converter
+ * @param dev DAC device
+ * @param flags Flags:
+ * DAC_CH1: Enable channel 1
+ * DAC_CH2: Enable channel 2
+ * @sideeffect May set PA4 or PA5 to INPUT_ANALOG
+ */
+void dac_init(const dac_dev *dev, uint32 flags) {
/* First turn on the clock */
rcc_clk_enable(RCC_DAC);
+ rcc_reset_dev(RCC_DAC);
- /* Then setup ANALOG mode on PA4 and PA5 */
- gpio_set_mode(GPIOA_BASE, 4, CNF_INPUT_ANALOG);
- gpio_set_mode(GPIOA_BASE, 5, CNF_INPUT_ANALOG);
+ if (flags & DAC_CH1) {
+ dac_enable_channel(dev, 1);
+ }
- /* Then do register stuff. Default does no triggering, and
- * buffered output, so all good. */
- dac->CR = DAC_CR_EN1 | DAC_CR_EN2;
+ if (flags & DAC_CH2) {
+ dac_enable_channel(dev, 2);
+ }
}
-void dac_write(uint8 chan, uint16 val) {
- switch(chan) {
- case DAC_CH1:
- dac->DHR12R1 = 0x0FFF & val;
+/**
+ * @brief Write a 12-bit value to the DAC to output
+ * @param dev DAC device
+ * @param channel channel to select (1 or 2)
+ * @param val value to write
+ */
+void dac_write_channel(const dac_dev *dev, uint8 channel, uint16 val) {
+ switch(channel) {
+ case 1:
+ dev->regs->DHR12R1 = DAC_DHR12R1_DACC1DHR & val;
break;
- case DAC_CH2:
- dac->DHR12R2 = 0x0FFF & val;
+ case 2:
+ dev->regs->DHR12R2 = DAC_DHR12R2_DACC2DHR & val;
break;
- default:
- ASSERT(0); // can't happen
}
}
+
+/**
+ * @brief Enable a DAC channel
+ * @param dev DAC device
+ * @param channel channel to enable, either 1 or 2
+ * @sideeffect May change pin mode of PA4 or PA5
+ */
+void dac_enable_channel(const dac_dev *dev, uint8 channel) {
+ /*
+ * Setup ANALOG mode on PA4 and PA5. This mapping is consistent across
+ * all STM32 chips with a DAC. See RM0008 12.2.
+ */
+ switch (channel) {
+ case 1:
+ gpio_set_mode(GPIOA, 4, GPIO_INPUT_ANALOG);
+ dev->regs->CR |= DAC_CR_EN1;
+ break;
+ case 2:
+ gpio_set_mode(GPIOA, 5, GPIO_INPUT_ANALOG);
+ dev->regs->CR |= DAC_CR_EN2;
+ break;
+ }
+}
+
+/**
+ * @brief Disable a DAC channel
+ * @param dev DAC device
+ * @param channel channel to disable, either 1 or 2
+ */
+void dac_disable_channel(const dac_dev *dev, uint8 channel) {
+ switch (channel) {
+ case 1:
+ dev->regs->CR &= ~DAC_CR_EN1;
+ break;
+ case 2:
+ dev->regs->CR &= ~DAC_CR_EN2;
+ break;
+ }
+}
+
+#endif /* STM32_HIGH_DENSITY */
diff --git a/libmaple/dac.h b/libmaple/dac.h
index 340a49a..cf4fe85 100644
--- a/libmaple/dac.h
+++ b/libmaple/dac.h
@@ -22,92 +22,146 @@
* THE SOFTWARE.
*****************************************************************************/
-/*
- * See ../notes/dac.txt for more info
- */
-
/**
* @file dac.h
+ * @brief Digital to analog converter header file
*/
+/* See notes/dac.txt for more info */
+
#ifndef _DAC_H_
#define _DAC_H_
+#include "rcc.h"
+
#ifdef __cplusplus
extern "C"{
#endif
-#define DAC_BASE 0x40007400
-
-typedef struct {
- volatile uint32 CR;
- volatile uint32 SWTRIGR;
- volatile uint32 DHR12R1;
- volatile uint32 DHR12L1;
- volatile uint32 DHR8R1;
- volatile uint32 DHR12R2;
- volatile uint32 DHR12L2;
- volatile uint32 DHR8R2;
- volatile uint32 DHR12RD;
- volatile uint32 DHR12LD;
- volatile uint32 DHR8RD;
- volatile uint32 DOR1;
- volatile uint32 DOR2;
-} DAC_Map;
-
-/* There's only one DAC, so expose it. */
-extern DAC_Map *dac;
-
-// And here are the register bit ranges
-#define DAC_CR_EN1 BIT(0)
-#define DAC_CR_BOFF1 BIT(1)
-#define DAC_CR_TEN1 BIT(2)
-#define DAC_CR_TSEL1 (BIT(3) | BIT(4) | BIT(5))
-#define DAC_CR_WAVE1 (BIT(6) | BIT(7))
-#define DAC_CR_MAMP1 (BIT(8) | BIT(9) | BIT(10) | BIT(11))
-#define DAC_CR_DMAEN1 BIT(12)
-#define DAC_CR_EN2 BIT(16)
-#define DAC_CR_BOFF2 BIT(17)
-#define DAC_CR_TEN2 BIT(18)
-#define DAC_CR_TSEL2 (BIT(19) | BIT(20) | BIT(21))
-#define DAC_CR_WAVE2 (BIT(22) | BIT(23))
-#define DAC_CR_MAMP2 (BIT(24) | BIT(25) | BIT(26) | BIT(27))
-#define DAC_CR_DMAEN2 BIT(28)
-
-#define DAC_SWTRIGR_SWTRIG1 BIT(0)
-#define DAC_SWTRIGR_SWTRIG2 BIT(1)
+/*
+ * Register maps
+ */
+
+/** DAC register map. */
+typedef struct dac_reg_map {
+ __io uint32 CR; /**< Control register */
+ __io uint32 SWTRIGR; /**< Software trigger register */
+ __io uint32 DHR12R1; /**< Channel 1 12-bit right-aligned data
+ holding register */
+ __io uint32 DHR12L1; /**< Channel 1 12-bit left-aligned data
+ holding register */
+ __io uint32 DHR8R1; /**< Channel 1 8-bit left-aligned data
+ holding register */
+ __io uint32 DHR12R2; /**< Channel 2 12-bit right-aligned data
+ holding register */
+ __io uint32 DHR12L2; /**< Channel 2 12-bit left-aligned data
+ holding register */
+ __io uint32 DHR8R2; /**< Channel 2 8-bit left-aligned data
+ holding register */
+ __io uint32 DHR12RD; /**< Dual DAC 12-bit right-aligned data
+ holding register */
+ __io uint32 DHR12LD; /**< Dual DAC 12-bit left-aligned data
+ holding register */
+ __io uint32 DHR8RD; /**< Dual DAC 8-bit left-aligned data holding
+ register */
+ __io uint32 DOR1; /**< Channel 1 data output register */
+ __io uint32 DOR2; /**< Channel 2 data output register */
+} dac_reg_map;
+
+/** DAC register map base address */
+#define DAC_BASE ((dac_reg_map*)0x40007400)
+
+/*
+ * Devices
+ */
+
+/** DAC device type. */
+typedef struct dac_dev {
+ dac_reg_map *regs; /**< Register map */
+} dac_dev;
+
+/** DAC device. */
+extern const dac_dev *DAC;
+
+/*
+ * Register bit definitions
+ */
+/* Control register */
+/* Channel 1 control */
+#define DAC_CR_EN1 BIT(0) /* Enable */
+#define DAC_CR_BOFF1 BIT(1) /* Output buffer disable */
+#define DAC_CR_TEN1 BIT(2) /* Trigger enable */
+#define DAC_CR_TSEL1 (0x7 << 3) /* Trigger selection */
+#define DAC_CR_WAVE1 (0x3 << 6) /* Noise/triangle wave enable */
+#define DAC_CR_MAMP1 (0xF << 8) /* Mask/amplitude selector */
+#define DAC_CR_DMAEN1 BIT(12) /* DMA enable */
+/* Channel 2 control */
+#define DAC_CR_EN2 BIT(16) /* Enable */
+#define DAC_CR_BOFF2 BIT(17) /* Output buffer disable */
+#define DAC_CR_TEN2 BIT(18) /* Trigger enable */
+#define DAC_CR_TSEL2 (0x7 << 19) /* Trigger selection */
+#define DAC_CR_WAVE2 (0x3 << 22) /* Noise/triangle wave generation*/
+#define DAC_CR_MAMP2 (0xF << 24) /* Mask/amplitude selector */
+#define DAC_CR_DMAEN2 BIT(28) /* DMA enable */
+
+/* Software trigger register */
+#define DAC_SWTRIGR_SWTRIG1 BIT(0) /* Channel 1 software trigger */
+#define DAC_SWTRIGR_SWTRIG2 BIT(1) /* Channel 2 software trigger */
+
+/* Channel 1 12-bit right-aligned data holding register */
#define DAC_DHR12R1_DACC1DHR 0x00000FFF
+/* Channel 1 12-bit left-aligned data holding register */
#define DAC_DHR12L1_DACC1DHR 0x0000FFF0
+/* Channel 1 8-bit left-aligned data holding register */
#define DAC_DHR8R1_DACC1DHR 0x000000FF
+/* Channel 2 12-bit right-aligned data holding register */
#define DAC_DHR12R2_DACC2DHR 0x00000FFF
+/* Channel 2 12-bit left-aligned data holding register */
#define DAC_DHR12L2_DACC2DHR 0x0000FFF0
+/* Channel 2 8-bit left-aligned data holding register */
#define DAC_DHR8R2_DACC2DHR 0x000000FF
+/* Dual DAC 12-bit right-aligned data holding register */
#define DAC_DHR12RD_DACC1DHR 0x00000FFF
#define DAC_DHR12RD_DACC2DHR 0x0FFF0000
+/* Dual DAC 12-bit left-aligned data holding register */
#define DAC_DHR12LD_DACC1DHR 0x0000FFF0
#define DAC_DHR12LD_DACC2DHR 0xFFF00000
+/* Dual DAC 8-bit left-aligned data holding register */
#define DAC_DHR8RD_DACC1DHR 0x000000FF
#define DAC_DHR8RD_DACC2DHR 0x0000FF00
-#define DAC_DOR1 0x00000FFF
+/* Channel 1 data output register */
+#define DAC_DOR1_DACC1DOR 0x00000FFF
-#define DAC_DOR2 0x00000FFF
+/* Channel 1 data output register */
+#define DAC_DOR2_DACC2DOR 0x00000FFF
-void dac_init(void);
-void dac_write(uint8 chan, uint16 val);
+/*
+ * Convenience functions
+ */
+
+/* We take the dev argument in these convenience functions for
+ * future-proofing */
+
+#define DAC_CH1 0x1
+#define DAC_CH2 0x2
+void dac_init(const dac_dev *dev, uint32 flags);
+
+void dac_write_channel(const dac_dev *dev, uint8 channel, uint16 val);
+void dac_enable_channel(const dac_dev *dev, uint8 channel);
+void dac_disable_channel(const dac_dev *dev, uint8 channel);
#ifdef __cplusplus
} // extern "C"
#endif
-
#endif
diff --git a/libmaple/delay.h b/libmaple/delay.h
new file mode 100644
index 0000000..5372ac1
--- /dev/null
+++ b/libmaple/delay.h
@@ -0,0 +1,24 @@
+/**
+ * @brief Delay implementation
+ */
+
+#ifndef _DELAY_H_
+#define _DELAY_H_
+
+static inline void delay_us(uint32 us) {
+ /* TODO this makes unwarranted assumptions about the RCC
+ * config; add a hook so users can make their own decisions. */
+ /* So (2^32)/12 micros max, or less than 6 minutes */
+ us *= 12;
+
+ /* fudge for function call overhead */
+ us--;
+ asm volatile(" mov r0, %[us] \n\t"
+ "1: subs r0, #1 \n\t"
+ " bhi 1b \n\t"
+ :
+ : [us] "r" (us)
+ : "r0");
+}
+#endif
+
diff --git a/libmaple/dma.c b/libmaple/dma.c
index 15c96e1..04cdcea 100644
--- a/libmaple/dma.c
+++ b/libmaple/dma.c
@@ -23,126 +23,353 @@
*****************************************************************************/
/**
- * @file dma.c
- *
- * @brief Direct Memory Access peripheral support
+ * @file dma.c
+ * @author Marti Bolivar <mbolivar@leaflabs.com>;
+ * Original implementation by Michael Hope
+ * @brief Direct Memory Access peripheral support
*/
-#include "libmaple.h"
#include "dma.h"
-#include "rcc.h"
-#include "nvic.h"
-
-#define DMA_EN BIT(0)
-
-typedef struct dma_regs {
- uint32 CCR;
- uint32 CNDTR;
- uint32 CPAR;
- uint32 CMAR;
-} dma_regs;
-
-typedef struct DMAChannel {
- void (*handler)(void);
- uint32 irq_line;
-} DMAChannel;
-
-volatile static DMAChannel dma_channels[] = {
- { .handler = NULL, .irq_line = NVIC_DMA_CH1 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH2 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH3 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH4 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH5 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH6 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH7 }
+#include "bitband.h"
+#include "util.h"
+
+/*
+ * Devices
+ */
+
+static dma_dev dma1 = {
+ .regs = DMA1_BASE,
+ .clk_id = RCC_DMA1,
+ .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA_CH1 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH2 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH3 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH4 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH5 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH6 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH7 }}
+};
+dma_dev *DMA1 = &dma1;
+
+#ifdef STM32_HIGH_DENSITY
+static dma_dev dma2 = {
+ .regs = DMA2_BASE,
+ .clk_id = RCC_DMA2,
+ .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA2_CH1 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_CH2 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_CH3 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }} /* !@#$ */
};
+dma_dev *DMA2 = &dma2;
+#endif
-/** Get the base address of the given channel, asserting and returning
- * NULL on illegal
+/*
+ * Convenience routines
*/
-static dma_regs *dma_get_regs(uint8 channel) {
- if (channel >= 1 && channel <= 7) {
- return (dma_regs *)(DMA1_CCR1 + DMA_CHANNEL_STRIDE * (channel-1));
- } else {
- ASSERT(0);
- return NULL;
- }
+
+/**
+ * @brief Initialize a DMA device.
+ * @param dev Device to initialize.
+ */
+void dma_init(dma_dev *dev) {
+ rcc_clk_enable(dev->clk_id);
}
-/* Zero-based indexing */
-static inline void dispatch_handler(uint8 channel_idx) {
- ASSERT(dma_channels[channel_idx].handler);
- if (dma_channels[channel_idx].handler) {
- (dma_channels[channel_idx].handler)();
- }
+/**
+ * @brief Set up a DMA transfer.
+ *
+ * The channel will be disabled before being reconfigured. The
+ * transfer will have low priority by default. You may choose another
+ * priority before the transfer begins using dma_set_priority(), as
+ * well as performing any other configuration you desire. When the
+ * channel is configured to your liking, enable it using dma_enable().
+ *
+ * @param dev DMA device.
+ * @param channel DMA channel.
+ * @param peripheral_address Base address of peripheral data register
+ * involved in the transfer.
+ * @param peripheral_size Peripheral data transfer size.
+ * @param memory_address Base memory address involved in the transfer.
+ * @param memory_size Memory data transfer size.
+ * @param mode Logical OR of dma_mode_flags
+ * @sideeffect Disables the given DMA channel.
+ * @see dma_xfer_size
+ * @see dma_mode_flags
+ * @see dma_set_num_transfers()
+ * @see dma_set_priority()
+ * @see dma_attach_interrupt()
+ * @see dma_enable()
+ */
+void dma_setup_transfer(dma_dev *dev,
+ dma_channel channel,
+ __io void *peripheral_address,
+ dma_xfer_size peripheral_size,
+ __io void *memory_address,
+ dma_xfer_size memory_size,
+ uint32 mode) {
+ dma_channel_reg_map *channel_regs = dma_channel_regs(dev, channel);
+
+ dma_disable(dev, channel); /* can't write to CMAR/CPAR otherwise */
+ channel_regs->CCR = (memory_size << 10) | (peripheral_size << 8) | mode;
+ channel_regs->CMAR = (uint32)memory_address;
+ channel_regs->CPAR = (uint32)peripheral_address;
}
-void DMAChannel1_IRQHandler(void) {
- dispatch_handler(0);
+/**
+ * @brief Set the number of data to be transferred on a DMA channel.
+ *
+ * You may not call this function while the channel is enabled.
+ *
+ * @param dev DMA device
+ * @param channel Channel through which the transfer occurs.
+ * @param num_transfers
+ */
+void dma_set_num_transfers(dma_dev *dev,
+ dma_channel channel,
+ uint16 num_transfers) {
+ dma_channel_reg_map *channel_regs;
+
+ ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
+
+ channel_regs = dma_channel_regs(dev, channel);
+ channel_regs->CNDTR = num_transfers;
+}
+
+/**
+ * @brief Set the priority of a DMA transfer.
+ *
+ * You may not call this function while the channel is enabled.
+ *
+ * @param dev DMA device
+ * @param channel DMA channel
+ * @param priority priority to set.
+ */
+void dma_set_priority(dma_dev *dev,
+ dma_channel channel,
+ dma_priority priority) {
+ dma_channel_reg_map *channel_regs;
+ uint32 ccr;
+
+ ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
+
+ channel_regs = dma_channel_regs(dev, channel);
+ ccr = channel_regs->CCR;
+ ccr &= ~DMA_CCR_PL;
+ ccr |= priority;
+ channel_regs->CCR = ccr;
}
-void DMAChannel2_IRQHandler(void) {
- dispatch_handler(1);
+/**
+ * @brief Attach an interrupt to a DMA transfer.
+ *
+ * Interrupts are enabled using appropriate mode flags in
+ * dma_setup_transfer().
+ *
+ * @param dev DMA device
+ * @param channel Channel to attach handler to
+ * @param handler Interrupt handler to call when channel interrupt fires.
+ * @see dma_setup_transfer()
+ * @see dma_get_irq_cause()
+ * @see dma_detach_interrupt()
+ */
+void dma_attach_interrupt(dma_dev *dev,
+ dma_channel channel,
+ void (*handler)(void)) {
+ dev->handlers[channel - 1].handler = handler;
+ nvic_irq_enable(dev->handlers[channel - 1].irq_line);
}
-void DMAChannel3_IRQHandler(void) {
- dispatch_handler(2);
+/**
+ * @brief Detach a DMA transfer interrupt handler.
+ *
+ * After calling this function, the given channel's interrupts will be
+ * disabled.
+ *
+ * @param dev DMA device
+ * @param channel Channel whose handler to detach
+ * @sideeffect Clears interrupt enable bits in the channel's CCR register.
+ * @see dma_attach_interrupt()
+ */
+void dma_detach_interrupt(dma_dev *dev, dma_channel channel) {
+ /* Don't use nvic_irq_disable()! Think about DMA2 channels 4 and 5. */
+ dma_channel_regs(dev, channel)->CCR &= ~0xF;
+ dev->handlers[channel - 1].handler = NULL;
}
-void DMAChannel4_IRQHandler(void) {
- dispatch_handler(3);
+/**
+ * @brief Discover the reason why a DMA interrupt was called.
+ *
+ * You may only call this function within an attached interrupt
+ * handler for the given channel.
+ *
+ * This function resets the internal DMA register state which encodes
+ * the cause of the interrupt; consequently, it can only be called
+ * once per interrupt handler invocation.
+ *
+ * @brief dev DMA device
+ * @brief channel Channel whose interrupt is being handled.
+ * @return Reason why the interrupt fired.
+ * @sideeffect Clears channel status flags in dev->regs->ISR.
+ * @see dma_attach_interrupt()
+ * @see dma_irq_cause
+ */
+dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel) {
+ uint8 status_bits = dma_get_isr_bits(dev, channel);
+
+ /* If the channel global interrupt flag is cleared, then
+ * something's very wrong. */
+ ASSERT(status_bits & BIT(0));
+
+ dma_clear_isr_bits(dev, channel);
+
+ /* ISR flags get set even if the corresponding interrupt enable
+ * bits in the channel's configuration register are cleared, so we
+ * can't use a switch here.
+ *
+ * Don't change the order of these if statements. */
+ if (status_bits & BIT(3)) {
+ return DMA_TRANSFER_ERROR;
+ } else if (status_bits & BIT(1)) {
+ return DMA_TRANSFER_COMPLETE;
+ } else if (status_bits & BIT(2)) {
+ return DMA_TRANSFER_HALF_COMPLETE;
+ } else if (status_bits & BIT(0)) {
+ /* Shouldn't happen (unless someone messed up an IFCR write). */
+ throb();
+ }
+#if DEBUG_LEVEL < DEBUG_ALL
+ else {
+ /* We shouldn't have been called, but the debug level is too
+ * low for the above ASSERT() to have had any effect. In
+ * order to fail fast, mimic the DMA controller's behavior
+ * when an error occurs. */
+ dma_disable(dev, channel);
+ return DMA_TRANSFER_ERROR;
+ }
+#endif
}
-void DMAChannel5_IRQHandler(void) {
- dispatch_handler(4);
+/**
+ * @brief Enable a DMA channel.
+ * @param dev DMA device
+ * @param channel Channel to enable
+ */
+void dma_enable(dma_dev *dev, dma_channel channel) {
+ dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel);
+ bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 1);
}
-void DMAChannel6_IRQHandler(void) {
- dispatch_handler(5);
+/**
+ * @brief Disable a DMA channel.
+ * @param dev DMA device
+ * @param channel Channel to disable
+ */
+void dma_disable(dma_dev *dev, dma_channel channel) {
+ dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel);
+ bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 0);
}
-void DMAChannel7_IRQHandler(void) {
- dispatch_handler(6);
+/**
+ * @brief Set the base memory address where data will be read from or
+ * written to.
+ *
+ * You must not call this function while the channel is enabled.
+ *
+ * If the DMA memory size is 16 bits, the address is automatically
+ * aligned to a half-word. If the DMA memory size is 32 bits, the
+ * address is aligned to a word.
+ *
+ * @param dev DMA Device
+ * @param channel Channel whose base memory address to set.
+ * @param addr Memory base address to use.
+ */
+void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *addr) {
+ dma_channel_reg_map *chan_regs;
+
+ ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
+
+ chan_regs = dma_channel_regs(dev, channel);
+ chan_regs->CMAR = (uint32)addr;
}
-void dma_init(uint8 channel, volatile void *paddr,
- dma_transfer_size psize, dma_transfer_size msize,
- int mode) {
- volatile dma_regs *regs = dma_get_regs(channel);
+/**
+ * @brief Set the base peripheral address where data will be read from
+ * or written to.
+ *
+ * You must not call this function while the channel is enabled.
+ *
+ * If the DMA peripheral size is 16 bits, the address is automatically
+ * aligned to a half-word. If the DMA peripheral size is 32 bits, the
+ * address is aligned to a word.
+ *
+ * @param dev DMA Device
+ * @param channel Channel whose peripheral data register base address to set.
+ * @param addr Peripheral memory base address to use.
+ */
+void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *addr) {
+ dma_channel_reg_map *chan_regs;
- if (regs != NULL) {
- rcc_clk_enable(RCC_DMA1);
+ ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
- regs->CCR = ((0 << 12) /* Low priority */
- | (msize << 10)
- | (psize << 8)
- | (0 << 0) /* Disabled (until started) */
- | mode);
+ chan_regs = dma_channel_regs(dev, channel);
+ chan_regs->CPAR = (uint32)addr;
+}
+
+/*
+ * IRQ handlers
+ */
- regs->CPAR = (uint32)paddr;
+static inline void dispatch_handler(dma_dev *dev, dma_channel channel) {
+ void (*handler)(void) = dev->handlers[channel - 1].handler;
+ if (handler) {
+ handler();
+ dma_clear_isr_bits(dev, channel); /* in case handler doesn't */
}
}
-void dma_start(uint8 channel, volatile void *buffer, uint16 count) {
- volatile dma_regs *regs = dma_get_regs(channel);
+void __irq_dma_channel1(void) {
+ dispatch_handler(DMA1, DMA_CH1);
+}
- if (regs != NULL) {
- regs->CCR &= ~DMA_EN; /* CMAR may not be written with EN set */
- regs->CMAR = (uint32)buffer;
- regs->CNDTR = count;
+void __irq_dma_channel2(void) {
+ dispatch_handler(DMA1, DMA_CH2);
+}
- regs->CCR |= DMA_EN;
- }
+void __irq_dma_channel3(void) {
+ dispatch_handler(DMA1, DMA_CH3);
+}
+
+void __irq_dma_channel4(void) {
+ dispatch_handler(DMA1, DMA_CH4);
+}
+
+void __irq_dma_channel5(void) {
+ dispatch_handler(DMA1, DMA_CH5);
+}
+
+void __irq_dma_channel6(void) {
+ dispatch_handler(DMA1, DMA_CH6);
+}
+
+void __irq_dma_channel7(void) {
+ dispatch_handler(DMA1, DMA_CH7);
+}
+
+#ifdef STM32_HIGH_DENSITY
+void __irq_dma2_channel1(void) {
+ dispatch_handler(DMA2, DMA_CH1);
+}
+
+void __irq_dma2_channel2(void) {
+ dispatch_handler(DMA2, DMA_CH2);
}
-void dma_attach_interrupt(uint8 channel, voidFuncPtr handler) {
- channel--; /* 1-based -> 0-based indexing */
- dma_channels[channel].handler = handler;
- nvic_irq_enable(dma_channels[channel].irq_line);
+void __irq_dma2_channel3(void) {
+ dispatch_handler(DMA2, DMA_CH3);
}
-void dma_detach_interrupt(uint8 channel) {
- channel--;
- nvic_irq_disable(dma_channels[channel].irq_line);
- dma_channels[channel].handler = NULL;
+void __irq_dma2_channel4_5(void) {
+ dispatch_handler(DMA2, DMA_CH4);
+ dispatch_handler(DMA2, DMA_CH5);
}
+#endif
diff --git a/libmaple/dma.h b/libmaple/dma.h
index 3417f02..7c380d0 100644
--- a/libmaple/dma.h
+++ b/libmaple/dma.h
@@ -23,97 +23,422 @@
*****************************************************************************/
/**
- * @file dma.h
+ * @file dma.h
*
- * @brief Direct Memory Access peripheral support
+ * @author Marti Bolivar <mbolivar@leaflabs.com>;
+ * Original implementation by Michael Hope
*
- * TODO: add DMA2 support for high-density devices.
+ * @brief Direct Memory Access peripheral support
+ */
+
+/*
+ * See /notes/dma.txt for more information.
*/
#ifndef _DMA_H_
#define _DMA_H_
#include "libmaple_types.h"
+#include "rcc.h"
+#include "nvic.h"
#ifdef __cplusplus
extern "C"{
#endif
-/** Base address of the DMA1 peripheral */
-#define DMA1_BASE 0x40020000
-/** DMA Interrupt Status Register */
-#define DMA1_ISR (DMA1_BASE + 0x00)
-/** DMA Interrupt Flag Clear Register */
-#define DMA1_IFCR (DMA1_BASE + 0x04)
-/** DMA Channel Configuration Register */
-#define DMA1_CCR1 (DMA1_BASE + 0x08)
-/** DMA Channel Number of Data Register */
-#define DMA1_CNDTR1 (DMA1_BASE + 0x0C)
-/** DMA Channel Peripheral Address Register */
-#define DMA1_CPAR1 (DMA1_BASE + 0x10)
-/** DMA Channel Memory Address Register */
-#define DMA1_CMAR1 (DMA1_BASE + 0x14)
-/** Spacing between channel registers */
-#define DMA_CHANNEL_STRIDE 20
+/*
+ * Register maps
+ */
+
+/**
+ * @brief DMA register map type.
+ *
+ * Note that DMA controller 2 (register map base pointer DMA2_BASE)
+ * only supports channels 1--5.
+ */
+typedef struct dma_reg_map {
+ __io uint32 ISR; /**< Interrupt status register */
+ __io uint32 IFCR; /**< Interrupt flag clear register */
+ __io uint32 CCR1; /**< Channel 1 configuration register */
+ __io uint32 CNDTR1; /**< Channel 1 number of data register */
+ __io uint32 CPAR1; /**< Channel 1 peripheral address register */
+ __io uint32 CMAR1; /**< Channel 1 memory address register */
+ const uint32 RESERVED1; /**< Reserved. */
+ __io uint32 CCR2; /**< Channel 2 configuration register */
+ __io uint32 CNDTR2; /**< Channel 2 number of data register */
+ __io uint32 CPAR2; /**< Channel 2 peripheral address register */
+ __io uint32 CMAR2; /**< Channel 2 memory address register */
+ const uint32 RESERVED2; /**< Reserved. */
+ __io uint32 CCR3; /**< Channel 3 configuration register */
+ __io uint32 CNDTR3; /**< Channel 3 number of data register */
+ __io uint32 CPAR3; /**< Channel 3 peripheral address register */
+ __io uint32 CMAR3; /**< Channel 3 memory address register */
+ const uint32 RESERVED3; /**< Reserved. */
+ __io uint32 CCR4; /**< Channel 4 configuration register */
+ __io uint32 CNDTR4; /**< Channel 4 number of data register */
+ __io uint32 CPAR4; /**< Channel 4 peripheral address register */
+ __io uint32 CMAR4; /**< Channel 4 memory address register */
+ const uint32 RESERVED4; /**< Reserved. */
+ __io uint32 CCR5; /**< Channel 5 configuration register */
+ __io uint32 CNDTR5; /**< Channel 5 number of data register */
+ __io uint32 CPAR5; /**< Channel 5 peripheral address register */
+ __io uint32 CMAR5; /**< Channel 5 memory address register */
+ const uint32 RESERVED5; /**< Reserved. */
+ __io uint32 CCR6; /**< Channel 6 configuration register */
+ __io uint32 CNDTR6; /**< Channel 6 number of data register */
+ __io uint32 CPAR6; /**< Channel 6 peripheral address register */
+ __io uint32 CMAR6; /**< Channel 6 memory address register */
+ const uint32 RESERVED6; /**< Reserved. */
+ __io uint32 CCR7; /**< Channel 7 configuration register */
+ __io uint32 CNDTR7; /**< Channel 7 number of data register */
+ __io uint32 CPAR7; /**< Channel 7 peripheral address register */
+ __io uint32 CMAR7; /**< Channel 7 memory address register */
+ const uint32 RESERVED7; /**< Reserved. */
+} dma_reg_map;
+
+/** DMA controller 1 register map base pointer */
+#define DMA1_BASE ((struct dma_reg_map*)0x40020000)
+
+#ifdef STM32_HIGH_DENSITY
+/** DMA controller 2 register map base pointer */
+#define DMA2_BASE ((struct dma_reg_map*)0x40020400)
+#endif
+
+/*
+ * Register bit definitions
+ */
+
+/* Interrupt status register */
+
+#define DMA_ISR_TEIF7_BIT 27
+#define DMA_ISR_HTIF7_BIT 26
+#define DMA_ISR_TCIF7_BIT 25
+#define DMA_ISR_GIF7_BIT 24
+#define DMA_ISR_TEIF6_BIT 23
+#define DMA_ISR_HTIF6_BIT 22
+#define DMA_ISR_TCIF6_BIT 21
+#define DMA_ISR_GIF6_BIT 20
+#define DMA_ISR_TEIF5_BIT 19
+#define DMA_ISR_HTIF5_BIT 18
+#define DMA_ISR_TCIF5_BIT 17
+#define DMA_ISR_GIF5_BIT 16
+#define DMA_ISR_TEIF4_BIT 15
+#define DMA_ISR_HTIF4_BIT 14
+#define DMA_ISR_TCIF4_BIT 13
+#define DMA_ISR_GIF4_BIT 12
+#define DMA_ISR_TEIF3_BIT 11
+#define DMA_ISR_HTIF3_BIT 10
+#define DMA_ISR_TCIF3_BIT 9
+#define DMA_ISR_GIF3_BIT 8
+#define DMA_ISR_TEIF2_BIT 7
+#define DMA_ISR_HTIF2_BIT 6
+#define DMA_ISR_TCIF2_BIT 5
+#define DMA_ISR_GIF2_BIT 4
+#define DMA_ISR_TEIF1_BIT 3
+#define DMA_ISR_HTIF1_BIT 2
+#define DMA_ISR_TCIF1_BIT 1
+#define DMA_ISR_GIF1_BIT 0
+
+#define DMA_ISR_TEIF7 BIT(DMA_ISR_TEIF7_BIT)
+#define DMA_ISR_HTIF7 BIT(DMA_ISR_HTIF7_BIT)
+#define DMA_ISR_TCIF7 BIT(DMA_ISR_TCIF7_BIT)
+#define DMA_ISR_GIF7 BIT(DMA_ISR_GIF7_BIT)
+#define DMA_ISR_TEIF6 BIT(DMA_ISR_TEIF6_BIT)
+#define DMA_ISR_HTIF6 BIT(DMA_ISR_HTIF6_BIT)
+#define DMA_ISR_TCIF6 BIT(DMA_ISR_TCIF6_BIT)
+#define DMA_ISR_GIF6 BIT(DMA_ISR_GIF6_BIT)
+#define DMA_ISR_TEIF5 BIT(DMA_ISR_TEIF5_BIT)
+#define DMA_ISR_HTIF5 BIT(DMA_ISR_HTIF5_BIT)
+#define DMA_ISR_TCIF5 BIT(DMA_ISR_TCIF5_BIT)
+#define DMA_ISR_GIF5 BIT(DMA_ISR_GIF5_BIT)
+#define DMA_ISR_TEIF4 BIT(DMA_ISR_TEIF4_BIT)
+#define DMA_ISR_HTIF4 BIT(DMA_ISR_HTIF4_BIT)
+#define DMA_ISR_TCIF4 BIT(DMA_ISR_TCIF4_BIT)
+#define DMA_ISR_GIF4 BIT(DMA_ISR_GIF4_BIT)
+#define DMA_ISR_TEIF3 BIT(DMA_ISR_TEIF3_BIT)
+#define DMA_ISR_HTIF3 BIT(DMA_ISR_HTIF3_BIT)
+#define DMA_ISR_TCIF3 BIT(DMA_ISR_TCIF3_BIT)
+#define DMA_ISR_GIF3 BIT(DMA_ISR_GIF3_BIT)
+#define DMA_ISR_TEIF2 BIT(DMA_ISR_TEIF2_BIT)
+#define DMA_ISR_HTIF2 BIT(DMA_ISR_HTIF2_BIT)
+#define DMA_ISR_TCIF2 BIT(DMA_ISR_TCIF2_BIT)
+#define DMA_ISR_GIF2 BIT(DMA_ISR_GIF2_BIT)
+#define DMA_ISR_TEIF1 BIT(DMA_ISR_TEIF1_BIT)
+#define DMA_ISR_HTIF1 BIT(DMA_ISR_HTIF1_BIT)
+#define DMA_ISR_TCIF1 BIT(DMA_ISR_TCIF1_BIT)
+#define DMA_ISR_GIF1 BIT(DMA_ISR_GIF1_BIT)
+
+/* Interrupt flag clear register */
+
+#define DMA_IFCR_CTEIF7_BIT 27
+#define DMA_IFCR_CHTIF7_BIT 26
+#define DMA_IFCR_CTCIF7_BIT 25
+#define DMA_IFCR_CGIF7_BIT 24
+#define DMA_IFCR_CTEIF6_BIT 23
+#define DMA_IFCR_CHTIF6_BIT 22
+#define DMA_IFCR_CTCIF6_BIT 21
+#define DMA_IFCR_CGIF6_BIT 20
+#define DMA_IFCR_CTEIF5_BIT 19
+#define DMA_IFCR_CHTIF5_BIT 18
+#define DMA_IFCR_CTCIF5_BIT 17
+#define DMA_IFCR_CGIF5_BIT 16
+#define DMA_IFCR_CTEIF4_BIT 15
+#define DMA_IFCR_CHTIF4_BIT 14
+#define DMA_IFCR_CTCIF4_BIT 13
+#define DMA_IFCR_CGIF4_BIT 12
+#define DMA_IFCR_CTEIF3_BIT 11
+#define DMA_IFCR_CHTIF3_BIT 10
+#define DMA_IFCR_CTCIF3_BIT 9
+#define DMA_IFCR_CGIF3_BIT 8
+#define DMA_IFCR_CTEIF2_BIT 7
+#define DMA_IFCR_CHTIF2_BIT 6
+#define DMA_IFCR_CTCIF2_BIT 5
+#define DMA_IFCR_CGIF2_BIT 4
+#define DMA_IFCR_CTEIF1_BIT 3
+#define DMA_IFCR_CHTIF1_BIT 2
+#define DMA_IFCR_CTCIF1_BIT 1
+#define DMA_IFCR_CGIF1_BIT 0
+
+#define DMA_IFCR_CTEIF7 BIT(DMA_IFCR_CTEIF7_BIT)
+#define DMA_IFCR_CHTIF7 BIT(DMA_IFCR_CHTIF7_BIT)
+#define DMA_IFCR_CTCIF7 BIT(DMA_IFCR_CTCIF7_BIT)
+#define DMA_IFCR_CGIF7 BIT(DMA_IFCR_CGIF7_BIT)
+#define DMA_IFCR_CTEIF6 BIT(DMA_IFCR_CTEIF6_BIT)
+#define DMA_IFCR_CHTIF6 BIT(DMA_IFCR_CHTIF6_BIT)
+#define DMA_IFCR_CTCIF6 BIT(DMA_IFCR_CTCIF6_BIT)
+#define DMA_IFCR_CGIF6 BIT(DMA_IFCR_CGIF6_BIT)
+#define DMA_IFCR_CTEIF5 BIT(DMA_IFCR_CTEIF5_BIT)
+#define DMA_IFCR_CHTIF5 BIT(DMA_IFCR_CHTIF5_BIT)
+#define DMA_IFCR_CTCIF5 BIT(DMA_IFCR_CTCIF5_BIT)
+#define DMA_IFCR_CGIF5 BIT(DMA_IFCR_CGIF5_BIT)
+#define DMA_IFCR_CTEIF4 BIT(DMA_IFCR_CTEIF4_BIT)
+#define DMA_IFCR_CHTIF4 BIT(DMA_IFCR_CHTIF4_BIT)
+#define DMA_IFCR_CTCIF4 BIT(DMA_IFCR_CTCIF4_BIT)
+#define DMA_IFCR_CGIF4 BIT(DMA_IFCR_CGIF4_BIT)
+#define DMA_IFCR_CTEIF3 BIT(DMA_IFCR_CTEIF3_BIT)
+#define DMA_IFCR_CHTIF3 BIT(DMA_IFCR_CHTIF3_BIT)
+#define DMA_IFCR_CTCIF3 BIT(DMA_IFCR_CTCIF3_BIT)
+#define DMA_IFCR_CGIF3 BIT(DMA_IFCR_CGIF3_BIT)
+#define DMA_IFCR_CTEIF2 BIT(DMA_IFCR_CTEIF2_BIT)
+#define DMA_IFCR_CHTIF2 BIT(DMA_IFCR_CHTIF2_BIT)
+#define DMA_IFCR_CTCIF2 BIT(DMA_IFCR_CTCIF2_BIT)
+#define DMA_IFCR_CGIF2 BIT(DMA_IFCR_CGIF2_BIT)
+#define DMA_IFCR_CTEIF1 BIT(DMA_IFCR_CTEIF1_BIT)
+#define DMA_IFCR_CHTIF1 BIT(DMA_IFCR_CHTIF1_BIT)
+#define DMA_IFCR_CTCIF1 BIT(DMA_IFCR_CTCIF1_BIT)
+#define DMA_IFCR_CGIF1 BIT(DMA_IFCR_CGIF1_BIT)
+
+/* Channel configuration register */
+
+#define DMA_CCR_MEM2MEM_BIT 14
+#define DMA_CCR_MINC_BIT 7
+#define DMA_CCR_PINC_BIT 6
+#define DMA_CCR_CIRC_BIT 5
+#define DMA_CCR_DIR_BIT 4
+#define DMA_CCR_TEIE_BIT 3
+#define DMA_CCR_HTIE_BIT 2
+#define DMA_CCR_TCIE_BIT 1
+#define DMA_CCR_EN_BIT 0
+
+#define DMA_CCR_MEM2MEM BIT(DMA_CCR_MEM2MEM_BIT)
+#define DMA_CCR_PL (0x3 << 12)
+#define DMA_CCR_PL_LOW (0x0 << 12)
+#define DMA_CCR_PL_MEDIUM (0x1 << 12)
+#define DMA_CCR_PL_HIGH (0x2 << 12)
+#define DMA_CCR_PL_VERY_HIGH (0x3 << 12)
+#define DMA_CCR_MSIZE (0x3 << 10)
+#define DMA_CCR_MSIZE_8BITS (0x0 << 10)
+#define DMA_CCR_MSIZE_16BITS (0x1 << 10)
+#define DMA_CCR_MSIZE_32BITS (0x2 << 10)
+#define DMA_CCR_PSIZE (0x3 << 8)
+#define DMA_CCR_PSIZE_8BITS (0x0 << 8)
+#define DMA_CCR_PSIZE_16BITS (0x1 << 8)
+#define DMA_CCR_PSIZE_32BITS (0x2 << 8)
+#define DMA_CCR_MINC BIT(DMA_CCR_MINC_BIT)
+#define DMA_CCR_PINC BIT(DMA_CCR_PINC_BIT)
+#define DMA_CCR_CIRC BIT(DMA_CCR_CIRC_BIT)
+#define DMA_CCR_DIR BIT(DMA_CCR_DIR_BIT)
+#define DMA_CCR_TEIE BIT(DMA_CCR_TEIE_BIT)
+#define DMA_CCR_HTIE BIT(DMA_CCR_HTIE_BIT)
+#define DMA_CCR_TCIE BIT(DMA_CCR_TCIE_BIT)
+#define DMA_CCR_EN BIT(DMA_CCR_EN_BIT)
+
+/*
+ * Devices
+ */
+
+/** Encapsulates state related to a DMA channel interrupt. */
+typedef struct dma_handler_config {
+ void (*handler)(void);
+ nvic_irq_num irq_line;
+} dma_handler_config;
+
+/** DMA device type */
+typedef struct dma_dev {
+ dma_reg_map *regs; /**< Register map */
+ rcc_clk_id clk_id; /**< Clock ID */
+ dma_handler_config handlers[]; /**< IRQ handlers and NVIC numbers. */
+} dma_dev;
+
+/** DMA1 device */
+extern dma_dev *DMA1;
+#ifdef STM32_HIGH_DENSITY
+/** DMA2 device */
+extern dma_dev *DMA2;
+#endif
+
+/*
+ * Convenience functions
+ */
+
+void dma_init(dma_dev *dev);
/** Flags for DMA transfer configuration. */
typedef enum dma_mode_flags {
- DMA_MINC_MODE = 1 << 7, /**< Auto-increment memory address */
- DMA_PINC_MODE = 1 << 6, /**< Auto-increment peripheral address */
- DMA_CIRC_MODE = 1 << 5, /**< Circular mode */
- DMA_FROM_MEM = 1 << 4, /**< Read from memory to peripheral */
- DMA_TRNS_ERR = 1 << 3, /**< Interrupt on transfer error */
- DMA_HALF_TRNS = 1 << 2, /**< Interrupt on half-transfer */
- DMA_TRNS_CMPLT = 1 << 1 /**< Interrupt on transfer completion */
+ DMA_MEM_2_MEM = 1 << 14, /**< Memory to memory mode */
+ DMA_MINC_MODE = 1 << 7, /**< Auto-increment memory address */
+ DMA_PINC_MODE = 1 << 6, /**< Auto-increment peripheral address */
+ DMA_CIRC_MODE = 1 << 5, /**< Circular mode */
+ DMA_FROM_MEM = 1 << 4, /**< Read from memory to peripheral */
+ DMA_TRNS_ERR = 1 << 3, /**< Interrupt on transfer error */
+ DMA_HALF_TRNS = 1 << 2, /**< Interrupt on half-transfer */
+ DMA_TRNS_CMPLT = 1 << 1 /**< Interrupt on transfer completion */
} dma_mode_flags;
/** Source and destination transfer sizes. */
-typedef enum dma_transfer_size {
- DMA_SIZE_8BITS = 0,
- DMA_SIZE_16BITS = 1,
- DMA_SIZE_32BITS = 2
-} dma_transfer_size;
+typedef enum dma_xfer_size {
+ DMA_SIZE_8BITS = 0, /**< 8-bit transfers */
+ DMA_SIZE_16BITS = 1, /**< 16-bit transfers */
+ DMA_SIZE_32BITS = 2 /**< 32-bit transfers */
+} dma_xfer_size;
+
+/** DMA channel */
+typedef enum dma_channel {
+ DMA_CH1 = 1, /**< Channel 1 */
+ DMA_CH2 = 2, /**< Channel 2 */
+ DMA_CH3 = 3, /**< Channel 3 */
+ DMA_CH4 = 4, /**< Channel 4 */
+ DMA_CH5 = 5, /**< Channel 5 */
+ DMA_CH6 = 6, /**< Channel 6 */
+ DMA_CH7 = 7, /**< Channel 7 */
+} dma_channel;
+
+void dma_setup_transfer(dma_dev *dev,
+ dma_channel channel,
+ __io void *peripheral_address,
+ dma_xfer_size peripheral_size,
+ __io void *memory_address,
+ dma_xfer_size memory_size,
+ uint32 mode);
+
+void dma_set_num_transfers(dma_dev *dev,
+ dma_channel channel,
+ uint16 num_transfers);
+
+/** DMA transfer priority. */
+typedef enum dma_priority {
+ DMA_PRIORITY_LOW = DMA_CCR_PL_LOW, /**< Low priority */
+ DMA_PRIORITY_MEDIUM = DMA_CCR_PL_MEDIUM, /**< Medium priority */
+ DMA_PRIORITY_HIGH = DMA_CCR_PL_HIGH, /**< High priority */
+ DMA_PRIORITY_VERY_HIGH = DMA_CCR_PL_VERY_HIGH /**< Very high priority */
+} dma_priority;
+
+void dma_set_priority(dma_dev *dev,
+ dma_channel channel,
+ dma_priority priority);
+
+void dma_attach_interrupt(dma_dev *dev,
+ dma_channel channel,
+ void (*handler)(void));
+void dma_detach_interrupt(dma_dev *dev, dma_channel channel);
+
+/**
+ * Encodes the reason why a DMA interrupt was called.
+ * @see dma_get_irq_cause()
+ */
+typedef enum dma_irq_cause {
+ DMA_TRANSFER_COMPLETE, /**< Transfer is complete. */
+ DMA_TRANSFER_HALF_COMPLETE, /**< Transfer is half complete. */
+ DMA_TRANSFER_ERROR, /**< Error occurred during transfer. */
+} dma_irq_cause;
+
+dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel);
+
+void dma_enable(dma_dev *dev, dma_channel channel);
+void dma_disable(dma_dev *dev, dma_channel channel);
+
+void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *address);
+void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *address);
/**
- * Initialize a DMA channel. If desired, attach an interrupt handler
- * using dma_attach_interrupt(). Start the transfer using
- * dma_start().
+ * @brief DMA channel register map type.
*
- * @param channel the channel number (1..7)
- * @param paddr address of the peripheral
- * @param psize peripheral size
- * @param msize memory size
- * @param mode OR of the dma_mode_flags
- * @see dma_mode_flags
- * @see dma_attach_interrupt()
- * @see dma_start() */
-void dma_init(uint8 channel, volatile void *paddr,
- dma_transfer_size psize, dma_transfer_size msize,
- int mode);
+ * Provides access to an individual channel's registers.
+ */
+typedef struct dma_channel_reg_map {
+ __io uint32 CCR; /**< Channel configuration register */
+ __io uint32 CNDTR; /**< Channel number of data register */
+ __io uint32 CPAR; /**< Channel peripheral address register */
+ __io uint32 CMAR; /**< Channel memory address register */
+} dma_channel_reg_map;
+
+#define DMA_CHANNEL_NREGS 5
/**
- * Begin a DMA transfer initialized with dma_init().
+ * @brief Obtain a pointer to an individual DMA channel's registers.
*
- * @param channel Channel transfer to start.
- * @param buffer Buffer to write to (unless DMA_FROM_MEM was set in
- * mode argument to dma_init(), in which case, buffer
- * to read from). This must be aligned with msize
- * argument to dma_init().
- * @param count Number of elements to transfer.
- * @see dma_init() */
-void dma_start(uint8 channel, volatile void *buffer, uint16 count);
+ * For example, dma_channel_regs(DMA1, DMA_CH1)->CCR is DMA1_BASE->CCR1.
+ *
+ * @param dev DMA device
+ * @param channel DMA channel whose channel register map to obtain.
+ */
+static inline dma_channel_reg_map* dma_channel_regs(dma_dev *dev,
+ dma_channel channel) {
+ __io uint32 *ccr1 = &dev->regs->CCR1;
+ return (dma_channel_reg_map*)(ccr1 + DMA_CHANNEL_NREGS * (channel - 1));
+}
+
+/**
+ * @brief Check if a DMA channel is enabled
+ * @param dev DMA device
+ * @param channel Channel whose enabled bit to check.
+ */
+static inline uint8 dma_is_channel_enabled(dma_dev *dev, dma_channel channel) {
+ return (uint8)(dma_channel_regs(dev, channel)->CCR & DMA_CCR_EN);
+}
/**
- * Attach an interrupt handler for the given DMA channel.
- * @param channel DMA channel (1..7)
- * @param handler Interrupt handler to attach
- * @see voidFuncPtr */
-void dma_attach_interrupt(uint8 channel, voidFuncPtr handler);
+ * @brief Get the ISR status bits for a DMA channel.
+ *
+ * The bits are returned right-aligned, in the following order:
+ * transfer error flag, half-transfer flag, transfer complete flag,
+ * global interrupt flag.
+ *
+ * If you're attempting to figure out why a DMA interrupt fired; you
+ * may find dma_get_irq_cause() more convenient.
+ *
+ * @param dev DMA device
+ * @param channel Channel whose ISR bits to return.
+ * @see dma_get_irq_cause().
+ */
+static inline uint8 dma_get_isr_bits(dma_dev *dev, dma_channel channel) {
+ uint8 shift = (channel - 1) * 4;
+ return (dev->regs->ISR >> shift) & 0xF;
+}
/**
- * Detach any handler associated with the given DMA channel.
- * @param channel Channel whose interrupt handler to detach. */
-void dma_detach_interrupt(uint8 channel);
+ * @brief Clear the ISR status bits for a given DMA channel.
+ *
+ * If you're attempting to clean up after yourself in a DMA interrupt,
+ * you may find dma_get_irq_cause() more convenient.
+ *
+ * @param dev DMA device
+ * @param channel Channel whose ISR bits to clear.
+ * @see dma_get_irq_cause()
+ */
+static inline void dma_clear_isr_bits(dma_dev *dev, dma_channel channel) {
+ dev->regs->IFCR = BIT(4 * (channel - 1));
+}
#ifdef __cplusplus
} // extern "C"
diff --git a/libmaple/exc.S b/libmaple/exc.S
index a9f6561..7631e48 100644
--- a/libmaple/exc.S
+++ b/libmaple/exc.S
@@ -41,21 +41,61 @@
# SP--> r0
.text
-.globl HardFaultException
+.globl __exc_hardfault
+.globl __exc_nmi
+.globl __exc_hardfault
+.globl __exc_memmanage
+.globl __exc_busfault
+.globl __exc_usagefault
+
+.code 16
+.thumb_func
+__exc_nmi:
+ mov r0, #1
+ b __default_exc
+
+.thumb_func
+__exc_hardfault:
+ mov r0, #2
+ b __default_exc
+
+.thumb_func
+__exc_memmanage:
+ mov r0, #3
+ b __default_exc
+
+.thumb_func
+__exc_busfault:
+ mov r0, #4
+ b __default_exc
+
+.thumb_func
+__exc_usagefault:
+ mov r0, #5
+ b __default_exc
+
.thumb_func
-HardFaultException:
- ldr r0, CPSR_MASK @ Set default CPSR
- push {r0}
- ldr r0, TARGET_PC @ Set target pc
- push {r0}
- sub sp, sp, #24 @ its not like i even care
- ldr r0, EXC_RETURN @ Return to thread mode
- mov lr, r0
- bx lr @ Exception exit
-
- .align 4
- CPSR_MASK: .word 0x61000000
- EXC_RETURN: .word 0xFFFFFFF9
- TARGET_PC: .word throb
+__default_exc:
+ ldr r2, NVIC_CCR @ Enable returning to thread mode even if there are
+ mov r1 ,#1 @ pending exceptions. See flag NONEBASETHRDENA.
+ str r1, [r2]
+ cpsid i @ Disable global interrupts
+ ldr r2, SYSTICK_CSR @ Disable systick handler
+ mov r1, #0
+ str r1, [r2]
+ ldr r1, CPSR_MASK @ Set default CPSR
+ push {r1}
+ ldr r1, TARGET_PC @ Set target pc
+ push {r1}
+ sub sp, sp, #24 @ Don't care
+ ldr r1, EXC_RETURN @ Return to thread mode
+ mov lr, r1
+ bx lr @ Exception exit
+.align 4
+CPSR_MASK: .word 0x61000000
+EXC_RETURN: .word 0xFFFFFFF9
+TARGET_PC: .word __error
+NVIC_CCR: .word 0xE000ED14 @ NVIC configuration control register
+SYSTICK_CSR: .word 0xE000E010 @ Systick control register
diff --git a/libmaple/exti.c b/libmaple/exti.c
index 150dd05..38d3bba 100644
--- a/libmaple/exti.c
+++ b/libmaple/exti.c
@@ -23,184 +23,228 @@
*****************************************************************************/
/**
+ * @file exti.c
* @brief External interrupt control routines
*/
#include "libmaple.h"
#include "exti.h"
#include "nvic.h"
+#include "bitband.h"
-typedef struct ExtIChannel {
+/*
+ * Internal state
+ */
+
+/* Status bitmaps, for external interrupts with multiplexed IRQs */
+static uint16 exti_9_5_en = 0;
+static uint16 exti_15_10_en = 0;
+
+typedef struct exti_channel {
void (*handler)(void);
uint32 irq_line;
-} ExtIChannel;
-
-static ExtIChannel exti_channels[] = {
- { .handler = NULL, .irq_line = NVIC_EXTI0 }, // EXTI0
- { .handler = NULL, .irq_line = NVIC_EXTI1 }, // EXTI1
- { .handler = NULL, .irq_line = NVIC_EXTI2 }, // EXTI2
- { .handler = NULL, .irq_line = NVIC_EXTI3 }, // EXTI3
- { .handler = NULL, .irq_line = NVIC_EXTI4 }, // EXTI4
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI5
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI6
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI7
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI8
- { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI9
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI10
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI11
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI12
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI13
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI14
- { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI15
+} exti_channel;
+
+static exti_channel exti_channels[] = {
+ { .handler = NULL, .irq_line = NVIC_EXTI0 }, // EXTI0
+ { .handler = NULL, .irq_line = NVIC_EXTI1 }, // EXTI1
+ { .handler = NULL, .irq_line = NVIC_EXTI2 }, // EXTI2
+ { .handler = NULL, .irq_line = NVIC_EXTI3 }, // EXTI3
+ { .handler = NULL, .irq_line = NVIC_EXTI4 }, // EXTI4
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI5
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI6
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI7
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI8
+ { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI9
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI10
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI11
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI12
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI13
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI14
+ { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI15
};
-static inline void clear_pending(int bit) {
- __set_bits(EXTI_PR, BIT(bit));
- /* If the pending bit is cleared as the last instruction in an ISR,
- * it won't actually be cleared in time and the ISR will fire again.
- * Insert a 2-cycle buffer to allow it to take effect. */
- asm volatile("nop");
- asm volatile("nop");
-}
+/*
+ * Convenience routines
+ */
+
+static inline void enable_irq(afio_exti_num exti_num);
+static inline void maybe_disable_irq(afio_exti_num exti_num);
+
+/**
+ * @brief Register a handler to run upon external interrupt.
+ *
+ * This function assumes that the interrupt request corresponding to
+ * the given external interrupt is masked.
+ *
+ * @param num External interrupt line number.
+ * @param port Port to use as source input for external interrupt.
+ * @param handler Function handler to execute when interrupt is triggered.
+ * @param mode Type of transition to trigger on, one of:
+ * EXTI_RISING, EXTI_FALLING, EXTI_RISING_FALLING.
+ * @see exti_num
+ * @see exti_port
+ * @see exti_trigger_mode
+ */
+void exti_attach_interrupt(afio_exti_num num,
+ afio_exti_port port,
+ voidFuncPtr handler,
+ exti_trigger_mode mode) {
+ ASSERT(handler);
-static inline void dispatch_handler(uint32 channel) {
- ASSERT(exti_channels[channel].handler);
- if (exti_channels[channel].handler) {
- (exti_channels[channel].handler)();
+ /* Register the handler */
+ exti_channels[num].handler = handler;
+
+ /* Set trigger mode */
+ switch (mode) {
+ case EXTI_RISING:
+ *bb_perip(&EXTI_BASE->RTSR, num) = 1;
+ break;
+ case EXTI_FALLING:
+ *bb_perip(&EXTI_BASE->FTSR, num) = 1;
+ break;
+ case EXTI_RISING_FALLING:
+ *bb_perip(&EXTI_BASE->RTSR, num) = 1;
+ *bb_perip(&EXTI_BASE->FTSR, num) = 1;
+ break;
}
+
+ /* Map num to port */
+ afio_exti_select(num, port);
+
+ /* Unmask external interrupt request */
+ *bb_perip(&EXTI_BASE->IMR, num) = 1;
+
+ /* Enable the interrupt line */
+ enable_irq(num);
+}
+
+/**
+ * @brief Unregister an external interrupt handler
+ * @param num Number of the external interrupt line to disable.
+ * @see exti_num
+ */
+void exti_detach_interrupt(afio_exti_num num) {
+ /* First, mask the interrupt request */
+ *bb_perip(&EXTI_BASE->IMR, num) = 0;
+
+ /* Then, clear the trigger selection registers */
+ *bb_perip(&EXTI_BASE->FTSR, num) = 0;
+ *bb_perip(&EXTI_BASE->RTSR, num) = 0;
+
+ /* Next, disable the IRQ, unless it's multiplexed and there are
+ * other active external interrupts on the same IRQ line */
+ maybe_disable_irq(num);
+
+ /* Finally, unregister the user's handler */
+ exti_channels[num].handler = NULL;
}
-/* For EXTI0 through EXTI4, only one handler
- * is associated with each channel, so we
- * don't have to keep track of which channel
+/*
+ * Interrupt handlers
+ */
+
+static inline void clear_pending(uint32 exti_num);
+static inline void dispatch_handler(uint32 exti_num);
+
+/* For AFIO_EXTI_0 through AFIO_EXTI_4, only one handler is associated
+ * with each channel, so we don't have to keep track of which channel
* we came from */
-void EXTI0_IRQHandler(void) {
- dispatch_handler(EXTI0);
- clear_pending(EXTI0);
+void __irq_exti0(void) {
+ dispatch_handler(AFIO_EXTI_0);
+ clear_pending(AFIO_EXTI_0);
}
-void EXTI1_IRQHandler(void) {
- dispatch_handler(EXTI1);
- clear_pending(EXTI1);
+void __irq_exti1(void) {
+ dispatch_handler(AFIO_EXTI_1);
+ clear_pending(AFIO_EXTI_1);
}
-void EXTI2_IRQHandler(void) {
- dispatch_handler(EXTI2);
- clear_pending(EXTI2);
+void __irq_exti2(void) {
+ dispatch_handler(AFIO_EXTI_2);
+ clear_pending(AFIO_EXTI_2);
}
-void EXTI3_IRQHandler(void) {
- dispatch_handler(EXTI3);
- clear_pending(EXTI3);
+void __irq_exti3(void) {
+ dispatch_handler(AFIO_EXTI_3);
+ clear_pending(AFIO_EXTI_3);
}
-void EXTI4_IRQHandler(void) {
- dispatch_handler(EXTI4);
- clear_pending(EXTI4);
+void __irq_exti4(void) {
+ dispatch_handler(AFIO_EXTI_4);
+ clear_pending(AFIO_EXTI_4);
}
-void EXTI9_5_IRQHandler(void) {
+void __irq_exti9_5(void) {
/* Figure out which channel it came from */
- uint32 pending;
+ uint32 pending = GET_BITS(EXTI_BASE->PR, 5, 9);
uint32 i;
- pending = REG_GET(EXTI_PR);
- pending = GET_BITS(pending, 5, 9);
/* Dispatch every handler if the pending bit is set */
for (i = 0; i < 5; i++) {
if (pending & 0x1) {
- dispatch_handler(EXTI5 + i);
- clear_pending(EXTI5 + i);
+ dispatch_handler(AFIO_EXTI_5 + i);
+ clear_pending(AFIO_EXTI_5 + i);
}
pending >>= 1;
}
}
-void EXTI15_10_IRQHandler(void) {
+void __irq_exti15_10(void) {
/* Figure out which channel it came from */
- uint32 pending;
+ uint32 pending = GET_BITS(EXTI_BASE->PR, 10, 15);
uint32 i;
- pending = REG_GET(EXTI_PR);
- pending = GET_BITS(pending, 10, 15);
/* Dispatch every handler if the pending bit is set */
for (i = 0; i < 6; i++) {
if (pending & 0x1) {
- dispatch_handler(EXTI10 + i);
- clear_pending(EXTI10 + i);
+ dispatch_handler(AFIO_EXTI_10 + i);
+ clear_pending(AFIO_EXTI_10 + i);
}
pending >>= 1;
}
}
-
-/**
- * @brief Register a handler to run upon external interrupt
- * @param port source port of pin (eg EXTI_CONFIG_PORTA)
- * @param pin pin number on the source port
- * @param handler function handler to execute
- * @param mode type of transition to trigger on
+/*
+ * Auxiliary functions
*/
-void exti_attach_interrupt(uint32 port,
- uint32 pin,
- voidFuncPtr handler,
- uint32 mode) {
- static uint32 afio_regs[] = {
- AFIO_EXTICR1, // EXT0-3
- AFIO_EXTICR2, // EXT4-7
- AFIO_EXTICR3, // EXT8-11
- AFIO_EXTICR4, // EXT12-15
- };
-
- /* Note: All of the following code assumes that EXTI0 = 0 */
- ASSERT(EXTI0 == 0);
- ASSERT(handler);
-
- uint32 channel = pin;
- /* map port to channel */
- __write(afio_regs[pin/4], (port << ((pin % 4) * 4)));
-
- /* Unmask appropriate interrupt line */
- __set_bits(EXTI_IMR, BIT(channel));
-
- /* Set trigger mode */
- switch (mode) {
- case EXTI_RISING:
- __set_bits(EXTI_RTSR, BIT(channel));
- break;
-
- case EXTI_FALLING:
- __set_bits(EXTI_FTSR, BIT(channel));
- break;
+static inline void clear_pending(uint32 exti_num) {
+ *bb_perip(&EXTI_BASE->PR, exti_num) = 1;
+ /* If the pending bit is cleared as the last instruction in an ISR,
+ * it won't actually be cleared in time and the ISR will fire again.
+ * Insert a 2-cycle buffer to allow it to take effect. */
+ asm volatile("nop");
+ asm volatile("nop");
+}
- case EXTI_RISING_FALLING:
- __set_bits(EXTI_RTSR, BIT(channel));
- __set_bits(EXTI_FTSR, BIT(channel));
- break;
+static inline void dispatch_handler(uint32 exti_num) {
+ ASSERT(exti_channels[exti_num].handler);
+ if (exti_channels[exti_num].handler) {
+ (exti_channels[exti_num].handler)();
}
-
- /* Register the handler */
- exti_channels[channel].handler = handler;
-
- /* Configure the enable interrupt bits for the NVIC */
- nvic_irq_enable(exti_channels[channel].irq_line);
}
+static inline void enable_irq(afio_exti_num exti) {
+ /* Maybe twiddle the IRQ bitmap for extis with multiplexed IRQs */
+ if (exti > 4) {
+ uint16 *bitmap = exti < 10 ? &exti_9_5_en : &exti_15_10_en;
+ *bb_sramp(bitmap, exti) = 1;
+ }
-/**
- * @brief Unregister an external interrupt handler
- * @param channel channel to disable (eg EXTI0)
- */
-void exti_detach_interrupt(uint32 channel) {
- ASSERT(channel < NR_EXTI_CHANNELS);
- ASSERT(EXTI0 == 0);
-
- __clear_bits(EXTI_IMR, BIT(channel));
- __clear_bits(EXTI_FTSR, BIT(channel));
- __clear_bits(EXTI_RTSR, BIT(channel));
-
- nvic_irq_disable(exti_channels[channel].irq_line);
+ nvic_irq_enable(exti_channels[exti].irq_line);
+}
- exti_channels[channel].handler = NULL;
+static inline void maybe_disable_irq(afio_exti_num exti) {
+ if (exti > 4) {
+ uint16 *bitmap = exti < 10 ? &exti_9_5_en : &exti_15_10_en;
+ *bb_sramp(bitmap, exti) = 0;
+ if (*bitmap == 0) {
+ /* All of the external interrupts which share this IRQ
+ * line are disabled. */
+ nvic_irq_disable(exti_channels[exti].irq_line);
+ }
+ } else {
+ nvic_irq_disable(exti_channels[exti].irq_line);
+ }
}
diff --git a/libmaple/exti.h b/libmaple/exti.h
index 806578f..e145033 100644
--- a/libmaple/exti.h
+++ b/libmaple/exti.h
@@ -22,145 +22,51 @@
* THE SOFTWARE.
*****************************************************************************/
-
/**
- * @file exti.h
- *
- * @brief External interrupt control prototypes and defines
+ * @file exti.h
+ * @brief External interrupt control prototypes and defines
*/
-#ifndef _EXTI_H_
-#define _EXTI_H_
-
-/* Notes:
- *
- * To generate the interrupt, the interrupt line should be configured
- * and enabled. This is done by programming the two trigger registers
- * with the desired edge detection and by enabling the interrupt
- * request by writing a '1' to the corresponding bit in the interrupt
- * mask register. When the selected edge occurs on the external
- * interrupt line, an interrupt request is generated. The pending bit
- * corresponding to the interrupt line is also set. This request is
- * reset by writing a '1' in the pending register.
- *
- * Hardware interrupt selection:
- *
- * To configure the 20 lines as interrupt sources, use the following
- * procedure:
- *
- * 1) Configure AFIO_EXTIICR[y] to select the source input for EXTIx
- * external interrupt
- * 2) Configure the mask bits of the 20 interrupt lines (EXTI_IMR)
- * 3) Configure the trigger selection bits of the interrupt lines
- * (EXTI_RTSR and EXTI_FTSR)
- * 4) Configure the enable and mask bits that control the NVIC_IRQ
- * channel mapped to the External
- *
- * Interrupt Controller (EXTI) so that an inerrupt coming from one of
- * the 20 lines can be correctly acknowledged.
- *
- * AFIO clock must be on.
- *
- * RM0008, page 107: "PD0, PD1 cannot be used for external
- * interrupt/event generation on 36, 48, 64-bin packages."
- *
- * ----------------------------------------------------------------------------
- * Pin to EXTI Line Mappings:
- * EXTI0 EXTI1 EXTI2 EXTI3 EXTI4
- * --------------------------------------------------------------------------
- * D2/PA0 D3/PA1 D1/PA2 D0/A6/PA3 D10/A10/PA4
- * D26/EXT7/PB0 D27/EXT8/PB1 D16/A2/PC2 D17/A3/PC3 D18/A4/PC4
- * D14/A0/PC0 D15/PC1 D25/EXT5/PD2
- *
- * EXTI5 EXTI6 EXTI7 EXTI8 EXTI9
- * ----------------------------------------------------------------------------
- * D13/A13/PA5 D12/A12/PA6 D11/A11/PA7 D6/PA8 D7/PA9
- * D4/PB5 D5/PB6 D9/PB7 D38/PB8 D23/EXT4/PB9
- * D19/A5/PC5 D34/EXTI15/PC6 D35/EXT16/PC7 D36/PC8 D37/EXT18/PC9
- *
- * EXTI10 EXTI11 EXTI12 EXTI13 EXTI14
- * ----------------------------------------------------------------------------
- * D8/PA10 D29/EXT10/PB11 D30/EXTI1/PB12 D31/EXTI12/PB13 D32/EXT13/PB14
- * D28/PB10 D20/EXTI1/PC13 D21/EXT2/PC14
- * D25/PC10
- *
- * EXTI15
- * ----------------------------------------------------------------------------
- * D33/EXTI14/PB15
- * D22/EXT3/PC15
- *
- *
- * The 16 EXTI interrupts are mapped to 7 interrupt handlers.
- *
- * EXTI Lines to Interrupt Mapping:
- * EXTI0 -> EXTI0
- * EXTI1 -> EXTI1
- * EXTI2 -> EXTI2
- * EXTI3 -> EXTI3
- * EXTI4 -> EXTI4
- * EXTI[5-9] -> EXT9_5
- * EXTI[10-15] -> EXT15_10
- *
- * */
-
-#define NR_EXTI_MODES 3
-#define NR_EXTI_CHANNELS 16
-#define NR_EXTI_PORTS NR_GPIO_PORTS // board specific
+/* See notes/exti.txt for more info */
-#define EXTI_RISING 0
-#define EXTI_FALLING 1
-#define EXTI_RISING_FALLING 2
+#include "libmaple.h"
+#include "gpio.h"
-#define EXTI_IMR 0x40010400 // Interrupt mask register
-#define EXTI_EMR (EXTI_IMR + 0x04) // Event mask register
-#define EXTI_RTSR (EXTI_IMR + 0x08) // Rising trigger selection register
-#define EXTI_FTSR (EXTI_IMR + 0x0C) // Falling trigger selection register
-#define EXTI_SWIER (EXTI_IMR + 0x10) // Software interrupt event register
-#define EXTI_PR (EXTI_IMR + 0x14) // Pending register
-
-#define AFIO_EVCR 0x40010000
-#define AFIO_EXTICR1 (AFIO_EVCR + 0x08)
-#define AFIO_EXTICR2 (AFIO_EVCR + 0x0C)
-#define AFIO_EXTICR3 (AFIO_EVCR + 0x10)
-#define AFIO_EXTICR4 (AFIO_EVCR + 0x14)
-
-#define EXTI0 0
-#define EXTI1 1
-#define EXTI2 2
-#define EXTI3 3
-#define EXTI4 4
-#define EXTI5 5
-#define EXTI6 6
-#define EXTI7 7
-#define EXTI8 8
-#define EXTI9 9
-#define EXTI10 10
-#define EXTI11 11
-#define EXTI12 12
-#define EXTI13 13
-#define EXTI14 14
-#define EXTI15 15
-
-#define EXTI_CONFIG_PORTA 0 // Maple, Maple Native, Maple Mini
-#define EXTI_CONFIG_PORTB 1 // Maple, Maple Native, Maple Mini
-#define EXTI_CONFIG_PORTC 2 // Maple, Maple Native, Maple Mini
-#define EXTI_CONFIG_PORTD 3 // Maple and Maple Native only
-#define EXTI_CONFIG_PORTE 4 // Native only
-#define EXTI_CONFIG_PORTF 5 // Native only
-#define EXTI_CONFIG_PORTG 6 // Native only
+#ifndef _EXTI_H_
+#define _EXTI_H_
#ifdef __cplusplus
extern "C"{
#endif
-void exti_attach_interrupt(uint32 port, uint32 pin, voidFuncPtr handler,
- uint32 mode);
-void exti_detach_interrupt(uint32 channel);
+/** EXTI register map type */
+typedef struct exti_reg_map {
+ __io uint32 IMR; /**< Interrupt mask register */
+ __io uint32 EMR; /**< Event mask register */
+ __io uint32 RTSR; /**< Rising trigger selection register */
+ __io uint32 FTSR; /**< Falling trigger selection register */
+ __io uint32 SWIER; /**< Software interrupt event register */
+ __io uint32 PR; /**< Pending register */
+} exti_reg_map;
+
+/** EXTI register map base pointer */
+#define EXTI_BASE ((exti_reg_map*)0x40010400)
+
+/** External interrupt trigger mode */
+typedef enum exti_trigger_mode {
+ EXTI_RISING, /**< Trigger on the rising edge */
+ EXTI_FALLING, /**< Trigger on the falling edge */
+ EXTI_RISING_FALLING /**< Trigger on both the rising and falling edges */
+} exti_trigger_mode;
+
+void exti_attach_interrupt(afio_exti_num num,
+ afio_exti_port port,
+ voidFuncPtr handler,
+ exti_trigger_mode mode);
+void exti_detach_interrupt(afio_exti_num num);
#ifdef __cplusplus
} // extern "C"
#endif
-
#endif
-
diff --git a/libmaple/flash.c b/libmaple/flash.c
index 1d7bfa6..c921256 100644
--- a/libmaple/flash.c
+++ b/libmaple/flash.c
@@ -28,41 +28,30 @@
#include "libmaple.h"
#include "flash.h"
-
-/* flash registers */
-#define FLASH_BASE 0x40022000
-#define FLASH_ACR FLASH_BASE
-
-/* flash prefetcher */
-#define ACR_PRFTBE BIT(4)
-#define ACR_PRFTBE_ENABLE BIT(4)
-
-/* flash wait states */
-#define ACR_LATENCY (0x7)
-
-#define FLASH_WRITE_ACR(val) __write(FLASH_ACR, val)
-#define FLASH_READ_ACR() __read(FLASH_ACR)
+#include "bitband.h"
/**
- * @brief turn on the hardware prefetcher
+ * @brief Turn on the hardware prefetcher.
*/
void flash_enable_prefetch(void) {
- uint32 val = FLASH_READ_ACR();
-
- val |= ACR_PRFTBE_ENABLE;
-
- FLASH_WRITE_ACR(val);
+ *bb_perip(&FLASH_BASE->ACR, FLASH_ACR_PRFTBE_BIT) = 1;
}
/**
- * @brief set flash wait states
- * @param number of wait states
+ * @brief Set flash wait states
+ *
+ * See ST PM0042, section 3.1 for restrictions on the acceptable value
+ * of wait_states for a given SYSCLK configuration.
+ *
+ * @param wait_states number of wait states (one of
+ * FLASH_WAIT_STATE_0, FLASH_WAIT_STATE_1,
+ * FLASH_WAIT_STATE_2).
*/
void flash_set_latency(uint32 wait_states) {
- uint32 val = FLASH_READ_ACR();
+ uint32 val = FLASH_BASE->ACR;
- val &= ~ACR_LATENCY;
+ val &= ~FLASH_ACR_LATENCY;
val |= wait_states;
- FLASH_WRITE_ACR(val);
+ FLASH_BASE->ACR = val;
}
diff --git a/libmaple/flash.h b/libmaple/flash.h
index 7b74c83..9db5015 100644
--- a/libmaple/flash.h
+++ b/libmaple/flash.h
@@ -25,20 +25,108 @@
/**
* @file flash.h
- * @brief basic stm32 flash setup routines
+ * @brief STM32 Medium and high density Flash register map and setup
+ * routines
*/
#ifndef _FLASH_H_
#define _FLASH_H_
-#define FLASH_WAIT_STATE_0 0x0
-#define FLASH_WAIT_STATE_1 0x1
-#define FLASH_WAIT_STATE_2 0x2
-
#ifdef __cplusplus
extern "C"{
#endif
+/** Flash register map type */
+typedef struct flash_reg_map {
+ __io uint32 ACR; /**< Access control register */
+ __io uint32 KEYR; /**< Key register */
+ __io uint32 OPTKEYR; /**< OPTKEY register */
+ __io uint32 SR; /**< Status register */
+ __io uint32 CR; /**< Control register */
+ __io uint32 AR; /**< Address register */
+ __io uint32 OBR; /**< Option byte register */
+ __io uint32 WRPR; /**< Write protection register */
+} flash_reg_map;
+
+/** Flash register map base pointer */
+#define FLASH_BASE ((flash_reg_map*)0x40022000)
+
+/*
+ * Register bit definitions
+ */
+
+/* Access control register */
+
+#define FLASH_ACR_PRFTBS_BIT 5
+#define FLASH_ACR_PRFTBE_BIT 4
+#define FLASH_ACR_HLFCYA_BIT 3
+
+#define FLASH_ACR_PRFTBS BIT(FLASH_ACR_PRFTBS_BIT)
+#define FLASH_ACR_PRFTBE BIT(FLASH_ACR_PRFTBE_BIT)
+#define FLASH_ACR_HLFCYA BIT(FLASH_ACR_HLFCYA_BIT)
+#define FLASH_ACR_LATENCY 0x7
+
+/* Status register */
+
+#define FLASH_SR_EOP_BIT 5
+#define FLASH_SR_WRPRTERR_BIT 4
+#define FLASH_SR_PGERR_BIT 2
+#define FLASH_SR_BSY_BIT 0
+
+#define FLASH_SR_EOP BIT(FLASH_SR_EOP_BIT)
+#define FLASH_SR_WRPRTERR BIT(FLASH_SR_WRPRTERR_BIT)
+#define FLASH_SR_PGERR BIT(FLASH_SR_PGERR_BIT)
+#define FLASH_SR_BSY BIT(FLASH_SR_BSY_BIT)
+
+/* Control register */
+
+#define FLASH_CR_EOPIE_BIT 12
+#define FLASH_CR_ERRIE_BIT 10
+#define FLASH_CR_OPTWRE_BIT 9
+#define FLASH_CR_LOCK_BIT 7
+#define FLASH_CR_STRT_BIT 6
+#define FLASH_CR_OPTER_BIT 5
+#define FLASH_CR_OPTPG_BIT 4
+#define FLASH_CR_MER_BIT 2
+#define FLASH_CR_PER_BIT 1
+#define FLASH_CR_PG_BIT 0
+
+#define FLASH_CR_EOPIE BIT(FLASH_CR_EOPIE_BIT)
+#define FLASH_CR_ERRIE BIT(FLASH_CR_ERRIE_BIT)
+#define FLASH_CR_OPTWRE BIT(FLASH_CR_OPTWRE_BIT)
+#define FLASH_CR_LOCK BIT(FLASH_CR_LOCK_BIT)
+#define FLASH_CR_STRT BIT(FLASH_CR_STRT_BIT)
+#define FLASH_CR_OPTER BIT(FLASH_CR_OPTER_BIT)
+#define FLASH_CR_OPTPG BIT(FLASH_CR_OPTPG_BIT)
+#define FLASH_CR_MER BIT(FLASH_CR_MER_BIT)
+#define FLASH_CR_PER BIT(FLASH_CR_PER_BIT)
+#define FLASH_CR_PG BIT(FLASH_CR_PG_BIT)
+
+/* Option byte register */
+
+#define FLASH_OBR_nRST_STDBY_BIT 4
+#define FLASH_OBR_nRST_STOP_BIT 3
+#define FLASH_OBR_WDG_SW_BIT 2
+#define FLASH_OBR_RDPRT_BIT 1
+#define FLASH_OBR_OPTERR_BIT 0
+
+#define FLASH_OBR_DATA1 (0xFF << 18)
+#define FLASH_OBR_DATA0 (0xFF << 10)
+#define FLASH_OBR_USER 0x3FF
+#define FLASH_OBR_nRST_STDBY BIT(FLASH_OBR_nRST_STDBY_BIT)
+#define FLASH_OBR_nRST_STOP BIT(FLASH_OBR_nRST_STOP_BIT)
+#define FLASH_OBR_WDG_SW BIT(FLASH_OBR_WDG_SW_BIT)
+#define FLASH_OBR_RDPRT BIT(FLASH_OBR_RDPRT_BIT)
+#define FLASH_OBR_OPTERR BIT(FLASH_OBR_OPTERR_BIT)
+
+/*
+ * Setup routines
+ */
+
+#define FLASH_WAIT_STATE_0 0x0
+#define FLASH_WAIT_STATE_1 0x1
+#define FLASH_WAIT_STATE_2 0x2
+
void flash_enable_prefetch(void);
void flash_set_latency(uint32 wait_states);
diff --git a/libmaple/fsmc.c b/libmaple/fsmc.c
index 49526f4..356e1e5 100644
--- a/libmaple/fsmc.c
+++ b/libmaple/fsmc.c
@@ -26,109 +26,65 @@
* @brief
*/
-#include "libmaple.h"
-#include "rcc.h"
-#include "gpio.h"
#include "fsmc.h"
+#include "gpio.h"
-/* These values determined for a particular SRAM chip by following the
- * calculations in the ST FSMC application note. */
-#define FSMC_ADDSET 0x0
-#define FSMC_DATAST 0x3
-
-/* Sets up the FSMC peripheral to use the SRAM chip on the maple
- * native as an external segment of system memory space. This
- * implementation is for the IS62WV51216BLL 8mbit chip (55ns
- * timing) */
-void fsmc_native_sram_init(void) {
- FSMC_Bank *bank;
+#ifdef STM32_HIGH_DENSITY
- /* First we setup all the GPIO pins. */
+/**
+ * Configure FSMC GPIOs for use with SRAM.
+ */
+void fsmc_sram_init_gpios(void) {
/* Data lines... */
- gpio_set_mode(GPIOD_BASE, 0, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOD_BASE, 1, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOD_BASE, 8, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOD_BASE, 9, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOD_BASE, 10, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOD_BASE, 14, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOD_BASE, 15, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOE_BASE, 7, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOE_BASE, 8, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOE_BASE, 9, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOE_BASE, 10, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOE_BASE, 11, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOE_BASE, 12, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOE_BASE, 13, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOE_BASE, 14, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOE_BASE, 15, MODE_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 0, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 1, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 8, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 9, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 10, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 14, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 15, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOE, 7, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOE, 8, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOE, 9, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOE, 10, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOE, 11, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOE, 12, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOE, 13, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOE, 14, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOE, 15, GPIO_AF_OUTPUT_PP);
/* Address lines... */
- gpio_set_mode(GPIOD_BASE, 11, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOD_BASE, 12, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOD_BASE, 13, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 0, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 1, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 2, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 3, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 4, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 5, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 12, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 13, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 14, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOF_BASE, 15, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOG_BASE, 0, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOG_BASE, 1, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOG_BASE, 2, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOG_BASE, 3, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOG_BASE, 4, MODE_AF_OUTPUT_PP);
- gpio_set_mode(GPIOG_BASE, 5, MODE_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 11, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 12, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOD, 13, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 0, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 1, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 2, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 3, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 4, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 5, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 12, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 13, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 14, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOF, 15, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOG, 0, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOG, 1, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOG, 2, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOG, 3, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOG, 4, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(GPIOG, 5, GPIO_AF_OUTPUT_PP);
/* And control lines... */
- gpio_set_mode(GPIOD_BASE, 4, MODE_AF_OUTPUT_PP); // NOE
- gpio_set_mode(GPIOD_BASE, 5, MODE_AF_OUTPUT_PP); // NWE
-
- gpio_set_mode(GPIOD_BASE, 7, MODE_AF_OUTPUT_PP); // NE1
- gpio_set_mode(GPIOG_BASE, 9, MODE_AF_OUTPUT_PP); // NE2
- gpio_set_mode(GPIOG_BASE, 10, MODE_AF_OUTPUT_PP); // NE3
- gpio_set_mode(GPIOG_BASE, 12, MODE_AF_OUTPUT_PP); // NE4
-
- gpio_set_mode(GPIOE_BASE, 0, MODE_AF_OUTPUT_PP); // NBL0
- gpio_set_mode(GPIOE_BASE, 1, MODE_AF_OUTPUT_PP); // NBL1
-
- /* Next enable the clock */
- rcc_clk_enable(RCC_FSMC);
-
- /* Then we configure channel 1 the FSMC SRAM peripheral (all SRAM
- * channels are in "Bank 1" of the FSMC) */
- bank = (FSMC_Bank*)(FSMC1_BASE);
-
- /* Everything else is cleared (BCR1) */
- bank->BCR = 0x0000;
-
- /* Memory type is SRAM */
- bank->BCR &= ~(FSMC_BCR_MTYP); // '00'
-
- /* Databus width is 16bits */
- bank->BCR &= ~(FSMC_BCR_MWID);
- bank->BCR |= 0x1 << 4; // '01'
-
- /* Memory is nonmultiplexed */
- bank->BCR &= ~(FSMC_BCR_MUXEN); // '0'
-
- /* Need write enable to write to the chip */
- bank->BCR |= FSMC_BCR_WREN;
-
- /* Set ADDSET */
- bank->BTR &= ~(FSMC_BTR_ADDSET);
- bank->BTR |= (FSMC_BTR_ADDSET | FSMC_ADDSET);
-
- /* Set DATAST */
- bank->BTR &= ~(FSMC_BTR_DATAST);
- bank->BTR |= (FSMC_BTR_DATAST | (FSMC_DATAST << 8));
+ gpio_set_mode(GPIOD, 4, GPIO_AF_OUTPUT_PP); // NOE
+ gpio_set_mode(GPIOD, 5, GPIO_AF_OUTPUT_PP); // NWE
- /* Enable channel 1 */
- bank->BCR |= FSMC_BCR_MBKEN; // '1'
+ gpio_set_mode(GPIOD, 7, GPIO_AF_OUTPUT_PP); // NE1
+ gpio_set_mode(GPIOG, 9, GPIO_AF_OUTPUT_PP); // NE2
+ gpio_set_mode(GPIOG, 10, GPIO_AF_OUTPUT_PP); // NE3
+ gpio_set_mode(GPIOG, 12, GPIO_AF_OUTPUT_PP); // NE4
- /* (FSMC_BWTR3 not used for this simple configuration.) */
+ gpio_set_mode(GPIOE, 0, GPIO_AF_OUTPUT_PP); // NBL0
+ gpio_set_mode(GPIOE, 1, GPIO_AF_OUTPUT_PP); // NBL1
}
+#endif /* STM32_HIGH_DENSITY */
diff --git a/libmaple/fsmc.h b/libmaple/fsmc.h
index e83b529..8b6cac5 100644
--- a/libmaple/fsmc.h
+++ b/libmaple/fsmc.h
@@ -26,6 +26,8 @@
* See ../notes/fsmc.txt for more info
*/
+#include "libmaple_types.h"
+
/**
* @file fsmc.h
*/
@@ -37,54 +39,275 @@
extern "C"{
#endif
-// There are 4 FSMC chip-select devices; here are the SRAM-specific registers
-// for each
-
-#define FSMC1_BASE 0xA0000000
-#define FSMC2_BASE 0xA0000008
-#define FSMC3_BASE 0xA0000010
-#define FSMC4_BASE 0xA0000018
-
-typedef struct {
- volatile uint32 BCR;
- volatile uint32 BTR;
- //uint32 pad[62]; // double check this?
- //__io uint32 BWTR;
-} FSMC_Bank;
-
-// And here are the register bit ranges
-#define FSMC_BCR_MBKEN 0b00000000000000000000000000000001
-#define FSMC_BCR_MUXEN 0b00000000000000000000000000000010
-#define FSMC_BCR_MTYP 0b00000000000000000000000000001100
-#define FSMC_BCR_MWID 0b00000000000000000000000000110000
-#define FSMC_BCR_FACCEN 0b00000000000000000000000001000000
-#define FSMC_BCR_BURSTEN 0b00000000000000000000000100000000
-#define FSMC_BCR_WAITPOL 0b00000000000000000000001000000000
-#define FSMC_BCR_WRAPMOD 0b00000000000000000000010000000000
-#define FSMC_BCR_WAITCFG 0b00000000000000000000100000000000
-#define FSMC_BCR_WREN 0b00000000000000000001000000000000
-#define FSMC_BCR_WAITEN 0b00000000000000000010000000000000
-#define FSMC_BCR_EXTMOD 0b00000000000000000100000000000000
-#define FSMC_BCR_CBURSTRW 0b00000000000010000000000000000000
-#define FSMC_BTR_ADDSET 0b00000000000000000000000000001111
-#define FSMC_BTR_ADDHOLD 0b00000000000000000000000011110000
-#define FSMC_BTR_DATAST 0b00000000000000001111111100000000
-#define FSMC_BTR_BUSTURN 0b00000000000011110000000000000000
-#define FSMC_BTR_CLKDIV 0b00000000111100000000000000000000
-#define FSMC_BTR_DATALAT 0b00001111000000000000000000000000
-#define FSMC_BTR_ACCMOD 0b00110000000000000000000000000000
-#define FSMC_BWTR_ADDSET 0b00000000000000000000000000001111
-#define FSMC_BWTR_ADDHLD 0b00000000000000000000000011110000
-#define FSMC_BWTR_DATAST 0b00000000000000001111111100000000
-#define FSMC_BWTR_CLKDIV 0b00000000111100000000000000000000
-#define FSMC_BWTR_DATLAT 0b00001111000000000000000000000000
-#define FSMC_BWTR_ACCMOD 0b00110000000000000000000000000000
-
-void fsmc_native_sram_init(void);
+#ifdef STM32_HIGH_DENSITY
+
+/*
+ * Register maps and devices
+ */
+
+/** FSMC register map type */
+typedef struct fsmc_reg_map {
+ __io uint32 BCR1; /**< SRAM/NOR-Flash chip-select control register 1 */
+ __io uint32 BTR1; /**< SRAM/NOR-Flash chip-select timing register 1 */
+ __io uint32 BCR2; /**< SRAM/NOR-Flash chip-select control register 2 */
+ __io uint32 BTR2; /**< SRAM/NOR-Flash chip-select timing register 2 */
+ __io uint32 BCR3; /**< SRAM/NOR-Flash chip-select control register 3 */
+ __io uint32 BTR3; /**< SRAM/NOR-Flash chip-select timing register 3 */
+ __io uint32 BCR4; /**< SRAM/NOR-Flash chip-select control register 4 */
+ __io uint32 BTR4; /**< SRAM/NOR-Flash chip-select timing register 4 */
+ const uint8 RESERVED1[64]; /**< Reserved */
+ __io uint32 PCR2; /**< PC Card/NAND Flash control register 2 */
+ __io uint32 SR2; /**< FIFO status and interrupt register 2 */
+ __io uint32 PMEM2; /**< Common memory space timing register 2 */
+ __io uint32 PATT2; /**< Attribute memory space timing register 2 */
+ const uint8 RESERVED2[4]; /**< Reserved */
+ __io uint32 ECCR2; /**< ECC result register 2 */
+ const uint8 RESERVED3[2];
+ __io uint32 PCR3; /**< PC Card/NAND Flash control register 3 */
+ __io uint32 SR3; /**< FIFO status and interrupt register 3 */
+ __io uint32 PMEM3; /**< Common memory space timing register 3 */
+ __io uint32 PATT3; /**< Attribute memory space timing register 3 */
+ const uint32 RESERVED4; /**< Reserved */
+ __io uint32 ECCR3; /**< ECC result register 3 */
+ const uint8 RESERVED5[8]; /**< Reserved */
+ __io uint32 PCR4; /**< PC Card/NAND Flash control register 4 */
+ __io uint32 SR4; /**< FIFO status and interrupt register 4 */
+ __io uint32 PMEM4; /**< Common memory space timing register 4 */
+ __io uint32 PATT4; /**< Attribute memory space timing register 4 */
+ __io uint32 PIO4; /**< I/O space timing register 4 */
+ const uint8 RESERVED6[80]; /**< Reserved */
+ __io uint32 BWTR1; /**< SRAM/NOR-Flash write timing register 1 */
+ const uint32 RESERVED7; /**< Reserved */
+ __io uint32 BWTR2; /**< SRAM/NOR-Flash write timing register 2 */
+ const uint32 RESERVED8; /**< Reserved */
+ __io uint32 BWTR3; /**< SRAM/NOR-Flash write timing register 3 */
+ const uint32 RESERVED9; /**< Reserved */
+ __io uint32 BWTR4; /**< SRAM/NOR-Flash write timing register 4 */
+} __attribute__((packed)) fsmc_reg_map;
+
+#define __FSMCB 0xA0000000
+
+/** FSMC register map base pointer */
+#define FSMC_BASE ((struct fsmc_reg_map*)__FSMCB)
+
+/** FSMC NOR/PSRAM register map type */
+typedef struct fsmc_nor_psram_reg_map {
+ __io uint32 BCR; /**< Chip-select control register */
+ __io uint32 BTR; /**< Chip-select timing register */
+ const uint8 RESERVED[252]; /**< Reserved */
+ __io uint32 BWTR; /**< Write timing register */
+} fsmc_nor_psram_reg_map;
+
+/** FSMC NOR/PSRAM base pointer 1 */
+#define FSMC_NOR_PSRAM1_BASE ((struct fsmc_nor_psram_reg_map*)__FSMCB)
+
+/** FSMC NOR/PSRAM base pointer 2 */
+#define FSMC_NOR_PSRAM2_BASE ((struct fsmc_nor_psram_reg_map*)(__FSMCB + 0x8))
+
+/** FSMC NOR/PSRAM base pointer 3 */
+#define FSMC_NOR_PSRAM3_BASE ((struct fsmc_nor_psram_reg_map*)(__FSMCB + 0x10))
+
+/** FSMC NOR/PSRAM base pointer 4 */
+#define FSMC_NOR_PSRAM4_BASE ((struct fsmc_nor_psram_reg_map*)(__FSMCB + 0x18))
+
+/*
+ * Register bit definitions
+ */
+
+/* NOR/PSRAM chip-select control registers */
+
+#define FSMC_BCR_CBURSTRW_BIT 19
+#define FSMC_BCR_ASYNCWAIT_BIT 15
+#define FSMC_BCR_EXTMOD_BIT 14
+#define FSMC_BCR_WAITEN_BIT 13
+#define FSMC_BCR_WREN_BIT 12
+#define FSMC_BCR_WAITCFG_BIT 11
+#define FSMC_BCR_WRAPMOD_BIT 10
+#define FSMC_BCR_WAITPOL_BIT 9
+#define FSMC_BCR_BURSTEN_BIT 8
+#define FSMC_BCR_FACCEN_BIT 6
+#define FSMC_BCR_MUXEN_BIT 1
+#define FSMC_BCR_MBKEN_BIT 0
+
+#define FSMC_BCR_CBURSTRW BIT(FSMC_BCR_CBURSTRW_BIT)
+#define FSMC_BCR_ASYNCWAIT BIT(FSMC_BCR_ASYNCWAIT_BIT)
+#define FSMC_BCR_EXTMOD BIT(FSMC_BCR_EXTMOD_BIT)
+#define FSMC_BCR_WAITEN BIT(FSMC_BCR_WAITEN_BIT)
+#define FSMC_BCR_WREN BIT(FSMC_BCR_WREN_BIT)
+#define FSMC_BCR_WAITCFG BIT(FSMC_BCR_WAITCFG_BIT)
+#define FSMC_BCR_WRAPMOD BIT(FSMC_BCR_WRAPMOD_BIT)
+#define FSMC_BCR_WAITPOL BIT(FSMC_BCR_WAITPOL_BIT)
+#define FSMC_BCR_BURSTEN BIT(FSMC_BCR_BURSTEN_BIT)
+#define FSMC_BCR_FACCEN BIT(FSMC_BCR_FACCEN_BIT)
+#define FSMC_BCR_MWID (0x3 << 4)
+#define FSMC_BCR_MWID_8BITS (0x0 << 4)
+#define FSMC_BCR_MWID_16BITS (0x1 << 4)
+#define FSMC_BCR_MTYP (0x3 << 2)
+#define FSMC_BCR_MTYP_SRAM (0x0 << 2)
+#define FSMC_BCR_MTYP_PSRAM (0x1 << 2)
+#define FSMC_BCR_MTYP_NOR_FLASH (0x2 << 2)
+#define FSMC_BCR_MUXEN BIT(FSMC_BCR_MUXEN_BIT)
+#define FSMC_BCR_MBKEN BIT(FSMC_BCR_MBKEN_BIT)
+
+/* SRAM/NOR-Flash chip-select timing registers */
+
+#define FSMC_BTR_ACCMOD (0x3 << 28)
+#define FSMC_BTR_ACCMOD_A (0x0 << 28)
+#define FSMC_BTR_ACCMOD_B (0x1 << 28)
+#define FSMC_BTR_ACCMOD_C (0x2 << 28)
+#define FSMC_BTR_ACCMOD_D (0x3 << 28)
+#define FSMC_BTR_DATLAT (0xF << 24)
+#define FSMC_BTR_CLKDIV (0xF << 20)
+#define FSMC_BTR_BUSTURN (0xF << 16)
+#define FSMC_BTR_DATAST (0xFF << 8)
+#define FSMC_BTR_ADDHLD (0xF << 4)
+#define FSMC_BTR_ADDSET 0xF
+
+/* SRAM/NOR-Flash write timing registers */
+
+#define FSMC_BWTR_ACCMOD (0x3 << 28)
+#define FSMC_BWTR_ACCMOD_A (0x0 << 28)
+#define FSMC_BWTR_ACCMOD_B (0x1 << 28)
+#define FSMC_BWTR_ACCMOD_C (0x2 << 28)
+#define FSMC_BWTR_ACCMOD_D (0x3 << 28)
+#define FSMC_BWTR_DATLAT (0xF << 24)
+#define FSMC_BWTR_CLKDIV (0xF << 20)
+#define FSMC_BWTR_DATAST (0xFF << 8)
+#define FSMC_BWTR_ADDHLD (0xF << 4)
+#define FSMC_BWTR_ADDSET 0xF
+
+/* NAND Flash/PC Card controller registers */
+
+#define FSMC_PCR_ECCEN_BIT 6
+#define FSMC_PCR_PTYP_BIT 3
+#define FSMC_PCR_PBKEN_BIT 2
+#define FSMC_PCR_PWAITEN_BIT 1
+
+#define FSMC_PCR_ECCPS (0x7 << 17)
+#define FSMC_PCR_ECCPS_256B (0x0 << 17)
+#define FSMC_PCR_ECCPS_512B (0x1 << 17)
+#define FSMC_PCR_ECCPS_1024B (0x2 << 17)
+#define FSMC_PCR_ECCPS_2048B (0x3 << 17)
+#define FSMC_PCR_ECCPS_4096B (0x4 << 17)
+#define FSMC_PCR_ECCPS_8192B (0x5 << 17)
+#define FSMC_PCR_TAR (0xF << 13)
+#define FSMC_PCR_TCLR (0xF << 9)
+#define FSMC_PCR_ECCEN BIT(FSMC_PCR_ECCEN_BIT)
+#define FSMC_PCR_PWID (0x3 << 4)
+#define FSMC_PCR_PWID_8BITS (0x0 << 4)
+#define FSMC_PCR_PWID_16BITS (0x1 << 4)
+#define FSMC_PCR_PTYP BIT(FSMC_PCR_PTYP_BIT)
+#define FSMC_PCR_PTYP_PC_CF_PCMCIA (0x0 << FSMC_PCR_PTYP_BIT)
+#define FSMC_PCR_PTYP_NAND (0x1 << FSMC_PCR_PTYP_BIT)
+#define FSMC_PCR_PBKEN BIT(FSMC_PCR_PBKEN_BIT)
+#define FSMC_PCR_PWAITEN BIT(FSMC_PCR_PWAITEN_BIT)
+
+/* FIFO status and interrupt registers */
+
+#define FSMC_SR_FEMPT_BIT 6
+#define FSMC_SR_IFEN_BIT 5
+#define FSMC_SR_ILEN_BIT 4
+#define FSMC_SR_IREN_BIT 3
+#define FSMC_SR_IFS_BIT 2
+#define FSMC_SR_ILS_BIT 1
+#define FSMC_SR_IRS_BIT 0
+
+#define FSMC_SR_FEMPT BIT(FSMC_SR_FEMPT_BIT)
+#define FSMC_SR_IFEN BIT(FSMC_SR_IFEN_BIT)
+#define FSMC_SR_ILEN BIT(FSMC_SR_ILEN_BIT)
+#define FSMC_SR_IREN BIT(FSMC_SR_IREN_BIT)
+#define FSMC_SR_IFS BIT(FSMC_SR_IFS_BIT)
+#define FSMC_SR_ILS BIT(FSMC_SR_ILS_BIT)
+#define FSMC_SR_IRS BIT(FSMC_SR_IRS_BIT)
+
+/* Common memory space timing registers */
+
+#define FSMC_PMEM_MEMHIZ (0xFF << 24)
+#define FSMC_PMEM_MEMHOLD (0xFF << 16)
+#define FSMC_PMEM_MEMWAIT (0xFF << 8)
+#define FSMC_PMEM_MEMSET 0xFF
+
+/* Attribute memory space timing registers */
+
+#define FSMC_PATT_ATTHIZ (0xFF << 24)
+#define FSMC_PATT_ATTHOLD (0xFF << 16)
+#define FSMC_PATT_ATTWAIT (0xFF << 8)
+#define FSMC_PATT_ATTSET 0xFF
+
+/* I/O space timing register 4 */
+
+#define FSMC_PIO_IOHIZ (0xFF << 24)
+#define FSMC_PIO_IOHOLD (0xFF << 16)
+#define FSMC_PIO_IOWAIT (0xFF << 8)
+#define FSMC_PIO_IOSET 0xFF
+
+/*
+ * Memory bank boundary addresses
+ */
+
+/** Pointer to base address of FSMC memory bank 1 (split into 4
+ * regions, each supporting 1 NOR Flash, SRAM, or PSRAM chip) */
+#define FSMC_BANK1 ((void*)0x60000000)
+
+/** Pointer to base address of FSMC memory bank 1, region 1 (for NOR/PSRAM) */
+#define FSMC_NOR_PSRAM_REGION1 FSMC_BANK1
+
+/** Pointer to base address of FSMC memory bank 1, region 2 (for NOR/PSRAM) */
+#define FSMC_NOR_PSRAM_REGION2 ((void*)0x64000000)
+
+/** Pointer to base address of FSMC memory bank 1, region 3 (for NOR/PSRAM) */
+#define FSMC_NOR_PSRAM_REGION3 ((void*)0x68000000)
+
+/** Pointer to base address of FSMC memory bank 1, region 4 (for NOR/PSRAM) */
+#define FSMC_NOR_PSRAM_REGION4 ((void*)0x6C000000)
+
+/** Pointer to base address of FSMC memory bank 2 (for NAND Flash) */
+#define FSMC_BANK2 ((void*)0x70000000)
+
+/** Pointer to base address of FSMC memory bank 3 (for NAND Flash) */
+#define FSMC_BANK3 ((void*)0x80000000)
+
+/** Pointer to base address of FSMC memory bank 4 (for PC card devices */
+#define FSMC_BANK4 ((void*)0x90000000)
+
+/*
+ * SRAM/NOR Flash routines
+ */
+
+void fsmc_sram_init_gpios(void);
+
+/**
+ * Set the DATAST bits in the given NOR/PSRAM register map's
+ * chip-select timing register (FSMC_BTR).
+ *
+ * @param regs NOR Flash/PSRAM register map whose chip-select timing
+ * register to set.
+ * @param datast Value to use for DATAST bits.
+ */
+static inline void fsmc_nor_psram_set_datast(fsmc_nor_psram_reg_map *regs,
+ uint8 datast) {
+ regs->BTR &= ~FSMC_BTR_DATAST;
+ regs->BTR |= datast << 8;
+}
+
+/**
+ * Set the ADDHLD bits in the given NOR/PSRAM register map's chip
+ * select timing register (FSMC_BTRx).
+ *
+ * @param regs NOR Flash/PSRAM register map whose chip-select timing
+ * register to set.
+ * @param addset Value to use for ADDSET bits.
+ */
+static inline void fsmc_nor_psram_set_addset(fsmc_nor_psram_reg_map *regs,
+ uint8 addset) {
+ regs->BTR &= ~FSMC_BTR_ADDSET;
+ regs->BTR |= addset & 0xF;
+}
+
+#endif /* STM32_HIGH_DENSITY */
#ifdef __cplusplus
-} // extern "C"
+} /* extern "C" */
#endif
-
#endif
diff --git a/libmaple/gpio.c b/libmaple/gpio.c
index 71e5230..5484e21 100644
--- a/libmaple/gpio.c
+++ b/libmaple/gpio.c
@@ -26,44 +26,151 @@
* @brief GPIO initialization routine
*/
-#include "libmaple.h"
-#include "rcc.h"
#include "gpio.h"
+#include "rcc.h"
+
+/*
+ * GPIO devices
+ */
+
+gpio_dev gpioa = {
+ .regs = GPIOA_BASE,
+ .clk_id = RCC_GPIOA,
+ .exti_port = AFIO_EXTI_PA,
+};
+gpio_dev* const GPIOA = &gpioa;
+
+gpio_dev gpiob = {
+ .regs = GPIOB_BASE,
+ .clk_id = RCC_GPIOB,
+ .exti_port = AFIO_EXTI_PB,
+};
+gpio_dev* const GPIOB = &gpiob;
+
+gpio_dev gpioc = {
+ .regs = GPIOC_BASE,
+ .clk_id = RCC_GPIOC,
+ .exti_port = AFIO_EXTI_PC,
+};
+gpio_dev* const GPIOC = &gpioc;
+
+gpio_dev gpiod = {
+ .regs = GPIOD_BASE,
+ .clk_id = RCC_GPIOD,
+ .exti_port = AFIO_EXTI_PD,
+};
+gpio_dev* const GPIOD = &gpiod;
+
+#ifdef STM32_HIGH_DENSITY
+gpio_dev gpioe = {
+ .regs = GPIOE_BASE,
+ .clk_id = RCC_GPIOE,
+ .exti_port = AFIO_EXTI_PE,
+};
+gpio_dev* const GPIOE = &gpioe;
+
+gpio_dev gpiof = {
+ .regs = GPIOF_BASE,
+ .clk_id = RCC_GPIOF,
+ .exti_port = AFIO_EXTI_PF,
+};
+gpio_dev* const GPIOF = &gpiof;
-void gpio_init(void) {
- rcc_clk_enable(RCC_GPIOA);
- rcc_clk_enable(RCC_GPIOB);
- rcc_clk_enable(RCC_GPIOC);
-#if NR_GPIO_PORTS >= 4 /* Maple, but not Maple Mini */
- rcc_clk_enable(RCC_GPIOD);
-#elif NR_GPIO_PORTS >= 7 /* Maple Native (high density only) */
- rcc_clk_enable(RCC_GPIOE);
- rcc_clk_enable(RCC_GPIOF);
- rcc_clk_enable(RCC_GPIOG);
+gpio_dev gpiog = {
+ .regs = GPIOG_BASE,
+ .clk_id = RCC_GPIOG,
+ .exti_port = AFIO_EXTI_PG,
+};
+gpio_dev* const GPIOG = &gpiog;
#endif
- rcc_clk_enable(RCC_AFIO);
+
+/*
+ * GPIO convenience routines
+ */
+
+/**
+ * Initialize a GPIO device.
+ *
+ * Enables the clock for and resets the given device.
+ *
+ * @param dev GPIO device to initialize.
+ */
+void gpio_init(gpio_dev *dev) {
+ rcc_clk_enable(dev->clk_id);
+ rcc_reset_dev(dev->clk_id);
}
-void gpio_set_mode(GPIO_Port* port, uint8 gpio_pin, GPIOPinMode mode) {
- uint32 tmp;
- uint32 shift = POS(gpio_pin % 8);
- GPIOReg CR;
+/**
+ * Initialize and reset all available GPIO devices.
+ */
+void gpio_init_all(void) {
+ gpio_init(GPIOA);
+ gpio_init(GPIOB);
+ gpio_init(GPIOC);
+ gpio_init(GPIOD);
+#ifdef STM32_HIGH_DENSITY
+ gpio_init(GPIOE);
+ gpio_init(GPIOF);
+ gpio_init(GPIOG);
+#endif
+}
- ASSERT(port);
- ASSERT(gpio_pin < 16);
+/**
+ * Set the mode of a GPIO pin.
+ *
+ * @param dev GPIO device.
+ * @param pin Pin on the device whose mode to set, 0--15.
+ * @param mode General purpose or alternate function mode to set the pin to.
+ * @see gpio_pin_mode
+ */
+void gpio_set_mode(gpio_dev *dev, uint8 pin, gpio_pin_mode mode) {
+ gpio_reg_map *regs = dev->regs;
+ __io uint32 *cr = &regs->CRL + (pin >> 3);
+ uint32 shift = (pin & 0x7) * 4;
+ uint32 tmp = *cr;
+
+ tmp &= ~(0xF << shift);
+ tmp |= (mode == GPIO_INPUT_PU ? GPIO_INPUT_PD : mode) << shift;
+ *cr = tmp;
- if (mode == GPIO_MODE_INPUT_PU) {
- port->ODR |= BIT(gpio_pin);
- mode = CNF_INPUT_PD;
- } else if (mode == GPIO_MODE_INPUT_PD) {
- port->ODR &= ~BIT(gpio_pin);
+ if (mode == GPIO_INPUT_PD) {
+ regs->ODR &= ~BIT(pin);
+ } else if (mode == GPIO_INPUT_PU) {
+ regs->ODR |= BIT(pin);
}
+}
+
+/*
+ * AFIO
+ */
- CR = (gpio_pin < 8) ? &(port->CRL) : &(port->CRH);
+/**
+ * Initialize the AFIO clock, and reset the AFIO registers.
+ */
+void afio_init(void) {
+ rcc_clk_enable(RCC_AFIO);
+ rcc_reset_dev(RCC_AFIO);
+}
- tmp = *CR;
- tmp &= POS_MASK(shift);
- tmp |= mode << shift;
+#define AFIO_EXTI_SEL_MASK 0xF
+
+/**
+ * Select a source input for an external interrupt.
+ *
+ * @param exti External interrupt. One of: AFIO_EXTI_0,
+ * AFIO_EXTI_1, ..., AFIO_EXTI_15.
+ * @param gpio_port Port which contains pin to use as source input.
+ * One of: AFIO_EXTI_PA, AFIO_EXTI_PB, AFIO_EXTI_PC,
+ * AFIO_EXTI_PD, and, on high density devices,
+ * AFIO_EXTI_PE, AFIO_EXTI_PF, AFIO_EXTI_PG.
+ * @see exti_port
+ */
+void afio_exti_select(afio_exti_num exti, afio_exti_port gpio_port) {
+ __io uint32 *exti_cr = &AFIO_BASE->EXTICR1 + exti / 4;
+ uint32 shift = 4 * (exti % 4);
+ uint32 cr = *exti_cr;
- *CR = tmp;
+ cr &= ~(AFIO_EXTI_SEL_MASK << shift);
+ cr |= gpio_port << shift;
+ *exti_cr = cr;
}
diff --git a/libmaple/gpio.h b/libmaple/gpio.h
index 53f77c4..353f965 100644
--- a/libmaple/gpio.h
+++ b/libmaple/gpio.h
@@ -3,126 +3,357 @@
*
* 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
+ * 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 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.
- *****************************************************************************/
+ * 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 gpio.h
*
- * @brief GPIO prototypes, defines, and inlined access functions
+ * @brief General purpose I/0 (GPIO) and Alternate Function I/O
+ * (AFIO) prototypes, defines, and inlined access functions.
*/
-#ifndef _GPIO_H
-#define _GPIO_H
-
-/* Each of the GPIO port bits can be in the following modes (STM32 138/995):
- * Input floating
- * Input pull-up
- * Input pull-down
- * Analog Input
- * Output open-drain
- * Output push-pull
- * Alternate function push-pull
- * Alternate function open-drain
- *
- * - After reset, the alternate functions are not active and IO prts
- * are set to Input Floating mode, EXCEPT for the Serial Wire and JTAG
- * ports, which are in alternate function mode by default. */
-
-#define AFIO_MAPR ((volatile uint32*)0x40010004)
-
-#define GPIOA_BASE ((GPIO_Port*)0x40010800)
-#define GPIOB_BASE ((GPIO_Port*)0x40010C00)
-#define GPIOC_BASE ((GPIO_Port*)0x40011000)
-#define GPIOD_BASE ((GPIO_Port*)0x40011400)
-#define GPIOE_BASE ((GPIO_Port*)0x40011800) // High-density devices only
-#define GPIOF_BASE ((GPIO_Port*)0x40011C00) // High-density devices only
-#define GPIOG_BASE ((GPIO_Port*)0x40012000) // High-density devices only
-
-#define GPIO_SPEED_50MHZ (0x3)
-
-#define MODE_OUTPUT_PP ((0x00 << 2) | GPIO_SPEED_50MHZ)
-#define MODE_OUTPUT_OD ((0x01 << 2) | GPIO_SPEED_50MHZ)
-#define MODE_AF_OUTPUT_PP ((0x02 << 2) | GPIO_SPEED_50MHZ)
-#define MODE_AF_OUTPUT_OD ((0x03 << 2) | GPIO_SPEED_50MHZ)
-
-#define CNF_INPUT_ANALOG (0x00 << 2)
-#define CNF_INPUT_FLOATING (0x01 << 2)
-#define CNF_INPUT_PD (0x02 << 2)
-#define CNF_INPUT_PU (0x02 << 2)
-
-typedef enum GPIOPinMode {
- GPIO_MODE_OUTPUT_PP = MODE_OUTPUT_PP,
- GPIO_MODE_OUTPUT_OD = MODE_OUTPUT_OD,
- GPIO_MODE_AF_OUTPUT_PP = MODE_AF_OUTPUT_PP,
- GPIO_MODE_AF_OUTPUT_OD = MODE_AF_OUTPUT_OD,
- GPIO_MODE_INPUT_ANALOG = CNF_INPUT_ANALOG,
- GPIO_MODE_INPUT_FLOATING = CNF_INPUT_FLOATING,
- GPIO_MODE_INPUT_PD = CNF_INPUT_PD,
- GPIO_MODE_INPUT_PU,
-} GPIOPinMode;
-
-typedef struct {
- volatile uint32 CRL; // Port configuration register low
- volatile uint32 CRH; // Port configuration register high
- volatile uint32 IDR; // Port input data register
- volatile uint32 ODR; // Port output data register
- volatile uint32 BSRR; // Port bit set/reset register
- volatile uint32 BRR; // Port bit reset register
- volatile uint32 LCKR; // Port configuration lock register
-} GPIO_Port;
-
-typedef volatile uint32* GPIOReg;
-
-#define POS_MASK(shift) (~(0xF << shift))
-#define POS(val) (val << 2)
+#ifndef _GPIO_H_
+#define _GPIO_H_
+
+#include "libmaple.h"
+#include "rcc.h"
#ifdef __cplusplus
extern "C"{
#endif
-void gpio_init(void);
-void gpio_set_mode(GPIO_Port* port, uint8 gpio_pin, GPIOPinMode mode);
+/*
+ * GPIO register maps and devices
+ */
+
+/** GPIO register map type */
+typedef struct gpio_reg_map {
+ __io uint32 CRL; /**< Port configuration register low */
+ __io uint32 CRH; /**< Port configuration register high */
+ __io uint32 IDR; /**< Port input data register */
+ __io uint32 ODR; /**< Port output data register */
+ __io uint32 BSRR; /**< Port bit set/reset register */
+ __io uint32 BRR; /**< Port bit reset register */
+ __io uint32 LCKR; /**< Port configuration lock register */
+} gpio_reg_map;
+
+/**
+ * External interrupt line port selector. Used to determine which
+ * GPIO port to map an external interrupt line onto. */
+/* (See AFIO sections, below) */
+typedef enum {
+ AFIO_EXTI_PA, /**< Use PAx pin. */
+ AFIO_EXTI_PB, /**< Use PBx pin. */
+ AFIO_EXTI_PC, /**< Use PCx pin. */
+ AFIO_EXTI_PD, /**< Use PDx pin. */
+#ifdef STM32_HIGH_DENSITY
+ AFIO_EXTI_PE, /**< Use PEx pin. */
+ AFIO_EXTI_PF, /**< Use PFx pin. */
+ AFIO_EXTI_PG, /**< Use PGx pin. */
+#endif
+} afio_exti_port;
+
+/** GPIO device type */
+typedef struct gpio_dev {
+ gpio_reg_map *regs; /**< Register map */
+ rcc_clk_id clk_id; /**< RCC clock information */
+ afio_exti_port exti_port; /**< AFIO external interrupt port value */
+} gpio_dev;
+
+extern gpio_dev gpioa;
+extern gpio_dev* const GPIOA;
+extern gpio_dev gpiob;
+extern gpio_dev* const GPIOB;
+extern gpio_dev gpioc;
+extern gpio_dev* const GPIOC;
+extern gpio_dev gpiod;
+extern gpio_dev* const GPIOD;
+#ifdef STM32_HIGH_DENSITY
+extern gpio_dev gpioe;
+extern gpio_dev* const GPIOE;
+extern gpio_dev gpiof;
+extern gpio_dev* const GPIOF;
+extern gpio_dev gpiog;
+extern gpio_dev* const GPIOG;
+#endif
+
+/** GPIO port A register map base pointer */
+#define GPIOA_BASE ((gpio_reg_map*)0x40010800)
+/** GPIO port B register map base pointer */
+#define GPIOB_BASE ((gpio_reg_map*)0x40010C00)
+/** GPIO port C register map base pointer */
+#define GPIOC_BASE ((gpio_reg_map*)0x40011000)
+/** GPIO port D register map base pointer */
+#define GPIOD_BASE ((gpio_reg_map*)0x40011400)
+#ifdef STM32_HIGH_DENSITY
+/** GPIO port E register map base pointer */
+#define GPIOE_BASE ((gpio_reg_map*)0x40011800)
+/** GPIO port F register map base pointer */
+#define GPIOF_BASE ((gpio_reg_map*)0x40011C00)
+/** GPIO port G register map base pointer */
+#define GPIOG_BASE ((gpio_reg_map*)0x40012000)
+#endif
+
+/*
+ * GPIO register bit definitions
+ */
+
+/* Control registers, low and high */
+#define GPIO_CR_CNF_INPUT_ANALOG (0x0 << 2)
+#define GPIO_CR_CNF_INPUT_FLOATING (0x1 << 2)
+#define GPIO_CR_CNF_INPUT_PU_PD (0x2 << 2)
+#define GPIO_CR_CNF_OUTPUT_PP (0x0 << 2)
+#define GPIO_CR_CNF_OUTPUT_OD (0x1 << 2)
+#define GPIO_CR_CNF_AF_OUTPUT_PP (0x2 << 2)
+#define GPIO_CR_CNF_AF_OUTPUT_OD (0x3 << 2)
+#define GPIO_CR_MODE_INPUT 0x0
+#define GPIO_CR_MODE_OUTPUT_10MHZ 0x1
+#define GPIO_CR_MODE_OUTPUT_2MHZ 0x2
+#define GPIO_CR_MODE_OUTPUT_50MHZ 0x3
-static inline void gpio_write_bit(GPIO_Port *port, uint8 gpio_pin, uint8 val) {
- if (val){
- port->BSRR = BIT(gpio_pin);
+/** GPIO Pin modes. These only allow for 50MHZ max output speeds; if
+ * you want slower, use direct register access. */
+typedef enum gpio_pin_mode {
+ GPIO_OUTPUT_PP = (GPIO_CR_CNF_OUTPUT_PP |
+ GPIO_CR_MODE_OUTPUT_50MHZ), /**< Output push-pull. */
+ GPIO_OUTPUT_OD = (GPIO_CR_CNF_OUTPUT_OD |
+ GPIO_CR_MODE_OUTPUT_50MHZ), /**< Output open-drain. */
+ GPIO_AF_OUTPUT_PP = (GPIO_CR_CNF_AF_OUTPUT_PP |
+ GPIO_CR_MODE_OUTPUT_50MHZ), /**< Alternate function
+ output push-pull. */
+ GPIO_AF_OUTPUT_OD = (GPIO_CR_CNF_AF_OUTPUT_OD |
+ GPIO_CR_MODE_OUTPUT_50MHZ), /**< Alternate function
+ output open drain. */
+ GPIO_INPUT_ANALOG = (GPIO_CR_CNF_INPUT_ANALOG |
+ GPIO_CR_MODE_INPUT), /**< Analog input. */
+ GPIO_INPUT_FLOATING = (GPIO_CR_CNF_INPUT_FLOATING |
+ GPIO_CR_MODE_INPUT), /**< Input floating. */
+ GPIO_INPUT_PD = (GPIO_CR_CNF_INPUT_PU_PD |
+ GPIO_CR_MODE_INPUT), /**< Input pull-down. */
+ GPIO_INPUT_PU /**< Input pull-up. */
+ /* GPIO_INPUT_PU treated as a special case, for ODR twiddling */
+} gpio_pin_mode;
+
+/*
+ * GPIO Convenience routines
+ */
+
+void gpio_init(gpio_dev *dev);
+void gpio_init_all(void);
+void gpio_set_mode(gpio_dev *dev, uint8 pin, gpio_pin_mode mode);
+
+/**
+ * @brief Get a GPIO port's corresponding afio_exti_port.
+ * @param dev GPIO device whose afio_exti_port to return.
+ */
+static inline afio_exti_port gpio_exti_port(gpio_dev *dev) {
+ return dev->exti_port;
+}
+
+/**
+ * Set or reset a GPIO pin.
+ *
+ * Pin must have previously been configured to output mode.
+ *
+ * @param dev GPIO device whose pin to set.
+ * @param pin Pin on to set or reset
+ * @param val If true, set the pin. If false, reset the pin.
+ */
+static inline void gpio_write_bit(gpio_dev *dev, uint8 pin, uint8 val) {
+ if (val) {
+ dev->regs->BSRR = BIT(pin);
} else {
- port->BRR = BIT(gpio_pin);
+ dev->regs->BRR = BIT(pin);
}
}
-static inline uint32 gpio_read_bit(GPIO_Port *port, uint8 gpio_pin) {
- return (port->IDR & BIT(gpio_pin) ? 1 : 0);
+/**
+ * Determine whether or not a GPIO pin is set.
+ *
+ * Pin must have previously been configured to input mode.
+ *
+ * @param dev GPIO device whose pin to test.
+ * @param pin Pin on dev to test.
+ * @return True if the pin is set, false otherwise.
+ */
+static inline uint32 gpio_read_bit(gpio_dev *dev, uint8 pin) {
+ return dev->regs->IDR & BIT(pin);
+}
+
+/**
+ * Toggle a pin configured as output push-pull.
+ * @param dev GPIO device.
+ * @param pin Pin on dev to toggle.
+ */
+static inline void gpio_toggle_bit(gpio_dev *dev, uint8 pin) {
+ dev->regs->ODR = dev->regs->ODR ^ BIT(pin);
}
-/* For pins configured as output push-pull, reading the ODR returns
- * the last value written in push-pull mode.
+/*
+ * AFIO register map
*/
-static inline void gpio_toggle_pin(GPIO_Port *port, uint8 gpio_pin) {
- port->ODR = port->ODR ^ BIT(gpio_pin);
+
+/** AFIO register map */
+typedef struct afio_reg_map {
+ __io uint32 EVCR; /**< Event control register. */
+ __io uint32 MAPR; /**< AF remap and debug I/O configuration
+ register. */
+ __io uint32 EXTICR1; /**< External interrupt configuration
+ register 1. */
+ __io uint32 EXTICR2; /**< External interrupt configuration
+ register 2. */
+ __io uint32 EXTICR3; /**< External interrupt configuration
+ register 3. */
+ __io uint32 EXTICR4; /**< External interrupt configuration
+ register 4. */
+ __io uint32 MAPR2; /**< AF remap and debug I/O configuration
+ register 2. */
+} afio_reg_map;
+
+/** AFIO register map base pointer. */
+#define AFIO_BASE ((afio_reg_map *)0x40010000)
+
+/*
+ * AFIO register bit definitions
+ */
+
+/* Event control register */
+#define AFIO_EVCR_EVOE (0x1 << 7)
+#define AFIO_EVCR_PORT_PA (0x0 << 4)
+#define AFIO_EVCR_PORT_PB (0x1 << 4)
+#define AFIO_EVCR_PORT_PC (0x2 << 4)
+#define AFIO_EVCR_PORT_PD (0x3 << 4)
+#define AFIO_EVCR_PORT_PE (0x4 << 4)
+#define AFIO_EVCR_PIN_0 0x0
+#define AFIO_EVCR_PIN_1 0x1
+#define AFIO_EVCR_PIN_2 0x2
+#define AFIO_EVCR_PIN_3 0x3
+#define AFIO_EVCR_PIN_4 0x4
+#define AFIO_EVCR_PIN_5 0x5
+#define AFIO_EVCR_PIN_6 0x6
+#define AFIO_EVCR_PIN_7 0x7
+#define AFIO_EVCR_PIN_8 0x8
+#define AFIO_EVCR_PIN_9 0x9
+#define AFIO_EVCR_PIN_10 0xA
+#define AFIO_EVCR_PIN_11 0xB
+#define AFIO_EVCR_PIN_12 0xC
+#define AFIO_EVCR_PIN_13 0xD
+#define AFIO_EVCR_PIN_14 0xE
+#define AFIO_EVCR_PIN_15 0xF
+
+/* AF remap and debug I/O configuration register */
+#define AFIO_MAPR_SWJ_CFG (0x7 << 24)
+#define AFIO_MAPR_SWJ_CFG_FULL_SWJ (0x0 << 24)
+#define AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_NJRST (0x1 << 24)
+#define AFIO_MAPR_SWJ_CFG_NO_JTAG_SW (0x2 << 24)
+#define AFIO_MAPR_SWJ_CFG_NO_JTAG_NO_SW (0x4 << 24)
+#define AFIO_MAPR_ADC2_ETRGREG_REMAP BIT(20)
+#define AFIO_MAPR_ADC2_ETRGINJ_REMAP BIT(19)
+#define AFIO_MAPR_ADC1_ETRGREG_REMAP BIT(18)
+#define AFIO_MAPR_ADC1_ETRGINJ_REMAP BIT(17)
+#define AFIO_MAPR_TIM5CH4_IREMAP BIT(16)
+#define AFIO_MAPR_PD01_REMAP BIT(15)
+#define AFIO_MAPR_CAN_REMAP (0x3 << 13)
+#define AFIO_MAPR_TIM4_REMAP BIT(12)
+#define AFIO_MAPR_TIM3_REMAP (0x3 << 10)
+#define AFIO_MAPR_TIM2_REMAP (0x3 << 8)
+#define AFIO_MAPR_TIM1_REMAP (0x3 << 6)
+#define AFIO_MAPR_USART3_REMAP (0x3 << 4)
+#define AFIO_MAPR_USART2_REMAP BIT(3)
+#define AFIO_MAPR_USART1_REMAP BIT(2)
+#define AFIO_MAPR_I2C1_REMAP BIT(1)
+#define AFIO_MAPR_SPI1_REMAP BIT(0)
+
+/* External interrupt configuration registers */
+
+/**
+ * External interrupt line numbers.
+ */
+typedef enum {
+ AFIO_EXTI_0, /**< External interrupt line 0. */
+ AFIO_EXTI_1, /**< External interrupt line 1. */
+ AFIO_EXTI_2, /**< External interrupt line 2. */
+ AFIO_EXTI_3, /**< External interrupt line 3. */
+ AFIO_EXTI_4, /**< External interrupt line 4. */
+ AFIO_EXTI_5, /**< External interrupt line 5. */
+ AFIO_EXTI_6, /**< External interrupt line 6. */
+ AFIO_EXTI_7, /**< External interrupt line 7. */
+ AFIO_EXTI_8, /**< External interrupt line 8. */
+ AFIO_EXTI_9, /**< External interrupt line 9. */
+ AFIO_EXTI_10, /**< External interrupt line 10. */
+ AFIO_EXTI_11, /**< External interrupt line 11. */
+ AFIO_EXTI_12, /**< External interrupt line 12. */
+ AFIO_EXTI_13, /**< External interrupt line 13. */
+ AFIO_EXTI_14, /**< External interrupt line 14. */
+ AFIO_EXTI_15, /**< External interrupt line 15. */
+} afio_exti_num;
+
+/* AF remap and debug I/O configuration register 2 */
+#define AFIO_MAPR2_FSMC_NADV BIT(10)
+#define AFIO_MAPR2_TIM14_REMAP BIT(9)
+#define AFIO_MAPR2_TIM13_REMAP BIT(8)
+#define AFIO_MAPR2_TIM11_REMAP BIT(7)
+#define AFIO_MAPR2_TIM10_REMAP BIT(6)
+#define AFIO_MAPR2_TIM9_REMAP BIT(5)
+
+/*
+ * AFIO convenience routines
+ */
+
+void afio_init(void);
+void afio_exti_select(afio_exti_num exti, afio_exti_port gpio_port);
+
+/**
+ * @brief Debug port configuration
+ *
+ * Used to configure the behavior of JTAG and Serial Wire (SW) debug
+ * ports and their associated GPIO pins.
+ */
+typedef enum afio_debug_cfg {
+ AFIO_DEBUG_FULL_SWJ = AFIO_MAPR_SWJ_CFG_FULL_SWJ, /**<
+ Full Serial Wire and JTAG debug */
+ AFIO_DEBUG_FULL_SWJ_NO_NJRST = AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_NJRST, /**<
+ Full Serial Wire and JTAG, but no NJTRST. */
+ AFIO_DEBUG_SW_ONLY = AFIO_MAPR_SWJ_CFG_NO_JTAG_SW, /**<
+ Serial Wire debug only (JTAG-DP disabled,
+ SW-DP enabled) */
+ AFIO_DEBUG_NONE = AFIO_MAPR_SWJ_CFG_NO_JTAG_NO_SW /**<
+ No debug; all JTAG and SW pins are free
+ for use as GPIOs. */
+} afio_debug_cfg;
+
+/**
+ * @brief Enable or disable the JTAG and SW debug ports.
+ * @param config Desired debug port configuration
+ * @see afio_debug_cfg
+ */
+static inline void afio_cfg_debug_ports(afio_debug_cfg config) {
+ __io uint32 *mapr = &AFIO_BASE->MAPR;
+ *mapr = (*mapr & ~AFIO_MAPR_SWJ_CFG) | config;
}
#ifdef __cplusplus
-} // extern "C"
+}
#endif
-
#endif
diff --git a/libmaple/i2c.c b/libmaple/i2c.c
new file mode 100644
index 0000000..f4cb522
--- /dev/null
+++ b/libmaple/i2c.c
@@ -0,0 +1,446 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * 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 i2c.c
+ * @brief Inter-Integrated Circuit (I2C) support.
+ */
+
+#include "libmaple.h"
+#include "rcc.h"
+#include "nvic.h"
+#include "gpio.h"
+#include "nvic.h"
+#include "i2c.h"
+#include "string.h"
+
+static inline int32 wait_for_state_change(i2c_dev *dev, i2c_state state);
+
+static i2c_dev i2c_dev1 = {
+ .regs = I2C1_BASE,
+ .gpio_port = &gpiob,
+ .sda_pin = 7,
+ .scl_pin = 6,
+ .clk_line = RCC_I2C1,
+ .ev_nvic_line = NVIC_I2C1_EV,
+ .er_nvic_line = NVIC_I2C1_ER,
+ .state = I2C_STATE_IDLE
+};
+
+i2c_dev* const I2C1 = &i2c_dev1;
+
+static i2c_dev i2c_dev2 = {
+ .regs = I2C2_BASE,
+ .gpio_port = &gpiob,
+ .sda_pin = 11,
+ .scl_pin = 10,
+ .clk_line = RCC_I2C2,
+ .ev_nvic_line = NVIC_I2C2_EV,
+ .er_nvic_line = NVIC_I2C2_ER,
+ .state = I2C_STATE_IDLE
+};
+
+i2c_dev* const I2C2 = &i2c_dev2;
+
+struct crumb {
+ uint32 event;
+ uint32 sr1;
+ uint32 sr2;
+};
+
+#define NR_CRUMBS 128
+static struct crumb crumbs[NR_CRUMBS];
+static uint32 cur_crumb = 0;
+
+static inline void leave_big_crumb(uint32 event, uint32 sr1, uint32 sr2) {
+ if (cur_crumb < NR_CRUMBS) {
+ struct crumb *crumb = &crumbs[cur_crumb++];
+ crumb->event = event;
+ crumb->sr1 = sr1;
+ crumb->sr2 = sr2;
+ }
+}
+
+enum {
+ IRQ_ENTRY = 1,
+ TXE_ONLY = 2,
+ TXE_BTF = 3,
+ STOP_SENT = 4,
+ TEST = 5,
+ RX_ADDR_START = 6,
+ RX_ADDR_STOP = 7,
+ RXNE_ONLY = 8,
+ RXNE_SENDING = 9,
+ RXNE_START_SENT = 10,
+ RXNE_STOP_SENT = 11,
+ RXNE_DONE = 12,
+ ERROR_ENTRY = 13,
+};
+
+/**
+ * @brief IRQ handler for i2c master. Handles transmission/reception.
+ * @param dev i2c device
+ */
+static void i2c_irq_handler(i2c_dev *dev) {
+ i2c_msg *msg = dev->msg;
+
+ uint8 read = msg->flags & I2C_MSG_READ;
+
+ uint32 sr1 = dev->regs->SR1;
+ uint32 sr2 = dev->regs->SR2;
+ leave_big_crumb(IRQ_ENTRY, sr1, sr2);
+
+ /*
+ * EV5: Start condition sent
+ */
+ if (sr1 & I2C_SR1_SB) {
+ msg->xferred = 0;
+ i2c_enable_irq(dev, I2C_IRQ_BUFFER);
+
+ /*
+ * Master receiver
+ */
+ if (read) {
+ i2c_enable_ack(dev);
+ }
+
+ i2c_send_slave_addr(dev, msg->addr, read);
+ sr1 = sr2 = 0;
+ }
+
+ /*
+ * EV6: Slave address sent
+ */
+ if (sr1 & I2C_SR1_ADDR) {
+ /*
+ * Special case event EV6_1 for master receiver.
+ * Generate NACK and restart/stop condition after ADDR
+ * is cleared.
+ */
+ if (read) {
+ if (msg->length == 1) {
+ i2c_disable_ack(dev);
+ if (dev->msgs_left > 1) {
+ i2c_start_condition(dev);
+ leave_big_crumb(RX_ADDR_START, 0, 0);
+ } else {
+ i2c_stop_condition(dev);
+ leave_big_crumb(RX_ADDR_STOP, 0, 0);
+ }
+ }
+ } else {
+ /*
+ * Master transmitter: write first byte to fill shift
+ * register. We should get another TXE interrupt
+ * immediately to fill DR again.
+ */
+ if (msg->length != 1)
+ i2c_write(dev, msg->data[msg->xferred++]);
+ }
+ sr1 = sr2 = 0;
+ }
+
+ /*
+ * EV8: Master transmitter
+ * Transmit buffer empty, but we haven't finished transmitting the last
+ * byte written.
+ */
+ if ((sr1 & I2C_SR1_TXE) && !(sr1 & I2C_SR1_BTF)) {
+ leave_big_crumb(TXE_ONLY, 0, 0);
+ if (dev->msgs_left) {
+ i2c_write(dev, msg->data[msg->xferred++]);
+ if (msg->xferred == msg->length) {
+ /*
+ * End of this message. Turn off TXE/RXNE and wait for
+ * BTF to send repeated start or stop condition.
+ */
+ i2c_disable_irq(dev, I2C_IRQ_BUFFER);
+ dev->msgs_left--;
+ }
+ } else {
+ /*
+ * This should be impossible...
+ */
+ throb();
+ }
+ sr1 = sr2 = 0;
+ }
+
+ /*
+ * EV8_2: Master transmitter
+ * Last byte sent, program repeated start/stop
+ */
+ if ((sr1 & I2C_SR1_TXE) && (sr1 & I2C_SR1_BTF)) {
+ leave_big_crumb(TXE_BTF, 0, 0);
+ if (dev->msgs_left) {
+ leave_big_crumb(TEST, 0, 0);
+ /*
+ * Repeated start insanity: We can't disable ITEVTEN or else SB
+ * won't interrupt, but if we don't disable ITEVTEN, BTF will
+ * continually interrupt us. What the fuck ST?
+ */
+ i2c_start_condition(dev);
+ while (!(dev->regs->SR1 & I2C_SR1_SB))
+ ;
+ dev->msg++;
+ } else {
+ i2c_stop_condition(dev);
+
+ /*
+ * Turn off event interrupts to keep BTF from firing until
+ * the end of the stop condition. Why on earth they didn't
+ * have a start/stop condition request clear BTF is beyond
+ * me.
+ */
+ i2c_disable_irq(dev, I2C_IRQ_EVENT);
+ leave_big_crumb(STOP_SENT, 0, 0);
+ dev->state = I2C_STATE_XFER_DONE;
+ }
+ sr1 = sr2 = 0;
+ }
+
+ /*
+ * EV7: Master Receiver
+ */
+ if (sr1 & I2C_SR1_RXNE) {
+ leave_big_crumb(RXNE_ONLY, 0, 0);
+ msg->data[msg->xferred++] = dev->regs->DR;
+
+ /*
+ * EV7_1: Second to last byte in the reception? Set NACK and generate
+ * stop/restart condition in time for the last byte. We'll get one more
+ * RXNE interrupt before shutting things down.
+ */
+ if (msg->xferred == (msg->length - 1)) {
+ i2c_disable_ack(dev);
+ if (dev->msgs_left > 2) {
+ i2c_start_condition(dev);
+ leave_big_crumb(RXNE_START_SENT, 0, 0);
+ } else {
+ i2c_stop_condition(dev);
+ leave_big_crumb(RXNE_STOP_SENT, 0, 0);
+ }
+ } else if (msg->xferred == msg->length) {
+ dev->msgs_left--;
+ if (dev->msgs_left == 0) {
+ /*
+ * We're done.
+ */
+ leave_big_crumb(RXNE_DONE, 0, 0);
+ dev->state = I2C_STATE_XFER_DONE;
+ } else {
+ dev->msg++;
+ }
+ }
+ }
+}
+
+void __irq_i2c1_ev(void) {
+ i2c_irq_handler(&i2c_dev1);
+}
+
+void __irq_i2c2_ev(void) {
+ i2c_irq_handler(&i2c_dev2);
+}
+
+static void i2c_irq_error_handler(i2c_dev *dev) {
+ uint32 sr1 = dev->regs->SR1;
+ uint32 sr2 = dev->regs->SR2;
+ leave_big_crumb(ERROR_ENTRY, sr1, sr2);
+
+ i2c_stop_condition(dev);
+ i2c_disable_irq(dev, I2C_IRQ_BUFFER | I2C_IRQ_EVENT | I2C_IRQ_ERROR);
+ dev->state = I2C_STATE_ERROR;
+}
+
+void __irq_i2c1_er(void) {
+ i2c_irq_error_handler(&i2c_dev1);
+}
+
+void __irq_i2c2_er(void) {
+ i2c_irq_error_handler(&i2c_dev2);
+}
+
+static void i2c_bus_reset(const i2c_dev *dev) {
+ /* Release both lines */
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 1);
+ gpio_write_bit(dev->gpio_port, dev->sda_pin, 1);
+ gpio_set_mode(dev->gpio_port, dev->scl_pin, GPIO_OUTPUT_OD);
+ gpio_set_mode(dev->gpio_port, dev->sda_pin, GPIO_OUTPUT_OD);
+
+ /*
+ * Make sure the bus is free by clocking it until any slaves release the
+ * bus.
+ */
+ while (!gpio_read_bit(dev->gpio_port, dev->sda_pin)) {
+ /* Wait for any clock stretching to finish */
+ while (!gpio_read_bit(dev->gpio_port, dev->scl_pin))
+ ;
+ delay_us(10);
+
+ /* Pull low */
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 0);
+ delay_us(10);
+
+ /* Release high again */
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 1);
+ delay_us(10);
+ }
+
+ /* Generate start then stop condition */
+ gpio_write_bit(dev->gpio_port, dev->sda_pin, 0);
+ delay_us(10);
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 0);
+ delay_us(10);
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 1);
+ delay_us(10);
+ gpio_write_bit(dev->gpio_port, dev->sda_pin, 1);
+}
+
+/**
+ * @brief Initialize an I2C device and reset its registers to their
+ * default values.
+ * @param dev Device to enable.
+ */
+void i2c_init(i2c_dev *dev) {
+ rcc_reset_dev(dev->clk_line);
+ rcc_clk_enable(dev->clk_line);
+}
+
+/**
+ * @brief Initialize an i2c device as bus master
+ * @param dev Device to enable
+ * @param flags Bitwise or of the following I2C options:
+ * I2C_FAST_MODE: 400 khz operation
+ * I2C_10BIT_ADDRESSING: Enable 10-bit addressing
+ */
+void i2c_master_enable(i2c_dev *dev, uint32 flags) {
+#define I2C_CLK (PCLK1/1000000)
+#define STANDARD_CCR (PCLK1/(100000*2))
+#define STANDARD_TRISE (I2C_CLK+1)
+#define FAST_CCR (I2C_CLK/10)
+#define FAST_TRISE ((I2C_CLK*3)/10+1)
+ /* Reset the bus. Clock out any hung slaves. */
+ i2c_bus_reset(dev);
+
+ /* Turn on clock and set GPIO modes */
+ i2c_init(dev);
+ gpio_set_mode(dev->gpio_port, dev->sda_pin, GPIO_AF_OUTPUT_OD);
+ gpio_set_mode(dev->gpio_port, dev->scl_pin, GPIO_AF_OUTPUT_OD);
+
+ /* I2C1 and I2C2 are fed from APB1, clocked at 36MHz */
+ i2c_set_input_clk(dev, I2C_CLK);
+
+ if(flags & I2C_FAST_MODE) {
+ /* 400 kHz for fast mode, set DUTY and F/S bits */
+ i2c_set_clk_control(dev, FAST_CCR|I2C_CCR_DUTY|I2C_CCR_FS);
+
+ /* Set scl rise time, max rise time in fast mode: 300ns */
+ i2c_set_trise(dev, FAST_TRISE);
+
+ } else {
+
+ /* 100 kHz for standard mode */
+ i2c_set_clk_control(dev, STANDARD_CCR);
+
+ /* Max rise time in standard mode: 1000 ns */
+ i2c_set_trise(dev, STANDARD_TRISE);
+ }
+
+ /* Enable event and buffer interrupts */
+ nvic_irq_enable(dev->ev_nvic_line);
+ nvic_irq_enable(dev->er_nvic_line);
+ i2c_enable_irq(dev, I2C_IRQ_EVENT | I2C_IRQ_BUFFER | I2C_IRQ_ERROR);
+
+ /*
+ * Important STM32 Errata:
+ *
+ * See STM32F10xx8 and STM32F10xxB Errata sheet (Doc ID 14574 Rev 8),
+ * Section 2.11.1, 2.11.2.
+ *
+ * 2.11.1:
+ * When the EV7, EV7_1, EV6_1, EV6_3, EV2, EV8, and EV3 events are not
+ * managed before the current byte is being transferred, problems may be
+ * encountered such as receiving an extra byte, reading the same data twice
+ * or missing data.
+ *
+ * 2.11.2:
+ * In Master Receiver mode, when closing the communication using
+ * method 2, the content of the last read data can be corrupted.
+ *
+ * If the user software is not able to read the data N-1 before the STOP
+ * condition is generated on the bus, the content of the shift register
+ * (data N) will be corrupted. (data N is shifted 1-bit to the left).
+ *
+ * ----------------------------------------------------------------------
+ *
+ * In order to ensure that events are not missed, the i2c interrupt must
+ * not be preempted. We set the i2c interrupt priority to be the highest
+ * interrupt in the system (priority level 0). All other interrupts have
+ * been initialized to priority level 16. See nvic_init().
+ */
+ nvic_irq_set_priority(dev->ev_nvic_line, 0);
+ nvic_irq_set_priority(dev->er_nvic_line, 0);
+
+ /* Make it go! */
+ i2c_peripheral_enable(dev);
+}
+
+int32 i2c_master_xfer(i2c_dev *dev, i2c_msg *msgs, uint16 num) {
+ int32 rc;
+
+ dev->msg = msgs;
+ dev->msgs_left = num;
+
+ while (dev->regs->SR2 & I2C_SR2_BUSY)
+ ;
+
+ dev->state = I2C_STATE_BUSY;
+ i2c_enable_irq(dev, I2C_IRQ_EVENT);
+
+ i2c_start_condition(dev);
+ rc = wait_for_state_change(dev, I2C_STATE_XFER_DONE);
+ if (rc < 0) {
+ goto out;
+ }
+
+ dev->state = I2C_STATE_IDLE;
+ rc = num;
+out:
+ return rc;
+}
+
+static inline int32 wait_for_state_change(i2c_dev *dev, i2c_state state) {
+ int32 rc;
+ i2c_state tmp;
+
+ while (1) {
+ tmp = dev->state;
+ if ((tmp == state) || (tmp == I2C_STATE_ERROR)) {
+ return (tmp == I2C_STATE_ERROR) ? -1 : 0;
+ }
+ }
+}
diff --git a/libmaple/i2c.h b/libmaple/i2c.h
new file mode 100644
index 0000000..3f351b2
--- /dev/null
+++ b/libmaple/i2c.h
@@ -0,0 +1,261 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * 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 i2c.h
+ * @brief Inter-Integrated Circuit (I2C) peripheral support
+ */
+
+#ifndef _I2C_H_
+#define _I2C_H_
+
+typedef struct i2c_reg_map {
+ __io uint32 CR1;
+ __io uint32 CR2;
+ __io uint32 OAR1;
+ __io uint32 OAR2;
+ __io uint32 DR;
+ __io uint32 SR1;
+ __io uint32 SR2;
+ __io uint32 CCR;
+ __io uint32 TRISE;
+} i2c_reg_map;
+
+typedef enum i2c_state {
+ I2C_STATE_IDLE,
+ I2C_STATE_XFER_DONE,
+ I2C_STATE_BUSY,
+ I2C_STATE_ERROR = -1
+} i2c_state;
+
+typedef struct i2c_msg {
+ uint16 addr;
+#define I2C_MSG_READ 0x1
+#define I2C_MSG_10BIT_ADDR 0x2
+ uint16 flags;
+ uint16 length;
+ uint16 xferred;
+ uint8 *data;
+} i2c_msg;
+
+typedef struct i2c_dev {
+ i2c_reg_map *regs;
+ gpio_dev *gpio_port;
+ uint8 sda_pin;
+ uint8 scl_pin;
+ uint8 clk_line;
+ uint8 ev_nvic_line;
+ uint8 er_nvic_line;
+ volatile uint8 state;
+ uint16 msgs_left;
+ i2c_msg *msg;
+} i2c_dev;
+
+extern i2c_dev* const I2C1;
+extern i2c_dev* const I2C2;
+
+#define I2C1_BASE (i2c_reg_map*)0x40005400
+#define I2C2_BASE (i2c_reg_map*)0x40005800
+
+/* i2c enable options */
+#define I2C_FAST_MODE 0x1 // 400 khz
+#define I2C_DUTY_16_9 0x2 // 16/9 duty ratio
+
+/* Control register 1 bits */
+#define I2C_CR1_SWRST BIT(15) // Software reset
+#define I2C_CR1_ALERT BIT(13) // SMBus alert
+#define I2C_CR1_PEC BIT(12) // Packet error checking
+#define I2C_CR1_POS BIT(11) // Acknowledge/PEC position
+#define I2C_CR1_ACK BIT(10) // Acknowledge enable
+#define I2C_CR1_START BIT(8) // Start generation
+#define I2C_CR1_STOP BIT(9) // Stop generation
+#define I2C_CR1_PE BIT(0) // Peripheral Enable
+
+/* Control register 2 bits */
+#define I2C_CR2_LAST BIT(12) // DMA last transfer
+#define I2C_CR2_DMAEN BIT(11) // DMA requests enable
+#define I2C_CR2_ITBUFEN BIT(10) // Buffer interrupt enable
+#define I2C_CR2_ITEVTEN BIT(9) // Event interupt enable
+#define I2C_CR2_ITERREN BIT(8) // Error interupt enable
+#define I2C_CR2_FREQ 0xFFF // Peripheral input frequency
+
+/* Clock control register bits */
+#define I2C_CCR_FS BIT(15) // Fast mode selection
+#define I2C_CCR_DUTY BIT(14) // 16/9 duty ratio
+#define I2C_CCR_CCR 0xFFF // Clock control bits
+
+/* Status register 1 bits */
+#define I2C_SR1_SB BIT(0) // Start bit
+#define I2C_SR1_ADDR BIT(1) // Address sent/matched
+#define I2C_SR1_BTF BIT(2) // Byte transfer finished
+#define I2C_SR1_ADD10 BIT(3) // 10-bit header sent
+#define I2C_SR1_STOPF BIT(4) // Stop detection
+#define I2C_SR1_RXNE BIT(6) // Data register not empty
+#define I2C_SR1_TXE BIT(7) // Data register empty
+#define I2C_SR1_BERR BIT(8) // Bus error
+#define I2C_SR1_ARLO BIT(9) // Arbitration lost
+#define I2C_SR1_AF BIT(10) // Acknowledge failure
+#define I2C_SR1_OVR BIT(11) // Overrun/underrun
+#define I2C_SR1_PECERR BIT(12) // PEC Error in reception
+#define I2C_SR1_TIMEOUT BIT(14) // Timeout or Tlow error
+#define I2C_SR1_SMBALERT BIT(15) // SMBus alert
+
+/* Status register 2 bits */
+#define I2C_SR2_MSL BIT(0) // Master/slave
+#define I2C_SR2_BUSY BIT(1) // Bus busy
+#define I2C_SR2_TRA BIT(2) // Transmitter/receiver
+#define I2C_SR2_GENCALL BIT(4) // General call address
+#define I2C_SR2_SMBDEFAULT BIT(5) // SMBus device default address
+#define I2C_SR2_SMBHOST BIT(6) // SMBus host header
+#define I2C_SR2_DUALF BIT(7) // Dual flag
+#define I2C_SR2_PEC 0xFF00 // Packet error checking register
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void i2c_init(i2c_dev *dev);
+void i2c_master_enable(i2c_dev *dev, uint32 flags);
+int32 i2c_master_xfer(i2c_dev *dev, i2c_msg *msgs, uint16 num);
+
+/*
+ * Low level register twiddling functions
+ */
+
+/**
+ * @brief turn on an i2c peripheral
+ * @param dev i2c device
+ */
+static inline void i2c_peripheral_enable(i2c_dev *dev) {
+ dev->regs->CR1 |= I2C_CR1_PE;
+}
+
+/**
+ * @brief turn off an i2c peripheral
+ * @param dev i2c device
+ */
+static inline void i2c_peripheral_disable(i2c_dev *dev) {
+ dev->regs->CR1 &= ~I2C_CR1_PE;
+}
+
+/**
+ * @brief Fill transmit register
+ * @param dev i2c device
+ * @param byte byte to write
+ */
+static inline void i2c_write(i2c_dev *dev, uint8 byte) {
+ dev->regs->DR = byte;
+}
+
+/**
+ * @brief Set input clock frequency, in mhz
+ * @param dev i2c
+ * @param freq frequency in megahertz (2-36)
+ */
+static inline void i2c_set_input_clk(i2c_dev *dev, uint32 freq) {
+ uint32 cr2 = dev->regs->CR2;
+ cr2 &= ~I2C_CR2_FREQ;
+ cr2 |= freq;
+ dev->regs->CR2 = freq;
+}
+
+/**
+ * @brief Set i2c clock control register. See RM008
+ * @param dev i2c device
+ * @return
+ * @sideeffect
+ */
+static inline void i2c_set_clk_control(i2c_dev *dev, uint32 val) {
+ uint32 ccr = dev->regs->CCR;
+ ccr &= ~I2C_CCR_CCR;
+ ccr |= val;
+ dev->regs->CCR = ccr;
+}
+
+static inline void i2c_set_fast_mode(i2c_dev *dev) {
+ dev->regs->CCR |= I2C_CCR_FS;
+}
+
+static inline void i2c_set_standard_mode(i2c_dev *dev) {
+ dev->regs->CCR &= ~I2C_CCR_FS;
+}
+
+/**
+ * @brief Set SCL rise time
+ * @param
+ */
+static inline void i2c_set_trise(i2c_dev *dev, uint32 trise) {
+ dev->regs->TRISE = trise;
+}
+
+static inline void i2c_start_condition(i2c_dev *dev) {
+ uint32 cr1;
+ while ((cr1 = dev->regs->CR1) & (I2C_CR1_START |
+ I2C_CR1_STOP |
+ I2C_CR1_PEC)) {
+ ;
+ }
+ dev->regs->CR1 |= I2C_CR1_START;
+}
+
+static inline void i2c_stop_condition(i2c_dev *dev) {
+ uint32 cr1;
+ while ((cr1 = dev->regs->CR1) & (I2C_CR1_START |
+ I2C_CR1_STOP |
+ I2C_CR1_PEC)) {
+ ;
+ }
+ dev->regs->CR1 |= I2C_CR1_STOP;
+}
+
+static inline void i2c_send_slave_addr(i2c_dev *dev, uint32 addr, uint32 rw) {
+ dev->regs->DR = (addr << 1) | rw;
+}
+
+#define I2C_IRQ_ERROR I2C_CR2_ITERREN
+#define I2C_IRQ_EVENT I2C_CR2_ITEVTEN
+#define I2C_IRQ_BUFFER I2C_CR2_ITBUFEN
+static inline void i2c_enable_irq(i2c_dev *dev, uint32 irqs) {
+ dev->regs->CR2 |= irqs;
+}
+
+static inline void i2c_disable_irq(i2c_dev *dev, uint32 irqs) {
+ dev->regs->CR2 &= ~irqs;
+}
+
+static inline void i2c_enable_ack(i2c_dev *dev) {
+ dev->regs->CR1 |= I2C_CR1_ACK;
+}
+
+static inline void i2c_disable_ack(i2c_dev *dev) {
+ dev->regs->CR1 &= ~I2C_CR1_ACK;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libmaple/iwdg.c b/libmaple/iwdg.c
index 82a0151..3d67ee8 100644
--- a/libmaple/iwdg.c
+++ b/libmaple/iwdg.c
@@ -23,34 +23,37 @@
*****************************************************************************/
/**
- * @file iwdg.c
- *
- * @brief Independent watchdog support
+ * @file iwdg.c
+ * @brief Independent watchdog (IWDG) support
*/
-#include "libmaple.h"
#include "iwdg.h"
-#define IWDG_UNLOCK 0x5555
-#define IWDG_START 0xCCCC
-#define IWDG_FEED 0xAAAA
-
/**
- * @brief Initialise and start the watchdog
+ * @brief Initialise and start the watchdog
+ *
+ * The prescaler and reload set the timeout. A prescaler of 3 divides
+ * the 40 kHz clock by 32 and gives roughly 1 ms per reload.
*
- * The prescaler and reload set the timeout. A prescaler of 3 divides
- * the 40 kHz clock by 32 and gives roughly 1 ms per reload.
+ * @param prescaler Prescaler for the 40 KHz IWDG clock.
+ * @param reload Independent watchdog counter reload value.
*/
-void iwdg_init(uint8 prescaler, uint16 reload) {
- __write(IWDG_KR, IWDG_UNLOCK);
- __write(IWDG_PR, prescaler);
- __write(IWDG_RLR, reload);
+void iwdg_init(iwdg_prescaler prescaler, uint16 reload) {
+ IWDG_BASE->KR = IWDG_KR_UNLOCK;
+ IWDG_BASE->PR = prescaler;
+ IWDG_BASE->RLR = reload;
- /* Start things off */
- __write(IWDG_KR, IWDG_START);
- __write(IWDG_KR, IWDG_FEED);
+ /* Start things off */
+ IWDG_BASE->KR = IWDG_KR_START;
+ iwdg_feed();
}
+/**
+ * @brief Reset the IWDG counter.
+ *
+ * Calling this function will cause the IWDG counter to be reset to
+ * its reload value.
+ */
void iwdg_feed(void) {
- __write(IWDG_KR, IWDG_FEED);
+ IWDG_BASE->KR = IWDG_KR_FEED;
}
diff --git a/libmaple/iwdg.h b/libmaple/iwdg.h
index 4ab0ddf..e0a2d62 100644
--- a/libmaple/iwdg.h
+++ b/libmaple/iwdg.h
@@ -23,35 +23,83 @@
*****************************************************************************/
/**
- * @file iwdg.h
+ * @file iwdg.h
+ * @author Michael Hope, Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief Independent watchdog support.
*
- * @brief Independent watchdog support
+ * To use the independent watchdog, first call iwdg_init() with the
+ * appropriate prescaler and IWDG counter reload values for your
+ * application. Afterwards, you must periodically call iwdg_feed()
+ * before the IWDG counter reaches 0 to reset the counter to its
+ * reload value. If you do not, the chip will reset.
+ *
+ * Once started, the independent watchdog cannot be turned off.
*/
#ifndef _IWDG_H_
#define _IWDG_H_
+#include "libmaple_types.h"
+#include "util.h"
+
#ifdef __cplusplus
extern "C"{
#endif
-#define IWDG_BASE 0x40003000
-#define IWDG_KR (IWDG_BASE + 0x0)
-#define IWDG_PR (IWDG_BASE + 0x4)
-#define IWDG_RLR (IWDG_BASE + 0x8)
-#define IWDG_SR (IWDG_BASE + 0xC)
-
-enum {
- IWDG_PRE_4,
- IWDG_PRE_8,
- IWDG_PRE_16,
- IWDG_PRE_32,
- IWDG_PRE_64,
- IWDG_PRE_128,
- IWDG_PRE_256
-};
-
-void iwdg_init(uint8 prescaler, uint16 reload);
+/*
+ * Register map
+ */
+
+/** Independent watchdog register map type. */
+typedef struct iwdg_reg_map {
+ __io uint32 KR; /**< Key register. */
+ __io uint32 PR; /**< Prescaler register. */
+ __io uint32 RLR; /**< Reload register. */
+ __io uint32 SR; /**< Status register */
+} iwdg_reg_map;
+
+#define IWDG_BASE ((struct iwdg_reg_map*)0x40003000)
+
+/*
+ * Register bit definitions
+ */
+
+/* Key register */
+
+#define IWDG_KR_UNLOCK 0x5555
+#define IWDG_KR_FEED 0xAAAA
+#define IWDG_KR_START 0xCCCC
+
+/* Prescaler register */
+
+#define IWDG_PR_DIV_4 0x0
+#define IWDG_PR_DIV_8 0x1
+#define IWDG_PR_DIV_16 0x2
+#define IWDG_PR_DIV_32 0x3
+#define IWDG_PR_DIV_64 0x4
+#define IWDG_PR_DIV_128 0x5
+#define IWDG_PR_DIV_256 0x6
+
+/* Status register */
+
+#define IWDG_SR_RVU_BIT 1
+#define IWDG_SR_PVU_BIT 0
+
+#define IWDG_SR_RVU BIT(IWDG_SR_RVU_BIT)
+#define IWDG_SR_PVU BIT(IWDG_SR_PVU_BIT)
+
+/** Independent watchdog prescalers */
+typedef enum {
+ IWDG_PRE_4 = IWDG_PR_DIV_4,
+ IWDG_PRE_8 = IWDG_PR_DIV_8,
+ IWDG_PRE_16 = IWDG_PR_DIV_16,
+ IWDG_PRE_32 = IWDG_PR_DIV_32,
+ IWDG_PRE_64 = IWDG_PR_DIV_64,
+ IWDG_PRE_128 = IWDG_PR_DIV_128,
+ IWDG_PRE_256 = IWDG_PR_DIV_256
+} iwdg_prescaler;
+
+void iwdg_init(iwdg_prescaler prescaler, uint16 reload);
void iwdg_feed(void);
#ifdef __cplusplus
diff --git a/libmaple/libmaple.h b/libmaple/libmaple.h
index 02e27d3..8e11660 100644
--- a/libmaple/libmaple.h
+++ b/libmaple/libmaple.h
@@ -24,159 +24,57 @@
/**
* @file libmaple.h
- *
- * @brief general include file for libmaple
+ * @brief General include file for libmaple
*/
#ifndef _LIBMAPLE_H_
#define _LIBMAPLE_H_
#include "libmaple_types.h"
+#include "stm32.h"
+#include "util.h"
+#include "delay.h"
-/* General configuration */
-#define DEBUG_NONE 0
-#define DEBUG_FAULT 1
-#define DEBUG_ALL 2
-
-#ifndef DEBUG_LEVEL
-#define DEBUG_LEVEL DEBUG_ALL
-#endif
+/*
+ * Where to put usercode, based on space reserved for bootloader.
+ *
+ * FIXME this has no business being here
+ */
+#define USER_ADDR_ROM 0x08005000
+#define USER_ADDR_RAM 0x20000C00
+#define STACK_TOP 0x20000800
/* MCU-specific configuration */
#if defined(MCU_STM32F103RB)
/* e.g., LeafLabs Maple */
/* Number of GPIO ports (GPIOA, GPIOB, etc.) */
- #define NR_GPIO_PORTS 4
-
- /* Total number of GPIO pins */
- #define NR_GPIO_PINS 39
-
- /* Number of 16-bit backup registers */
- #define NR_BKP_REGS 10
-
- /* Number of timer devices ports, definitely used */
- #define NR_TIMERS 4
-
- /* Number of USART ports */
- #define NR_USART 3
-
- /* Has an FSMC bus? */
- #define NR_FSMC 0
-
- /* Has a DAC? */
- #define NR_DAC_PINS 0
-
- /* USB Identifier numbers */
- /* Descriptor strings must be modified by hand in
- usb/descriptors.c for now */
- #define VCOM_ID_VENDOR 0x1EAF
- #define VCOM_ID_PRODUCT 0x0004
- #define USB_DISC_BANK GPIOC_BASE
- #define USB_DISC_PIN 12
- #define USB_CONFIG_MAX_POWER (100 >> 1)
- #define RESET_DELAY (100)
-
- /* Where to put usercode (based on space reserved for bootloader) */
- #define USER_ADDR_ROM 0x08005000
- #define USER_ADDR_RAM 0x20000C00
- #define STACK_TOP 0x20000800
-
- /* Debug port settings (from ASSERT) */
- #define ERROR_LED_PORT GPIOB_BASE
- #define ERROR_LED_PIN 12
- #define ERROR_USART_NUM USART2
- #define ERROR_USART_BAUD 9600
- #define ERROR_TX_PORT GPIOA_BASE
- #define ERROR_TX_PIN 2
-
- /* Just in case, most boards have at least some memory */
- #ifndef RAMSIZE
- # define RAMSIZE (caddr_t)0x50000
- #endif
-
- /* Bitbanded Memory sections */
- #define BITBAND_SRAM_REF 0x20000000
- #define BITBAND_SRAM_BASE 0x22000000
- #define BITBAND_PERI_REF 0x40000000
- #define BITBAND_PERI_BASE 0x42000000
+ #define NR_GPIO_PORTS 4
+
+ /* SRAM size, in bytes */
+ #define SRAM_SIZE 0x5000
#elif defined(MCU_STM32F103ZE)
/* e.g., LeafLabs Maple Native */
- #define NR_GPIO_PORTS 7
- #define NR_GPIO_PINS 100
- #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_FSMC 1
- #define NR_DAC_PINS 2
-
- #define VCOM_ID_VENDOR 0x1EAF
- #define VCOM_ID_PRODUCT 0x0004
- #define USB_DISC_BANK GPIOB_BASE
- #define USB_DISC_PIN 8
- #define USB_CONFIG_MAX_POWER (100 >> 1)
- #define RESET_DELAY (100)
-
- #define USER_ADDR_ROM 0x08005000
- #define USER_ADDR_RAM 0x20000C00
- #define STACK_TOP 0x20000800
-
- #define ERROR_LED_PORT GPIOC_BASE
- #define ERROR_LED_PIN 15
- #define ERROR_USART_NUM USART1
- #define ERROR_USART_BAUD 9600
- #define ERROR_TX_PORT GPIOA_BASE
- #define ERROR_TX_PIN 10
-
- #ifndef RAMSIZE
- # define RAMSIZE (caddr_t)0x50000
- #endif
-
- #define BITBAND_SRAM_REF 0x20000000
- #define BITBAND_SRAM_BASE 0x22000000
- #define BITBAND_PERI_REF 0x40000000
- #define BITBAND_PERI_BASE 0x42000000
+ #define NR_GPIO_PORTS 7
+ #define SRAM_SIZE 0x10000
#elif defined(MCU_STM32F103CB)
/* e.g., LeafLabs Maple Mini */
- #define NR_GPIO_PORTS 3
- #define NR_GPIO_PINS 34
- #define NR_BKP_REGS 10 /* TODO test on Mini */
- #define NR_TIMERS 4
- #define NR_USART 3
- #define NR_FSMC 0
- #define NR_DAC_PINS 0
-
- #define VCOM_ID_VENDOR 0x1EAF
- #define VCOM_ID_PRODUCT 0x0005
- #define USB_DISC_BANK GPIOB_BASE
- #define USB_DISC_PIN 9
- #define USB_CONFIG_MAX_POWER (100 >> 1)
- #define RESET_DELAY 100
-
- #define USER_ADDR_ROM 0x08005000
- #define USER_ADDR_RAM 0x20000C00
- #define STACK_TOP 0x20000800
-
- #define ERROR_LED_PORT GPIOB_BASE
- #define ERROR_LED_PIN 12
- #define ERROR_USART_NUM USART2
- #define ERROR_USART_BAUD 9600
- #define ERROR_TX_PORT GPIOA_BASE
- #define ERROR_TX_PIN 2
-
- #ifndef RAMSIZE
- # define RAMSIZE (caddr_t)0x50000
- #endif
-
- /* Bitbanded Memory sections */
- #define BITBAND_SRAM_REF 0x20000000
- #define BITBAND_SRAM_BASE 0x22000000
- #define BITBAND_PERI_REF 0x40000000
- #define BITBAND_PERI_BASE 0x42000000
+ /* Note that this is not, strictly speaking, true. But only pins
+ 0 and 1 exist, and they're used for OSC on the Mini, so we'll
+ live with this for now. */
+ #define NR_GPIO_PORTS 3
+
+ #define SRAM_SIZE 0x5000
+
+#elif defined(MCU_STM32F103RE)
+ /* e.g., LeafLabs Maple RET6 edition */
+
+ #define NR_GPIO_PORTS 4
+ #define SRAM_SIZE 0x10000
#else
@@ -185,8 +83,5 @@
#endif
-/* Requires board configuration info */
-#include "util.h"
-
#endif
diff --git a/libmaple/libmaple_types.h b/libmaple/libmaple_types.h
index 8d216a8..54bef65 100644
--- a/libmaple/libmaple_types.h
+++ b/libmaple/libmaple_types.h
@@ -44,8 +44,7 @@ typedef long long int64;
typedef void (*voidFuncPtr)(void);
#define __io volatile
-
-#define ALWAYS_INLINE inline __attribute__((always_inline))
+#define __attr_flash __attribute__((section (".USER_FLASH")))
#ifndef NULL
#define NULL 0
diff --git a/libmaple/nvic.c b/libmaple/nvic.c
index b1da605..a7a6d8e 100644
--- a/libmaple/nvic.c
+++ b/libmaple/nvic.c
@@ -26,56 +26,57 @@
* @brief Nested interrupt controller routines
*/
-#include "libmaple.h"
#include "nvic.h"
-#include "systick.h"
-
-void nvic_set_vector_table(uint32 addr, uint32 offset) {
- __write(SCB_VTOR, (uint32)addr | (offset & 0x1FFFFF80));
-}
+#include "scb.h"
+#include "stm32.h"
/**
- * @brief turn on interrupt number n
- * @param n interrupt number
+ * @brief Set interrupt priority for an interrupt line
+ *
+ * Note: The STM32 only implements 4 bits of priority, ignoring the
+ * lower 4 bits. This means there are only 16 levels of priority.
+ * Bits[3:0] read as zero and ignore writes.
+ *
+ * @param irqn device to set
+ * @param priority Priority to set, 0 being highest priority and 15
+ * being lowest.
*/
-void nvic_irq_enable(uint32 n) {
- uint32 *iser = &((uint32*)NVIC_ISER0)[(n/32)];
- __write(iser, BIT(n % 32));
+void nvic_irq_set_priority(nvic_irq_num irqn, uint8 priority) {
+ if (irqn < 0) {
+ /* This interrupt is in the system handler block */
+ SCB_BASE->SHP[((uint32)irqn & 0xF) - 4] = (priority & 0xF) << 4;
+ } else {
+ NVIC_BASE->IP[irqn] = (priority & 0xF) << 4;
+ }
}
/**
- * @brief turn off interrupt number n
- * @param n interrupt number
+ * @brief Initialize the NVIC
+ * @param vector_table_address Vector table base address.
+ * @param offset Offset from vector_table_address. Some restrictions
+ * apply to the use of nonzero offsets; see ST RM0008
+ * and the ARM Cortex M3 Technical Reference Manual.
*/
-void nvic_irq_disable(uint32 n) {
- uint32 *icer = &((uint32*)NVIC_ICER0)[(n/32)];
- __write(icer, BIT(n % 32));
-}
+void nvic_init(uint32 vector_table_address, uint32 offset) {
+ uint32 i;
+
+ nvic_set_vector_table(vector_table_address, offset);
-void nvic_irq_disable_all(void) {
- /* Each ICER register contains 1 bit per interrupt. Writing a 1
- to that bit disables the corresponding interrupt. So each of
- the following lines disables up to 32 interrupts at a time.
- Since low, medium, and high-density devices all have less than
- 64 interrupts, this suffices. */
- /* TODO: fix for connectivity line: __write(NVIC_ICER2,1),
- requires connectivity line support in libmaple.h */
- __write(NVIC_ICER0, 0xFFFFFFFF);
- __write(NVIC_ICER1, 0xFFFFFFFF);
+ /*
+ * Lower priority level for all peripheral interrupts to lowest
+ * possible.
+ */
+ for (i = 0; i < NR_INTERRUPTS; i++) {
+ nvic_irq_set_priority((nvic_irq_num)i, 0xF);
+ }
+
+ /* Lower systick interrupt priority to lowest level */
+ nvic_irq_set_priority(NVIC_SYSTICK, 0xF);
}
/**
- * @brief Initialize the NVIC according to VECT_TAB_FLASH,
- * VECT_TAB_RAM, or VECT_TAB_BASE.
+ * Reset the vector table address.
*/
-void nvic_init(void) {
-#ifdef VECT_TAB_FLASH
- nvic_set_vector_table(USER_ADDR_ROM, 0x0);
-#elif defined VECT_TAB_RAM
- nvic_set_vector_table(USER_ADDR_RAM, 0x0);
-#elif defined VECT_TAB_BASE
- nvic_set_vector_table(((uint32)0x08000000), 0x0);
-#else
-#error "You must set a base address for the vector table!"
-#endif
+void nvic_set_vector_table(uint32 addr, uint32 offset) {
+ SCB_BASE->VTOR = addr | (offset & 0x1FFFFF80);
}
diff --git a/libmaple/nvic.h b/libmaple/nvic.h
index 6004c36..496c41b 100644
--- a/libmaple/nvic.h
+++ b/libmaple/nvic.h
@@ -24,73 +24,177 @@
/**
* @file nvic.h
- * @brief Nested interrupt controller defines and prototypes
+ * @brief Nested vector interrupt controller support.
*/
#ifndef _NVIC_H_
#define _NVIC_H_
+#include "libmaple_types.h"
+#include "util.h"
+
#ifdef __cplusplus
extern "C"{
#endif
-#define NVIC_INT_USBHP 19
-#define NVIC_INT_USBLP 20
-
-/* NVIC Interrupt Enable registers */
-#define NVIC_ISER0 0xE000E100
-#define NVIC_ISER1 0xE000E104
-/* NVIC_ISER2 only on connectivity line */
-
-/* NVIC Interrupt Clear registers */
-#define NVIC_ICER0 0xE000E180
-#define NVIC_ICER1 0xE000E184
-/* NVIC_ICER2 only on connectivity line */
-
-/* System control registers */
-#define SCB_VTOR 0xE000ED08 // Vector table offset register
-
-enum {
- NVIC_TIMER1 = 27,
- NVIC_TIMER2 = 28,
- NVIC_TIMER3 = 29,
- NVIC_TIMER4 = 30,
- NVIC_TIMER5 = 50, // high density only (Maple Native, Maple Audio)
- NVIC_TIMER6 = 54, // high density only
- NVIC_TIMER7 = 55, // high density only
- NVIC_TIMER8 = 46, // high density only
-
- NVIC_USART1 = 37,
- NVIC_USART2 = 38,
- NVIC_USART3 = 39,
- NVIC_UART4 = 52, // high density only
- NVIC_UART5 = 53, // high density only
-
- NVIC_EXTI0 = 6,
- NVIC_EXTI1 = 7,
- NVIC_EXTI2 = 8,
- NVIC_EXTI3 = 9,
- NVIC_EXTI4 = 10,
- NVIC_EXTI9_5 = 23,
- NVIC_EXTI15_10 = 40,
-
- NVIC_DMA_CH1 = 11,
- NVIC_DMA_CH2 = 12,
- NVIC_DMA_CH3 = 13,
- NVIC_DMA_CH4 = 14,
- NVIC_DMA_CH5 = 15,
- NVIC_DMA_CH6 = 16,
- NVIC_DMA_CH7 = 17
-};
-
-
-#define nvic_globalirq_enable() asm volatile("cpsie i")
-#define nvic_globalirq_disable() asm volatile("cpsid i")
-
-void nvic_init(void);
-void nvic_irq_enable(uint32 device);
-void nvic_irq_disable(uint32 device);
-void nvic_irq_disable_all(void);
+/** NVIC register map type. */
+typedef struct nvic_reg_map {
+ __io uint32 ISER[8]; /**< Interrupt Set Enable Registers */
+ uint32 RESERVED0[24]; /**< Reserved */
+ __io uint32 ICER[8]; /**< Interrupt Clear Enable Registers */
+ uint32 RSERVED1[24]; /**< Reserved */
+ __io uint32 ISPR[8]; /**< Interrupt Set Pending Registers */
+ uint32 RESERVED2[24]; /**< Reserved */
+ __io uint32 ICPR[8]; /**< Interrupt Clear Pending Registers */
+ uint32 RESERVED3[24]; /**< Reserved */
+ __io uint32 IABR[8]; /**< Interrupt Active bit Registers */
+ uint32 RESERVED4[56]; /**< Reserved */
+ __io uint8 IP[240]; /**< Interrupt Priority Registers */
+ uint32 RESERVED5[644]; /**< Reserved */
+ __io uint32 STIR; /**< Software Trigger Interrupt Registers */
+} nvic_reg_map;
+
+/** NVIC register map base pointer. */
+#define NVIC_BASE ((nvic_reg_map*)0xE000E100)
+
+/**
+ * Interrupt vector table interrupt numbers. Each enumerator is the
+ * position of the corresponding interrupt in the vector table. */
+typedef enum nvic_irq_num {
+ NVIC_NMI = -14, /**< Non-maskable interrupt */
+ NVIC_HARDFAULT = -13, /**< Hard fault (all class of fault) */
+ NVIC_MEM_MANAGE = -12, /**< Memory management */
+ NVIC_BUS_FAULT = -11, /**< Bus fault: prefetch fault, memory
+ access fault. */
+ NVIC_USAGE_FAULT = -10, /**< Usage fault: Undefined instruction or
+ illegal state. */
+ NVIC_SVC = -5, /**< System service call via SWI insruction */
+ NVIC_DEBUG_MON = -4, /**< Debug monitor */
+ NVIC_PEND_SVC = -2, /**< Pendable request for system service */
+ NVIC_SYSTICK = -1, /**< System tick timer */
+ NVIC_WWDG = 0, /**< Window watchdog interrupt */
+ NVIC_PVD = 1, /**< PVD through EXTI line detection */
+ NVIC_TAMPER = 2, /**< Tamper */
+ NVIC_RTC = 3, /**< Real-time clock */
+ NVIC_FLASH = 4, /**< Flash */
+ NVIC_RCC = 5, /**< Reset and clock control */
+ NVIC_EXTI0 = 6, /**< EXTI line 0 */
+ NVIC_EXTI1 = 7, /**< EXTI line 1 */
+ NVIC_EXTI2 = 8, /**< EXTI line 2 */
+ NVIC_EXTI3 = 9, /**< EXTI line 3 */
+ NVIC_EXTI4 = 10, /**< EXTI line 4 */
+ NVIC_DMA_CH1 = 11, /**< DMA1 channel 1 */
+ NVIC_DMA_CH2 = 12, /**< DMA1 channel 2 */
+ NVIC_DMA_CH3 = 13, /**< DMA1 channel 3 */
+ NVIC_DMA_CH4 = 14, /**< DMA1 channel 4 */
+ NVIC_DMA_CH5 = 15, /**< DMA1 channel 5 */
+ NVIC_DMA_CH6 = 16, /**< DMA1 channel 6 */
+ NVIC_DMA_CH7 = 17, /**< DMA1 channel 7 */
+ NVIC_ADC_1_2 = 18, /**< ADC1 and ADC2 */
+ NVIC_USB_HP_CAN_TX = 19, /**< USB high priority or CAN TX */
+ NVIC_USB_LP_CAN_RX0 = 20, /**< USB low priority or CAN RX0 */
+ NVIC_CAN_RX1 = 21, /**< CAN RX1 */
+ NVIC_CAN_SCE = 22, /**< CAN SCE */
+ NVIC_EXTI_9_5 = 23, /**< EXTI line [9:5] */
+ NVIC_TIMER1_BRK = 24, /**< Timer 1 break */
+ NVIC_TIMER1_UP = 25, /**< Timer 1 update */
+ NVIC_TIMER1_TRG_COM = 26, /**< Timer 1 trigger and commutation */
+ NVIC_TIMER1_CC = 27, /**< Timer 1 capture/compare */
+ NVIC_TIMER2 = 28, /**< Timer 2 */
+ NVIC_TIMER3 = 29, /**< Timer 3 */
+ NVIC_TIMER4 = 30, /**< Timer 4 */
+ NVIC_I2C1_EV = 31, /**< I2C1 event */
+ NVIC_I2C1_ER = 32, /**< I2C1 error */
+ NVIC_I2C2_EV = 33, /**< I2C2 event */
+ NVIC_I2C2_ER = 34, /**< I2C2 error */
+ NVIC_SPI1 = 35, /**< SPI1 */
+ NVIC_SPI2 = 36, /**< SPI2 */
+ NVIC_USART1 = 37, /**< USART1 */
+ NVIC_USART2 = 38, /**< USART2 */
+ NVIC_USART3 = 39, /**< USART3 */
+ NVIC_EXTI_15_10 = 40, /**< EXTI line [15:10] */
+ NVIC_RTCALARM = 41, /**< RTC alarm through EXTI line */
+ NVIC_USBWAKEUP = 42, /**< USB wakeup from suspend through
+ EXTI line */
+ NVIC_TIMER8_BRK = 43, /**< Timer 8 break */
+ NVIC_TIMER8_UP = 44, /**< Timer 8 update */
+ NVIC_TIMER8_TRG_COM = 45, /**< Timer 8 trigger and commutation */
+ NVIC_TIMER8_CC = 46, /**< Timer 8 capture/compare */
+#ifdef STM32_HIGH_DENSITY
+ NVIC_ADC3 = 47, /**< ADC3 */
+ NVIC_FSMC = 48, /**< FSMC */
+ NVIC_SDIO = 49, /**< SDIO */
+ NVIC_TIMER5 = 50, /**< Timer 5 */
+ NVIC_SPI3 = 51, /**< SPI3 */
+ NVIC_UART4 = 52, /**< UART4 */
+ NVIC_UART5 = 53, /**< UART5 */
+ NVIC_TIMER6 = 54, /**< Timer 6 */
+ NVIC_TIMER7 = 55, /**< Timer 7 */
+ NVIC_DMA2_CH1 = 56, /**< DMA2 channel 1 */
+ NVIC_DMA2_CH2 = 57, /**< DMA2 channel 2 */
+ NVIC_DMA2_CH3 = 58, /**< DMA2 channel 3 */
+ NVIC_DMA2_CH_4_5 = 59, /**< DMA2 channels 4 and 5 */
+#endif
+} nvic_irq_num;
+
+void nvic_init(uint32 vector_table_address, uint32 offset);
+void nvic_set_vector_table(uint32 address, uint32 offset);
+void nvic_set_priority(nvic_irq_num irqn, uint8 priority);
+
+/**
+ * Enables interrupts and configurable fault handlers (clear PRIMASK).
+ */
+static inline void nvic_globalirq_enable() {
+ asm volatile("cpsie i");
+}
+
+/**
+ * Disable interrupts and configurable fault handlers (set PRIMASK).
+ */
+static inline void nvic_globalirq_disable() {
+ asm volatile("cpsid i");
+}
+
+/**
+ * @brief Enable interrupt irq_num
+ * @param irq_num Interrupt to enable
+ */
+static inline void nvic_irq_enable(nvic_irq_num irq_num) {
+ if (irq_num < 0) {
+ return;
+ }
+ NVIC_BASE->ISER[irq_num / 32] = BIT(irq_num % 32);
+}
+
+/**
+ * @brief Disable interrupt irq_num
+ * @param irq_num Interrupt to disable
+ */
+static inline void nvic_irq_disable(nvic_irq_num irq_num) {
+ if (irq_num < 0) {
+ return;
+ }
+ NVIC_BASE->ICER[irq_num / 32] = BIT(irq_num % 32);
+}
+
+/**
+ * @brief Quickly disable all interrupts.
+ *
+ * Calling this function is significantly faster than calling
+ * nvic_irq_disable() in a loop.
+ */
+static inline void nvic_irq_disable_all(void) {
+ /* Note: This only works up to XL density. The fix for
+ * connectivity line is:
+ *
+ * NVIC_BASE->ICER[2] = 0xF;
+ *
+ * We don't support connectivity line devices (yet), so leave it
+ * alone for now.
+ */
+ NVIC_BASE->ICER[0] = 0xFFFFFFFF;
+ NVIC_BASE->ICER[1] = 0xFFFFFFFF;
+}
#ifdef __cplusplus
}
diff --git a/libmaple/pwr.c b/libmaple/pwr.c
new file mode 100644
index 0000000..d63a92d
--- /dev/null
+++ b/libmaple/pwr.c
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2011 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.
+ *****************************************************************************/
+
+#include "pwr.h"
+#include "rcc.h"
+
+pwr_dev pwr = {
+ .regs = PWR_BASE,
+};
+
+const pwr_dev *PWR = &pwr;
+
+/**
+ * Enables the power interface clock, and resets the power device.
+ */
+void pwr_init(void) {
+ rcc_clk_enable(RCC_PWR);
+ rcc_reset_dev(RCC_PWR);
+}
diff --git a/libmaple/pwr.h b/libmaple/pwr.h
index 96a8356..5f68c34 100644
--- a/libmaple/pwr.h
+++ b/libmaple/pwr.h
@@ -29,18 +29,51 @@
* @brief Power control (PWR) defines.
*/
-#define PWR_BASE 0x40007000
-
-#define PWR_CR (PWR_BASE + 0x0)
-#define PWR_CR_DBP 8 /* Disable backup domain write protection bit */
-#define PWR_CR_PVDE 4 /* Power voltage detector enable bit */
-#define PWR_CR_CSBF 3 /* Clear standby flag bit */
-#define PWR_CR_CWUF 2 /* Clear wakeup flag bit */
-#define PWR_CR_PDDS 1 /* Power down deepsleep bit */
-#define PWR_CR_LPDS 0 /* Low-power deepsleep bit */
-
-#define PWR_CSR (PWR_BASE + 0x4)
-#define PWR_CSR_EWUP 8 /* Enable wakeup pin bit */
-#define PWR_CSR_PVDO 2 /* PVD output bit */
-#define PWR_CSR_SBF 1 /* Standby flag bit */
-#define PWR_CSR_WUF 0 /* Wakeup flag bit */
+#include "libmaple.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Power interface register map. */
+typedef struct pwr_reg_map {
+ __io uint32 CR; /**< Control register */
+ __io uint32 CSR; /**< Control and status register */
+} pwr_reg_map;
+
+/** Power peripheral register map base pointer. */
+#define PWR_BASE ((pwr_reg_map*)0x40007000)
+
+/** Power device type. */
+typedef struct pwr_dev {
+ pwr_reg_map *regs; /**< Register map */
+} pwr_dev;
+
+/**
+ * Power device.
+ */
+extern const pwr_dev *PWR;
+
+/*
+ * Register bit definitions
+ */
+
+/* Control register */
+#define PWR_CR_DBP 8 /**< Disable backup domain write protection bit */
+#define PWR_CR_PVDE 4 /**< Power voltage detector enable bit */
+#define PWR_CR_CSBF 3 /**< Clear standby flag bit */
+#define PWR_CR_CWUF 2 /**< Clear wakeup flag bit */
+#define PWR_CR_PDDS 1 /**< Power down deepsleep bit */
+#define PWR_CR_LPDS 0 /**< Low-power deepsleep bit */
+
+/* Control and status register */
+#define PWR_CSR_EWUP 8 /**< Enable wakeup pin bit */
+#define PWR_CSR_PVDO 2 /**< PVD output bit */
+#define PWR_CSR_SBF 1 /**< Standby flag bit */
+#define PWR_CSR_WUF 0 /**< Wakeup flag bit */
+
+void pwr_init(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libmaple/rcc.c b/libmaple/rcc.c
index 6905c22..37c1ec4 100644
--- a/libmaple/rcc.c
+++ b/libmaple/rcc.c
@@ -30,49 +30,69 @@
#include "libmaple.h"
#include "flash.h"
#include "rcc.h"
+#include "bitband.h"
-enum {
- APB1,
- APB2,
- AHB
-};
+#define APB1 RCC_APB1
+#define APB2 RCC_APB2
+#define AHB RCC_AHB
struct rcc_dev_info {
- const uint8 clk_domain;
+ const rcc_clk_domain clk_domain;
const uint8 line_num;
};
-/* device descriptor tables */
+/* Device descriptor table, maps rcc_clk_id onto bus and enable/reset
+ * register bit numbers. */
static const struct rcc_dev_info rcc_dev_table[] = {
[RCC_GPIOA] = { .clk_domain = APB2, .line_num = 2 },
[RCC_GPIOB] = { .clk_domain = APB2, .line_num = 3 },
[RCC_GPIOC] = { .clk_domain = APB2, .line_num = 4 },
[RCC_GPIOD] = { .clk_domain = APB2, .line_num = 5 },
- [RCC_GPIOE] = { .clk_domain = APB2, .line_num = 6 }, // High-density only
- [RCC_GPIOF] = { .clk_domain = APB2, .line_num = 7 }, // High-density only
- [RCC_GPIOG] = { .clk_domain = APB2, .line_num = 8 }, // High-density only
[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 },
- [RCC_UART4] = { .clk_domain = APB1, .line_num = 19 }, // High-density only
- [RCC_UART5] = { .clk_domain = APB1, .line_num = 20 }, // High-density only
[RCC_TIMER1] = { .clk_domain = APB2, .line_num = 11 },
[RCC_TIMER2] = { .clk_domain = APB1, .line_num = 0 },
[RCC_TIMER3] = { .clk_domain = APB1, .line_num = 1 },
[RCC_TIMER4] = { .clk_domain = APB1, .line_num = 2 },
- [RCC_TIMER5] = { .clk_domain = APB1, .line_num = 3 }, // High-density only
- [RCC_TIMER6] = { .clk_domain = APB1, .line_num = 4 }, // High-density only
- [RCC_TIMER7] = { .clk_domain = APB1, .line_num = 5 }, // High-density only
- [RCC_TIMER8] = { .clk_domain = APB2, .line_num = 13 }, // High-density only
[RCC_SPI1] = { .clk_domain = APB2, .line_num = 12 },
[RCC_SPI2] = { .clk_domain = APB1, .line_num = 14 },
- [RCC_FSMC] = { .clk_domain = AHB, .line_num = 8 }, // High-density only
- [RCC_DAC] = { .clk_domain = APB1, .line_num = 29 }, // High-density only
[RCC_DMA1] = { .clk_domain = AHB, .line_num = 0 },
- [RCC_DMA2] = { .clk_domain = AHB, .line_num = 1 }, // High-density only
+ [RCC_PWR] = { .clk_domain = APB1, .line_num = 28},
+ [RCC_BKP] = { .clk_domain = APB1, .line_num = 27},
+ [RCC_I2C1] = { .clk_domain = APB1, .line_num = 21 },
+ [RCC_I2C2] = { .clk_domain = APB1, .line_num = 22 },
+ [RCC_CRC] = { .clk_domain = AHB, .line_num = 6},
+ [RCC_FLITF] = { .clk_domain = AHB, .line_num = 4},
+ [RCC_SRAM] = { .clk_domain = AHB, .line_num = 2},
+#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
+ [RCC_GPIOE] = { .clk_domain = APB2, .line_num = 6 },
+ [RCC_GPIOF] = { .clk_domain = APB2, .line_num = 7 },
+ [RCC_GPIOG] = { .clk_domain = APB2, .line_num = 8 },
+ [RCC_UART4] = { .clk_domain = APB1, .line_num = 19 },
+ [RCC_UART5] = { .clk_domain = APB1, .line_num = 20 },
+ [RCC_TIMER5] = { .clk_domain = APB1, .line_num = 3 },
+ [RCC_TIMER6] = { .clk_domain = APB1, .line_num = 4 },
+ [RCC_TIMER7] = { .clk_domain = APB1, .line_num = 5 },
+ [RCC_TIMER8] = { .clk_domain = APB2, .line_num = 13 },
+ [RCC_FSMC] = { .clk_domain = AHB, .line_num = 8 },
+ [RCC_DAC] = { .clk_domain = APB1, .line_num = 29 },
+ [RCC_DMA2] = { .clk_domain = AHB, .line_num = 1 },
+ [RCC_SDIO] = { .clk_domain = AHB, .line_num = 10 },
+ [RCC_SPI3] = { .clk_domain = APB1, .line_num = 15 },
+#endif
+#ifdef STM32_XL_DENSITY
+ [RCC_TIMER9] = { .clk_domain = APB2, .line_num = 19 },
+ [RCC_TIMER10] = { .clk_domain = APB2, .line_num = 20 },
+ [RCC_TIMER11] = { .clk_domain = APB2, .line_num = 21 },
+ [RCC_TIMER12] = { .clk_domain = APB1, .line_num = 6 },
+ [RCC_TIMER13] = { .clk_domain = APB1, .line_num = 7 },
+ [RCC_TIMER14] = { .clk_domain = APB1, .line_num = 8 },
+#endif
};
/**
@@ -82,52 +102,84 @@ static const struct rcc_dev_info rcc_dev_table[] = {
* @param pll_src pll clock source, must be HSE
* @param pll_mul pll multiplier
*/
-void rcc_clk_init(uint32 sysclk_src, uint32 pll_src, uint32 pll_mul) {
+void rcc_clk_init(rcc_sysclk_src sysclk_src,
+ rcc_pllsrc pll_src,
+ rcc_pll_multiplier pll_mul) {
+ uint32 cfgr;
+ uint32 cr;
+
/* Assume that we're going to clock the chip off the PLL, fed by
* the HSE */
ASSERT(sysclk_src == RCC_CLKSRC_PLL &&
pll_src == RCC_PLLSRC_HSE);
- uint32 cfgr = 0;
- uint32 cr = RCC_READ_CR();
-
- cfgr = (pll_src | pll_mul);
- RCC_WRITE_CFGR(cfgr);
+ RCC_BASE->CFGR = pll_src | pll_mul;
- /* Turn on the HSE */
+ /* Turn on the HSE */
+ /* FIXME WTF why doesn't bit-banding work here? */
+ cr = RCC_BASE->CR;
cr |= RCC_CR_HSEON;
- RCC_WRITE_CR(cr);
- while (!(RCC_READ_CR() & RCC_CR_HSERDY))
+ RCC_BASE->CR = cr;
+ while (!(RCC_BASE->CR & RCC_CR_HSERDY))
;
/* Now the PLL */
cr |= RCC_CR_PLLON;
- RCC_WRITE_CR(cr);
- while (!(RCC_READ_CR() & RCC_CR_PLLRDY))
+ RCC_BASE->CR = cr;
+ while (!(RCC_BASE->CR & RCC_CR_PLLRDY))
;
/* Finally, let's switch over to the PLL */
cfgr &= ~RCC_CFGR_SW;
cfgr |= RCC_CFGR_SW_PLL;
- RCC_WRITE_CFGR(cfgr);
- while ((RCC_READ_CFGR() & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL)
+ RCC_BASE->CFGR = cfgr;
+ while ((RCC_BASE->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL)
;
}
/**
* @brief Turn on the clock line on a device
- * @param dev_num device to turn on
+ * @param device Clock ID of the device to turn on.
+ */
+void rcc_clk_enable(rcc_clk_id device) {
+ static const __io uint32* enable_regs[] = {
+ [APB1] = &RCC_BASE->APB1ENR,
+ [APB2] = &RCC_BASE->APB2ENR,
+ [AHB] = &RCC_BASE->AHBENR,
+ };
+
+ rcc_clk_domain clk_domain = rcc_dev_clk(device);
+ __io uint32* enr = (__io uint32*)enable_regs[clk_domain];
+ uint8 lnum = rcc_dev_table[device].line_num;
+
+ bb_peri_set_bit(enr, lnum, 1);
+}
+
+/**
+ * @brief reset a device
+ * @param device Clock ID of the device to reset.
*/
-void rcc_clk_enable(uint32 dev_num) {
- static const uint32 enable_regs[] = {
- [APB1] = RCC_APB1ENR,
- [APB2] = RCC_APB2ENR,
- [AHB] = RCC_AHBENR,
+void rcc_reset_dev(rcc_clk_id device) {
+ static const __io uint32* reset_regs[] = {
+ [APB1] = &RCC_BASE->APB1RSTR,
+ [APB2] = &RCC_BASE->APB2RSTR,
};
- uint8 clk_domain = rcc_dev_table[dev_num].clk_domain;
+ rcc_clk_domain clk_domain = rcc_dev_clk(device);
+ __io void* addr = (__io void*)reset_regs[clk_domain];
+ uint8 lnum = rcc_dev_table[device].line_num;
+
+ bb_peri_set_bit(addr, lnum, 1);
+ bb_peri_set_bit(addr, lnum, 0);
+}
- __set_bits(enable_regs[clk_domain], BIT(rcc_dev_table[dev_num].line_num));
+/**
+ * @brief Get a device's clock domain
+ * @param device Device whose clock domain to return
+ * @return Device's clock source
+ */
+rcc_clk_domain rcc_dev_clk(rcc_clk_id device) {
+ return rcc_dev_table[device].clk_domain;
}
/**
@@ -135,7 +187,7 @@ void rcc_clk_enable(uint32 dev_num) {
* @param prescaler prescaler to set
* @param divider prescaler divider
*/
-void rcc_set_prescaler(uint32 prescaler, uint32 divider) {
+void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider) {
static const uint32 masks[] = {
[RCC_PRESCALER_AHB] = RCC_CFGR_HPRE,
[RCC_PRESCALER_APB1] = RCC_CFGR_PPRE1,
@@ -144,25 +196,8 @@ void rcc_set_prescaler(uint32 prescaler, uint32 divider) {
[RCC_PRESCALER_ADC] = RCC_CFGR_ADCPRE,
};
- uint32 cfgr = RCC_READ_CFGR();
-
+ uint32 cfgr = RCC_BASE->CFGR;
cfgr &= ~masks[prescaler];
cfgr |= divider;
- RCC_WRITE_CFGR(cfgr);
-}
-
-/**
- * @brief reset a device
- * @param dev_num device to reset
- */
-void rcc_reset_dev(uint32 dev_num) {
- static const uint32 reset_regs[] = {
- [APB1] = RCC_APB1RSTR,
- [APB2] = RCC_APB2RSTR,
- };
-
- uint8 clk_domain = rcc_dev_table[dev_num].clk_domain;
-
- __set_bits(reset_regs[clk_domain], BIT(rcc_dev_table[dev_num].line_num));
- __clear_bits(reset_regs[clk_domain], BIT(rcc_dev_table[dev_num].line_num));
+ RCC_BASE->CFGR = cfgr;
}
diff --git a/libmaple/rcc.h b/libmaple/rcc.h
index 4b1f35d..f0f026b 100644
--- a/libmaple/rcc.h
+++ b/libmaple/rcc.h
@@ -27,6 +27,8 @@
* @brief reset and clock control definitions and prototypes
*/
+#include "libmaple_types.h"
+
#ifndef _RCC_H_
#define _RCC_H_
@@ -34,154 +36,528 @@
extern "C"{
#endif
-/* registers */
-#define RCC_BASE 0x40021000
-#define RCC_CR (RCC_BASE + 0x0)
-#define RCC_CFGR (RCC_BASE + 0x4)
-#define RCC_CIR (RCC_BASE + 0x8)
-#define RCC_APB2RSTR (RCC_BASE + 0xC)
-#define RCC_APB1RSTR (RCC_BASE + 0x10)
-#define RCC_AHBENR (RCC_BASE + 0x14)
-#define RCC_APB2ENR (RCC_BASE + 0x18)
-#define RCC_APB1ENR (RCC_BASE + 0x1C)
-#define RCC_BDCR (RCC_BASE + 0x20)
-#define RCC_CSR (RCC_BASE + 0x24)
-#define RCC_AHBSTR (RCC_BASE + 0x28)
-#define RCC_CFGR2 (RCC_BASE + 0x2C)
-
-#define RCC_CFGR_USBPRE (0x1 << 22)
-#define RCC_CFGR_ADCPRE (0x3 << 14)
-#define RCC_CFGR_PPRE1 (0x7 << 8)
-#define RCC_CFGR_PPRE2 (0x7 << 11)
-#define RCC_CFGR_HPRE (0xF << 4)
-#define RCC_CFGR_PLLSRC (0x1 << 16)
-
-#define RCC_CFGR_SWS (0x3 << 2)
-#define RCC_CFGR_SWS_PLL (0x2 << 2)
-#define RCC_CFGR_SWS_HSE (0x1 << 2)
-
-#define RCC_CFGR_SW (0x3 << 0)
-#define RCC_CFGR_SW_PLL (0x2 << 0)
-#define RCC_CFGR_SW_HSE (0x1 << 0)
-
-/* CR status bits */
-#define RCC_CR_HSEON (0x1 << 16)
-#define RCC_CR_HSERDY (0x1 << 17)
-#define RCC_CR_PLLON (0x1 << 24)
-#define RCC_CR_PLLRDY (0x1 << 25)
-
-#define RCC_WRITE_CFGR(val) __write(RCC_CFGR, val)
-#define RCC_READ_CFGR() __read(RCC_CFGR)
-
-#define RCC_WRITE_CR(val) __write(RCC_CR, val)
-#define RCC_READ_CR() __read(RCC_CR)
-
-/* sysclk source */
-#define RCC_CLKSRC_HSI (0x0)
-#define RCC_CLKSRC_HSE (0x1)
-#define RCC_CLKSRC_PLL (0x2)
-
-/* pll entry clock source */
-#define RCC_PLLSRC_HSE (0x1 << 16)
-#define RCC_PLLSRC_HSI_DIV_2 (0x0 << 16)
-
-/* adc prescaler dividers */
-#define RCC_ADCPRE_PCLK_DIV_2 (0x0 << 14)
-#define RCC_ADCPRE_PCLK_DIV_4 (0x1 << 14)
-#define RCC_ADCPRE_PCLK_DIV_6 (0x2 << 14)
-#define RCC_ADCPRE_PCLK_DIV_8 (0x3 << 14)
-
-/* apb1 prescaler dividers */
-#define RCC_APB1_HCLK_DIV_1 (0x0 << 8)
-#define RCC_APB1_HCLK_DIV_2 (0x4 << 8)
-#define RCC_APB1_HCLK_DIV_4 (0x5 << 8)
-#define RCC_APB1_HCLK_DIV_8 (0x6 << 8)
-#define RCC_APB1_HCLK_DIV_16 (0x7 << 8)
-
-/* apb2 prescaler dividers */
-#define RCC_APB2_HCLK_DIV_1 (0x0 << 11)
-#define RCC_APB2_HCLK_DIV_2 (0x4 << 11)
-#define RCC_APB2_HCLK_DIV_4 (0x5 << 11)
-#define RCC_APB2_HCLK_DIV_8 (0x6 << 11)
-#define RCC_APB2_HCLK_DIV_16 (0x7 << 11)
-
-/* ahb prescaler dividers */
-#define RCC_AHB_SYSCLK_DIV_1 (0x0 << 4)
-#define RCC_AHB_SYSCLK_DIV_2 (0x8 << 4)
-#define RCC_AHB_SYSCLK_DIV_4 (0x9 << 4)
-#define RCC_AHB_SYSCLK_DIV_8 (0xA << 4)
-#define RCC_AHB_SYSCLK_DIV_16 (0xB << 4)
-#define RCC_AHB_SYSCLK_DIV_32 (0xC << 4)
-#define RCC_AHB_SYSCLK_DIV_64 (0xD << 4)
-#define RCC_AHB_SYSCLK_DIV_128 (0xD << 4)
-#define RCC_AHB_SYSCLK_DIV_256 (0xE << 4)
-#define RCC_AHB_SYSCLK_DIV_512 (0xF << 4)
-
-/* pll multipliers */
-#define RCC_PLLMUL_2 (0x0 << 18)
-#define RCC_PLLMUL_3 (0x1 << 18)
-#define RCC_PLLMUL_4 (0x2 << 18)
-#define RCC_PLLMUL_5 (0x3 << 18)
-#define RCC_PLLMUL_6 (0x4 << 18)
-#define RCC_PLLMUL_7 (0x5 << 18)
-#define RCC_PLLMUL_8 (0x6 << 18)
-#define RCC_PLLMUL_9 (0x7 << 18)
-#define RCC_PLLMUL_10 (0x8 << 18)
-#define RCC_PLLMUL_11 (0x9 << 18)
-#define RCC_PLLMUL_12 (0xA << 18)
-#define RCC_PLLMUL_13 (0xB << 18)
-#define RCC_PLLMUL_14 (0xC << 18)
-#define RCC_PLLMUL_15 (0xD << 18)
-#define RCC_PLLMUL_16 (0xE << 18)
-
-
-/* prescalers */
-enum {
- RCC_PRESCALER_AHB,
- RCC_PRESCALER_APB1,
- RCC_PRESCALER_APB2,
- RCC_PRESCALER_USB,
- RCC_PRESCALER_ADC
-};
+/** RCC register map type */
+typedef struct rcc_reg_map {
+ __io uint32 CR; /**< Clock control register */
+ __io uint32 CFGR; /**< Clock configuration register */
+ __io uint32 CIR; /**< Clock interrupt register */
+ __io uint32 APB2RSTR; /**< APB2 peripheral reset register */
+ __io uint32 APB1RSTR; /**< APB1 peripheral reset register */
+ __io uint32 AHBENR; /**< AHB peripheral clock enable register */
+ __io uint32 APB2ENR; /**< APB2 peripheral clock enable register */
+ __io uint32 APB1ENR; /**< APB1 peripheral clock enable register */
+ __io uint32 BDCR; /**< Backup domain control register */
+ __io uint32 CSR; /**< Control/status register */
+} rcc_reg_map;
+
+/** RCC register map base pointer */
+#define RCC_BASE ((rcc_reg_map*)0x40021000)
+
+/*
+ * Register bit definitions
+ */
+
+/* Clock control register */
+
+#define RCC_CR_PLLRDY_BIT 25
+#define RCC_CR_PLLON_BIT 24
+#define RCC_CR_CSSON_BIT 19
+#define RCC_CR_HSEBYP_BIT 18
+#define RCC_CR_HSERDY_BIT 17
+#define RCC_CR_HSEON_BIT 16
+#define RCC_CR_HSIRDY_BIT 1
+#define RCC_CR_HSION_BIT 0
+
+#define RCC_CR_PLLRDY BIT(RCC_CR_PLLRDY_BIT)
+#define RCC_CR_PLLON BIT(RCC_CR_PLLON_BIT)
+#define RCC_CR_CSSON BIT(RCC_CR_CSSON_BIT)
+#define RCC_CR_HSEBYP BIT(RCC_CR_HSEBYP_BIT)
+#define RCC_CR_HSERDY BIT(RCC_CR_HSERDY_BIT)
+#define RCC_CR_HSEON BIT(RCC_CR_HSEON_BIT)
+#define RCC_CR_HSICAL (0xFF << 8)
+#define RCC_CR_HSITRIM (0x1F << 3)
+#define RCC_CR_HSIRDY BIT(RCC_CR_HSIRDY_BIT)
+#define RCC_CR_HSION BIT(RCC_CR_HSION_BIT)
+
+/* Clock configuration register */
+
+#define RCC_CFGR_USBPRE_BIT 22
+#define RCC_CFGR_PLLXTPRE_BIT 17
+#define RCC_CFGR_PLLSRC_BIT 16
+
+#define RCC_CFGR_MCO (0x3 << 24)
+#define RCC_CFGR_USBPRE BIT(RCC_CFGR_USBPRE_BIT)
+#define RCC_CFGR_PLLMUL (0xF << 18)
+#define RCC_CFGR_PLLXTPRE BIT(RCC_CFGR_PLLXTPRE_BIT)
+#define RCC_CFGR_PLLSRC BIT(RCC_CFGR_PLLSRC_BIT)
+#define RCC_CFGR_ADCPRE (0x3 << 14)
+#define RCC_CFGR_PPRE2 (0x7 << 11)
+#define RCC_CFGR_PPRE1 (0x7 << 8)
+#define RCC_CFGR_HPRE (0xF << 4)
+#define RCC_CFGR_SWS (0x3 << 2)
+#define RCC_CFGR_SWS_PLL (0x2 << 2)
+#define RCC_CFGR_SWS_HSE (0x1 << 2)
+#define RCC_CFGR_SW 0x3
+#define RCC_CFGR_SW_PLL 0x2
+#define RCC_CFGR_SW_HSE 0x1
+
+/* Clock interrupt register */
+
+#define RCC_CIR_CSSC_BIT 23
+#define RCC_CIR_PLLRDYC_BIT 20
+#define RCC_CIR_HSERDYC_BIT 19
+#define RCC_CIR_HSIRDYC_BIT 18
+#define RCC_CIR_LSERDYC_BIT 17
+#define RCC_CIR_LSIRDYC_BIT 16
+#define RCC_CIR_PLLRDYIE_BIT 12
+#define RCC_CIR_HSERDYIE_BIT 11
+#define RCC_CIR_HSIRDYIE_BIT 10
+#define RCC_CIR_LSERDYIE_BIT 9
+#define RCC_CIR_LSIRDYIE_BIT 8
+#define RCC_CIR_CSSF_BIT 7
+#define RCC_CIR_PLLRDYF_BIT 4
+#define RCC_CIR_HSERDYF_BIT 3
+#define RCC_CIR_HSIRDYF_BIT 2
+#define RCC_CIR_LSERDYF_BIT 1
+#define RCC_CIR_LSIRDYF_BIT 0
+
+#define RCC_CIR_CSSC BIT(RCC_CIR_CSSC_BIT)
+#define RCC_CIR_PLLRDYC BIT(RCC_CIR_PLLRDYC_BIT)
+#define RCC_CIR_HSERDYC BIT(RCC_CIR_HSERDYC_BIT)
+#define RCC_CIR_HSIRDYC BIT(RCC_CIR_HSIRDYC_BIT)
+#define RCC_CIR_LSERDYC BIT(RCC_CIR_LSERDYC_BIT)
+#define RCC_CIR_LSIRDYC BIT(RCC_CIR_LSIRDYC_BIT)
+#define RCC_CIR_PLLRDYIE BIT(RCC_CIR_PLLRDYIE_BIT)
+#define RCC_CIR_HSERDYIE BIT(RCC_CIR_HSERDYIE_BIT)
+#define RCC_CIR_HSIRDYIE BIT(RCC_CIR_HSIRDYIE_BIT)
+#define RCC_CIR_LSERDYIE BIT(RCC_CIR_LSERDYIE_BIT)
+#define RCC_CIR_LSIRDYIE BIT(RCC_CIR_LSIRDYIE_BIT)
+#define RCC_CIR_CSSF BIT(RCC_CIR_CSSF_BIT)
+#define RCC_CIR_PLLRDYF BIT(RCC_CIR_PLLRDYF_BIT)
+#define RCC_CIR_HSERDYF BIT(RCC_CIR_HSERDYF_BIT)
+#define RCC_CIR_HSIRDYF BIT(RCC_CIR_HSIRDYF_BIT)
+#define RCC_CIR_LSERDYF BIT(RCC_CIR_LSERDYF_BIT)
+#define RCC_CIR_LSIRDYF BIT(RCC_CIR_LSIRDYF_BIT)
+
+/* APB2 peripheral reset register */
+
+#define RCC_APB2RSTR_TIM11RST_BIT 21
+#define RCC_APB2RSTR_TIM10RST_BIT 20
+#define RCC_APB2RSTR_TIM9RST_BIT 19
+#define RCC_APB2RSTR_ADC3RST_BIT 15
+#define RCC_APB2RSTR_USART1RST_BIT 14
+#define RCC_APB2RSTR_TIM8RST_BIT 13
+#define RCC_APB2RSTR_SPI1RST_BIT 12
+#define RCC_APB2RSTR_TIM1RST_BIT 11
+#define RCC_APB2RSTR_ADC2RST_BIT 10
+#define RCC_APB2RSTR_ADC1RST_BIT 9
+#define RCC_APB2RSTR_IOPGRST_BIT 8
+#define RCC_APB2RSTR_IOPFRST_BIT 7
+#define RCC_APB2RSTR_IOPERST_BIT 6
+#define RCC_APB2RSTR_IOPDRST_BIT 5
+#define RCC_APB2RSTR_IOPCRST_BIT 4
+#define RCC_APB2RSTR_IOPBRST_BIT 3
+#define RCC_APB2RSTR_IOPARST_BIT 2
+#define RCC_APB2RSTR_AFIORST_BIT 0
+
+#define RCC_APB2RSTR_TIM11RST BIT(RCC_APB2RSTR_TIM11RST_BIT)
+#define RCC_APB2RSTR_TIM10RST BIT(RCC_APB2RSTR_TIM10RST_BIT)
+#define RCC_APB2RSTR_TIM9RST BIT(RCC_APB2RSTR_TIM9RST_BIT)
+#define RCC_APB2RSTR_ADC3RST BIT(RCC_APB2RSTR_ADC3RST_BIT)
+#define RCC_APB2RSTR_USART1RST BIT(RCC_APB2RSTR_USART1RST_BIT)
+#define RCC_APB2RSTR_TIM8RST BIT(RCC_APB2RSTR_TIM8RST_BIT)
+#define RCC_APB2RSTR_SPI1RST BIT(RCC_APB2RSTR_SPI1RST_BIT)
+#define RCC_APB2RSTR_TIM1RST BIT(RCC_APB2RSTR_TIM1RST_BIT)
+#define RCC_APB2RSTR_ADC2RST BIT(RCC_APB2RSTR_ADC2RST_BIT)
+#define RCC_APB2RSTR_ADC1RST BIT(RCC_APB2RSTR_ADC1RST_BIT)
+#define RCC_APB2RSTR_IOPGRST BIT(RCC_APB2RSTR_IOPGRST_BIT)
+#define RCC_APB2RSTR_IOPFRST BIT(RCC_APB2RSTR_IOPFRST_BIT)
+#define RCC_APB2RSTR_IOPERST BIT(RCC_APB2RSTR_IOPERST_BIT)
+#define RCC_APB2RSTR_IOPDRST BIT(RCC_APB2RSTR_IOPDRST_BIT)
+#define RCC_APB2RSTR_IOPCRST BIT(RCC_APB2RSTR_IOPCRST_BIT)
+#define RCC_APB2RSTR_IOPBRST BIT(RCC_APB2RSTR_IOPBRST_BIT)
+#define RCC_APB2RSTR_IOPARST BIT(RCC_APB2RSTR_IOPARST_BIT)
+#define RCC_APB2RSTR_AFIORST BIT(RCC_APB2RSTR_AFIORST_BIT)
+
+/* APB1 peripheral reset register */
+
+#define RCC_APB1RSTR_DACRST_BIT 29
+#define RCC_APB1RSTR_PWRRST_BIT 28
+#define RCC_APB1RSTR_BKPRST_BIT 27
+#define RCC_APB1RSTR_CANRST_BIT 25
+#define RCC_APB1RSTR_USBRST_BIT 23
+#define RCC_APB1RSTR_I2C2RST_BIT 22
+#define RCC_APB1RSTR_I2C1RST_BIT 21
+#define RCC_APB1RSTR_UART5RST_BIT 20
+#define RCC_APB1RSTR_UART4RST_BIT 19
+#define RCC_APB1RSTR_USART3RST_BIT 18
+#define RCC_APB1RSTR_USART2RST_BIT 17
+#define RCC_APB1RSTR_SPI3RST_BIT 15
+#define RCC_APB1RSTR_SPI2RST_BIT 14
+#define RCC_APB1RSTR_WWDRST_BIT 11
+#define RCC_APB1RSTR_TIM14RST_BIT 8
+#define RCC_APB1RSTR_TIM13RST_BIT 7
+#define RCC_APB1RSTR_TIM12RST_BIT 6
+#define RCC_APB1RSTR_TIM7RST_BIT 5
+#define RCC_APB1RSTR_TIM6RST_BIT 4
+#define RCC_APB1RSTR_TIM5RST_BIT 3
+#define RCC_APB1RSTR_TIM4RST_BIT 2
+#define RCC_APB1RSTR_TIM3RST_BIT 1
+#define RCC_APB1RSTR_TIM2RST_BIT 0
+
+#define RCC_APB1RSTR_DACRST BIT(RCC_APB1RSTR_DACRST_BIT)
+#define RCC_APB1RSTR_PWRRST BIT(RCC_APB1RSTR_PWRRST_BIT)
+#define RCC_APB1RSTR_BKPRST BIT(RCC_APB1RSTR_BKPRST_BIT)
+#define RCC_APB1RSTR_CANRST BIT(RCC_APB1RSTR_CANRST_BIT)
+#define RCC_APB1RSTR_USBRST BIT(RCC_APB1RSTR_USBRST_BIT)
+#define RCC_APB1RSTR_I2C2RST BIT(RCC_APB1RSTR_I2C2RST_BIT)
+#define RCC_APB1RSTR_I2C1RST BIT(RCC_APB1RSTR_I2C1RST_BIT)
+#define RCC_APB1RSTR_UART5RST BIT(RCC_APB1RSTR_UART5RST_BIT)
+#define RCC_APB1RSTR_UART4RST BIT(RCC_APB1RSTR_UART4RST_BIT)
+#define RCC_APB1RSTR_USART3RST BIT(RCC_APB1RSTR_USART3RST_BIT)
+#define RCC_APB1RSTR_USART2RST BIT(RCC_APB1RSTR_USART2RST_BIT)
+#define RCC_APB1RSTR_SPI3RST BIT(RCC_APB1RSTR_SPI3RST_BIT)
+#define RCC_APB1RSTR_SPI2RST BIT(RCC_APB1RSTR_SPI2RST_BIT)
+#define RCC_APB1RSTR_WWDRST BIT(RCC_APB1RSTR_WWDRST_BIT)
+#define RCC_APB1RSTR_TIM14RST BIT(RCC_APB1RSTR_TIM14RST_BIT)
+#define RCC_APB1RSTR_TIM13RST BIT(RCC_APB1RSTR_TIM13RST_BIT)
+#define RCC_APB1RSTR_TIM12RST BIT(RCC_APB1RSTR_TIM12RST_BIT)
+#define RCC_APB1RSTR_TIM7RST BIT(RCC_APB1RSTR_TIM7RST_BIT)
+#define RCC_APB1RSTR_TIM6RST BIT(RCC_APB1RSTR_TIM6RST_BIT)
+#define RCC_APB1RSTR_TIM5RST BIT(RCC_APB1RSTR_TIM5RST_BIT)
+#define RCC_APB1RSTR_TIM4RST BIT(RCC_APB1RSTR_TIM4RST_BIT)
+#define RCC_APB1RSTR_TIM3RST BIT(RCC_APB1RSTR_TIM3RST_BIT)
+#define RCC_APB1RSTR_TIM2RST BIT(RCC_APB1RSTR_TIM2RST_BIT)
+
+/* AHB peripheral clock enable register */
+
+#define RCC_AHBENR_SDIOEN_BIT 10
+#define RCC_AHBENR_FSMCEN_BIT 8
+#define RCC_AHBENR_CRCEN_BIT 7
+#define RCC_AHBENR_FLITFEN_BIT 4
+#define RCC_AHBENR_SRAMEN_BIT 2
+#define RCC_AHBENR_DMA2EN_BIT 1
+#define RCC_AHBENR_DMA1EN_BIT 0
+
+#define RCC_AHBENR_SDIOEN BIT(RCC_AHBENR_SDIOEN_BIT)
+#define RCC_AHBENR_FSMCEN BIT(RCC_AHBENR_FSMCEN_BIT)
+#define RCC_AHBENR_CRCEN BIT(RCC_AHBENR_CRCEN_BIT)
+#define RCC_AHBENR_FLITFEN BIT(RCC_AHBENR_FLITFEN_BIT)
+#define RCC_AHBENR_SRAMEN BIT(RCC_AHBENR_SRAMEN_BIT)
+#define RCC_AHBENR_DMA2EN BIT(RCC_AHBENR_DMA2EN_BIT)
+#define RCC_AHBENR_DMA1EN BIT(RCC_AHBENR_DMA1EN_BIT)
+
+/* APB2 peripheral clock enable register */
+
+#define RCC_APB2ENR_TIM11EN_BIT 21
+#define RCC_APB2ENR_TIM10EN_BIT 20
+#define RCC_APB2ENR_TIM9EN_BIT 19
+#define RCC_APB2ENR_ADC3EN_BIT 15
+#define RCC_APB2ENR_USART1EN_BIT 14
+#define RCC_APB2ENR_TIM8EN_BIT 13
+#define RCC_APB2ENR_SPI1EN_BIT 12
+#define RCC_APB2ENR_TIM1EN_BIT 11
+#define RCC_APB2ENR_ADC2EN_BIT 10
+#define RCC_APB2ENR_ADC1EN_BIT 9
+#define RCC_APB2ENR_IOPGEN_BIT 8
+#define RCC_APB2ENR_IOPFEN_BIT 7
+#define RCC_APB2ENR_IOPEEN_BIT 6
+#define RCC_APB2ENR_IOPDEN_BIT 5
+#define RCC_APB2ENR_IOPCEN_BIT 4
+#define RCC_APB2ENR_IOPBEN_BIT 3
+#define RCC_APB2ENR_IOPAEN_BIT 2
+#define RCC_APB2ENR_AFIOEN_BIT 0
+
+#define RCC_APB2ENR_TIM11EN BIT(RCC_APB2ENR_TIM11EN_BIT)
+#define RCC_APB2ENR_TIM10EN BIT(RCC_APB2ENR_TIM10EN_BIT)
+#define RCC_APB2ENR_TIM9EN BIT(RCC_APB2ENR_TIM9EN_BIT)
+#define RCC_APB2ENR_ADC3EN BIT(RCC_APB2ENR_ADC3EN_BIT)
+#define RCC_APB2ENR_USART1EN BIT(RCC_APB2ENR_USART1EN_BIT)
+#define RCC_APB2ENR_TIM8EN BIT(RCC_APB2ENR_TIM8EN_BIT)
+#define RCC_APB2ENR_SPI1EN BIT(RCC_APB2ENR_SPI1EN_BIT)
+#define RCC_APB2ENR_TIM1EN BIT(RCC_APB2ENR_TIM1EN_BIT)
+#define RCC_APB2ENR_ADC2EN BIT(RCC_APB2ENR_ADC2EN_BIT)
+#define RCC_APB2ENR_ADC1EN BIT(RCC_APB2ENR_ADC1EN_BIT)
+#define RCC_APB2ENR_IOPGEN BIT(RCC_APB2ENR_IOPGEN_BIT)
+#define RCC_APB2ENR_IOPFEN BIT(RCC_APB2ENR_IOPFEN_BIT)
+#define RCC_APB2ENR_IOPEEN BIT(RCC_APB2ENR_IOPEEN_BIT)
+#define RCC_APB2ENR_IOPDEN BIT(RCC_APB2ENR_IOPDEN_BIT)
+#define RCC_APB2ENR_IOPCEN BIT(RCC_APB2ENR_IOPCEN_BIT)
+#define RCC_APB2ENR_IOPBEN BIT(RCC_APB2ENR_IOPBEN_BIT)
+#define RCC_APB2ENR_IOPAEN BIT(RCC_APB2ENR_IOPAEN_BIT)
+#define RCC_APB2ENR_AFIOEN BIT(RCC_APB2ENR_AFIOEN_BIT)
+
+/* APB1 peripheral clock enable register */
+
+#define RCC_APB1ENR_DACEN_BIT 29
+#define RCC_APB1ENR_PWREN_BIT 28
+#define RCC_APB1ENR_BKPEN_BIT 27
+#define RCC_APB1ENR_CANEN_BIT 25
+#define RCC_APB1ENR_USBEN_BIT 23
+#define RCC_APB1ENR_I2C2EN_BIT 22
+#define RCC_APB1ENR_I2C1EN_BIT 21
+#define RCC_APB1ENR_UART5EN_BIT 20
+#define RCC_APB1ENR_UART4EN_BIT 19
+#define RCC_APB1ENR_USART3EN_BIT 18
+#define RCC_APB1ENR_USART2EN_BIT 17
+#define RCC_APB1ENR_SPI3EN_BIT 15
+#define RCC_APB1ENR_SPI2EN_BIT 14
+#define RCC_APB1ENR_WWDEN_BIT 11
+#define RCC_APB1ENR_TIM14EN_BIT 8
+#define RCC_APB1ENR_TIM13EN_BIT 7
+#define RCC_APB1ENR_TIM12EN_BIT 6
+#define RCC_APB1ENR_TIM7EN_BIT 5
+#define RCC_APB1ENR_TIM6EN_BIT 4
+#define RCC_APB1ENR_TIM5EN_BIT 3
+#define RCC_APB1ENR_TIM4EN_BIT 2
+#define RCC_APB1ENR_TIM3EN_BIT 1
+#define RCC_APB1ENR_TIM2EN_BIT 0
+
+#define RCC_APB1ENR_DACEN BIT(RCC_APB1ENR_DACEN_BIT)
+#define RCC_APB1ENR_PWREN BIT(RCC_APB1ENR_PWREN_BIT)
+#define RCC_APB1ENR_BKPEN BIT(RCC_APB1ENR_BKPEN_BIT)
+#define RCC_APB1ENR_CANEN BIT(RCC_APB1ENR_CANEN_BIT)
+#define RCC_APB1ENR_USBEN BIT(RCC_APB1ENR_USBEN_BIT)
+#define RCC_APB1ENR_I2C2EN BIT(RCC_APB1ENR_I2C2EN_BIT)
+#define RCC_APB1ENR_I2C1EN BIT(RCC_APB1ENR_I2C1EN_BIT)
+#define RCC_APB1ENR_UART5EN BIT(RCC_APB1ENR_UART5EN_BIT)
+#define RCC_APB1ENR_UART4EN BIT(RCC_APB1ENR_UART4EN_BIT)
+#define RCC_APB1ENR_USART3EN BIT(RCC_APB1ENR_USART3EN_BIT)
+#define RCC_APB1ENR_USART2EN BIT(RCC_APB1ENR_USART2EN_BIT)
+#define RCC_APB1ENR_SPI3EN BIT(RCC_APB1ENR_SPI3EN_BIT)
+#define RCC_APB1ENR_SPI2EN BIT(RCC_APB1ENR_SPI2EN_BIT)
+#define RCC_APB1ENR_WWDEN BIT(RCC_APB1ENR_WWDEN_BIT)
+#define RCC_APB1ENR_TIM14EN BIT(RCC_APB1ENR_TIM14EN_BIT)
+#define RCC_APB1ENR_TIM13EN BIT(RCC_APB1ENR_TIM13EN_BIT)
+#define RCC_APB1ENR_TIM12EN BIT(RCC_APB1ENR_TIM12EN_BIT)
+#define RCC_APB1ENR_TIM7EN BIT(RCC_APB1ENR_TIM7EN_BIT)
+#define RCC_APB1ENR_TIM6EN BIT(RCC_APB1ENR_TIM6EN_BIT)
+#define RCC_APB1ENR_TIM5EN BIT(RCC_APB1ENR_TIM5EN_BIT)
+#define RCC_APB1ENR_TIM4EN BIT(RCC_APB1ENR_TIM4EN_BIT)
+#define RCC_APB1ENR_TIM3EN BIT(RCC_APB1ENR_TIM3EN_BIT)
+#define RCC_APB1ENR_TIM2EN BIT(RCC_APB1ENR_TIM2EN_BIT)
+
+/* Backup domain control register */
+
+#define RCC_BDCR_BDRST_BIT 16
+#define RCC_BDCR_RTCEN_BIT 15
+#define RCC_BDCR_LSEBYP_BIT 2
+#define RCC_BDCR_LSERDY_BIT 1
+#define RCC_BDCR_LSEON_BIT 0
+
+#define RCC_BDCR_BDRST BIT(RCC_BDCR_BDRST_BIT)
+#define RCC_BDCR_RTCEN BIT(RCC_BDCR_RTC_BIT)
+#define RCC_BDCR_RTCSEL (0x3 << 8)
+#define RCC_BDCR_RTCSEL_NONE (0x0 << 8)
+#define RCC_BDCR_RTCSEL_LSE (0x1 << 8)
+#define RCC_BDCR_RTCSEL_HSE (0x3 << 8)
+#define RCC_BDCR_LSEBYP BIT(RCC_BDCR_LSEBYP_BIT)
+#define RCC_BDCR_LSERDY BIT(RCC_BDCR_LSERDY_BIT)
+#define RCC_BDCR_LSEON BIT(RCC_BDCR_LSEON_BIT)
+
+/* Control/status register */
+
+#define RCC_CSR_LPWRRSTF_BIT 31
+#define RCC_CSR_WWDGRSTF_BIT 30
+#define RCC_CSR_IWDGRSTF_BIT 29
+#define RCC_CSR_SFTRSTF_BIT 28
+#define RCC_CSR_PORRSTF_BIT 27
+#define RCC_CSR_PINRSTF_BIT 26
+#define RCC_CSR_RMVF_BIT 24
+#define RCC_CSR_LSIRDY_BIT 1
+#define RCC_CSR_LSION_BIT 0
+
+#define RCC_CSR_LPWRRSTF BIT(RCC_CSR_LPWRRSTF_BIT)
+#define RCC_CSR_WWDGRSTF BIT(RCC_CSR_WWDGRSTF_BIT)
+#define RCC_CSR_IWDGRSTF BIT(RCC_CSR_IWDGRSTF_BIT)
+#define RCC_CSR_SFTRSTF BIT(RCC_CSR_SFTRSTF_BIT)
+#define RCC_CSR_PORRSTF BIT(RCC_CSR_PORRSTF_BIT)
+#define RCC_CSR_PINRSTF BIT(RCC_CSR_PINRSTF_BIT)
+#define RCC_CSR_RMVF BIT(RCC_CSR_RMVF_BIT)
+#define RCC_CSR_LSIRDY BIT(RCC_CSR_LSIRDY_BIT)
+#define RCC_CSR_LSION BIT(RCC_CSR_LSION_BIT)
-// RCC Devices
-enum {
+/*
+ * Convenience routines
+ */
+
+/**
+ * SYSCLK sources
+ * @see rcc_clk_init()
+ */
+typedef enum rcc_sysclk_src {
+ RCC_CLKSRC_HSI = 0x0,
+ RCC_CLKSRC_HSE = 0x1,
+ RCC_CLKSRC_PLL = 0x2,
+} rcc_sysclk_src;
+
+/**
+ * PLL entry clock source
+ * @see rcc_clk_init()
+ */
+typedef enum rcc_pllsrc {
+ RCC_PLLSRC_HSE = (0x1 << 16),
+ RCC_PLLSRC_HSI_DIV_2 = (0x0 << 16)
+} rcc_pllsrc;
+
+/**
+ * PLL multipliers
+ * @see rcc_clk_init()
+ */
+typedef enum rcc_pll_multiplier {
+ RCC_PLLMUL_2 = (0x0 << 18),
+ RCC_PLLMUL_3 = (0x1 << 18),
+ RCC_PLLMUL_4 = (0x2 << 18),
+ RCC_PLLMUL_5 = (0x3 << 18),
+ RCC_PLLMUL_6 = (0x4 << 18),
+ RCC_PLLMUL_7 = (0x5 << 18),
+ RCC_PLLMUL_8 = (0x6 << 18),
+ RCC_PLLMUL_9 = (0x7 << 18),
+ RCC_PLLMUL_10 = (0x8 << 18),
+ RCC_PLLMUL_11 = (0x9 << 18),
+ RCC_PLLMUL_12 = (0xA << 18),
+ RCC_PLLMUL_13 = (0xB << 18),
+ RCC_PLLMUL_14 = (0xC << 18),
+ RCC_PLLMUL_15 = (0xD << 18),
+ RCC_PLLMUL_16 = (0xE << 18),
+} rcc_pll_multiplier;
+
+/**
+ * @brief Identifies bus and clock line for a device
+ *
+ * Also generally useful as a unique identifier for that device.
+ */
+typedef enum {
RCC_GPIOA,
RCC_GPIOB,
RCC_GPIOC,
RCC_GPIOD,
- RCC_GPIOE, // High-density devices only (Maple Native)
- RCC_GPIOF, // High-density devices only (Maple Native)
- RCC_GPIOG, // High-density devices only (Maple Native)
RCC_AFIO,
RCC_ADC1,
RCC_ADC2,
+ RCC_ADC3,
RCC_USART1,
RCC_USART2,
RCC_USART3,
- RCC_UART4, // High-density devices only (Maple Native)
- RCC_UART5, // High-density devices only (Maple Native)
RCC_TIMER1,
RCC_TIMER2,
RCC_TIMER3,
RCC_TIMER4,
- RCC_TIMER5, // High-density devices only (Maple Native)
- RCC_TIMER6, // High-density devices only (Maple Native)
- RCC_TIMER7, // High-density devices only (Maple Native)
- RCC_TIMER8, // High-density devices only (Maple Native)
RCC_SPI1,
RCC_SPI2,
- RCC_FSMC, // High-density devices only (Maple Native)
- RCC_DAC, // High-density devices only (Maple Native)
RCC_DMA1,
- RCC_DMA2, // High-density devices only (Maple Native)
-};
+ RCC_PWR,
+ RCC_BKP,
+ RCC_I2C1,
+ RCC_I2C2,
+ RCC_CRC,
+ RCC_FLITF,
+ RCC_SRAM,
+#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
+ RCC_GPIOE,
+ RCC_GPIOF,
+ RCC_GPIOG,
+ RCC_UART4,
+ RCC_UART5,
+ RCC_TIMER5,
+ RCC_TIMER6,
+ RCC_TIMER7,
+ RCC_TIMER8,
+ RCC_FSMC,
+ RCC_DAC,
+ RCC_DMA2,
+ RCC_SDIO,
+ RCC_SPI3,
+#endif
+#ifdef STM32_XL_DENSITY
+ RCC_TIMER9,
+ RCC_TIMER10,
+ RCC_TIMER11,
+ RCC_TIMER12,
+ RCC_TIMER13,
+ RCC_TIMER14,
+#endif
+} rcc_clk_id;
+
+void rcc_clk_init(rcc_sysclk_src sysclk_src,
+ rcc_pllsrc pll_src,
+ rcc_pll_multiplier pll_mul);
+void rcc_clk_enable(rcc_clk_id device);
+void rcc_reset_dev(rcc_clk_id device);
+
+typedef enum rcc_clk_domain {
+ RCC_APB1,
+ RCC_APB2,
+ RCC_AHB
+} rcc_clk_domain;
+
+rcc_clk_domain rcc_dev_clk(rcc_clk_id device);
+/**
+ * Prescaler identifiers
+ * @see rcc_set_prescaler()
+ */
+typedef enum rcc_prescaler {
+ RCC_PRESCALER_AHB,
+ RCC_PRESCALER_APB1,
+ RCC_PRESCALER_APB2,
+ RCC_PRESCALER_USB,
+ RCC_PRESCALER_ADC
+} rcc_prescaler;
+
+/**
+ * ADC prescaler dividers
+ * @see rcc_set_prescaler()
+ */
+typedef enum adc_prescaler_divider {
+ RCC_ADCPRE_PCLK_DIV_2 = 0x0 << 14,
+ RCC_ADCPRE_PCLK_DIV_4 = 0x1 << 14,
+ RCC_ADCPRE_PCLK_DIV_6 = 0x2 << 14,
+ RCC_ADCPRE_PCLK_DIV_8 = 0x3 << 14,
+} adc_prescaler_divider;
+
+/**
+ * APB1 prescaler dividers
+ * @see rcc_set_prescaler()
+ */
+typedef enum apb1_prescaler_divider {
+ RCC_APB1_HCLK_DIV_1 = 0x0 << 8,
+ RCC_APB1_HCLK_DIV_2 = 0x4 << 8,
+ RCC_APB1_HCLK_DIV_4 = 0x5 << 8,
+ RCC_APB1_HCLK_DIV_8 = 0x6 << 8,
+ RCC_APB1_HCLK_DIV_16 = 0x7 << 8,
+} apb1_prescaler_divider;
+
+/**
+ * APB2 prescaler dividers
+ * @see rcc_set_prescaler()
+ */
+typedef enum apb2_prescaler_divider {
+ RCC_APB2_HCLK_DIV_1 = 0x0 << 11,
+ RCC_APB2_HCLK_DIV_2 = 0x4 << 11,
+ RCC_APB2_HCLK_DIV_4 = 0x5 << 11,
+ RCC_APB2_HCLK_DIV_8 = 0x6 << 11,
+ RCC_APB2_HCLK_DIV_16 = 0x7 << 11,
+} apb2_prescaler_divider;
+
+/**
+ * AHB prescaler dividers
+ * @see rcc_set_prescaler()
+ */
+typedef enum ahb_prescaler_divider {
+ RCC_AHB_SYSCLK_DIV_1 = 0x0 << 4,
+ RCC_AHB_SYSCLK_DIV_2 = 0x8 << 4,
+ RCC_AHB_SYSCLK_DIV_4 = 0x9 << 4,
+ RCC_AHB_SYSCLK_DIV_8 = 0xA << 4,
+ RCC_AHB_SYSCLK_DIV_16 = 0xB << 4,
+ RCC_AHB_SYSCLK_DIV_32 = 0xC << 4,
+ RCC_AHB_SYSCLK_DIV_64 = 0xD << 4,
+ RCC_AHB_SYSCLK_DIV_128 = 0xD << 4,
+ RCC_AHB_SYSCLK_DIV_256 = 0xE << 4,
+ RCC_AHB_SYSCLK_DIV_512 = 0xF << 4,
+} ahb_prescaler_divider;
-void rcc_clk_init(uint32 sysclk_src, uint32 pll_src, uint32 pll_mul);
-void rcc_clk_enable(uint32 dev);
-void rcc_reset_dev(uint32 dev);
-void rcc_set_prescaler(uint32 prescaler, uint32 divider);
+void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider);
#ifdef __cplusplus
} // extern "C"
diff --git a/libmaple/ring_buffer.h b/libmaple/ring_buffer.h
index a44088e..2536617 100644
--- a/libmaple/ring_buffer.h
+++ b/libmaple/ring_buffer.h
@@ -1,3 +1,29 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2011 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 ring_buffer.h
* @brief Simple circular buffer
@@ -9,25 +35,26 @@
#ifndef _RING_BUFFER_H_
#define _RING_BUFFER_H_
+#include "libmaple_types.h"
+
#ifdef __cplusplus
extern "C"{
#endif
-/* The buffer is empty when head == tail.
+/**
+ * Ring buffer type.
+ *
+ * The buffer is empty when head == tail.
*
* The buffer is full when the head is one byte in front of the tail,
* modulo buffer length.
*
* One byte is left free to distinguish empty from full. */
typedef struct ring_buffer {
- /** Buffer items are stored into */
- volatile uint8 *buf;
- /** Index of the next item to remove */
- uint16 head;
- /** Index where the next item will get inserted */
- uint16 tail;
- /** Buffer capacity minus one */
- uint16 size;
+ volatile uint8 *buf; /**< Buffer items are stored into */
+ uint16 head; /**< Index of the next item to remove */
+ uint16 tail; /**< Index where the next item will get inserted */
+ uint16 size; /**< Buffer capacity minus one */
} ring_buffer;
/**
@@ -42,17 +69,19 @@ typedef struct ring_buffer {
*
* @param buf Buffer to store items into
*/
-__attribute__((unused))
-static void rb_init(ring_buffer *rb, uint16 size, uint8 *buf) {
+static inline void rb_init(ring_buffer *rb, uint16 size, uint8 *buf) {
rb->head = 0;
rb->tail = 0;
rb->size = size - 1;
rb->buf = buf;
}
-/** Return the number of elements stored in the ring buffer. */
+/**
+ * @brief Return the number of elements stored in the ring buffer.
+ * @param rb Buffer whose elements to count.
+ */
static inline uint16 rb_full_count(ring_buffer *rb) {
- volatile ring_buffer *arb = rb;
+ __io ring_buffer *arb = rb;
int32 size = arb->tail - arb->head;
if (arb->tail < arb->head) {
size += arb->size + 1;
@@ -60,19 +89,37 @@ static inline uint16 rb_full_count(ring_buffer *rb) {
return (uint16)size;
}
-/** Return true if and only if the ring buffer is full. */
+/**
+ * @brief Returns true if and only if the ring buffer is full.
+ * @param rb Buffer to test.
+ */
static inline int rb_is_full(ring_buffer *rb) {
return (rb->tail + 1 == rb->head) ||
(rb->tail == rb->size && rb->head == 0);
}
-/** Append element onto the end of the ring buffer. */
+/**
+ * @brief Returns true if and only if the ring buffer is empty.
+ * @param rb Buffer to test.
+ */
+static inline int rb_is_empty(ring_buffer *rb) {
+ return rb->head == rb->tail;
+}
+
+/**
+ * Append element onto the end of a ring buffer.
+ * @param rb Buffer to append onto.
+ * @param element Value to append.
+ */
static inline void rb_insert(ring_buffer *rb, uint8 element) {
rb->buf[rb->tail] = element;
rb->tail = (rb->tail == rb->size) ? 0 : rb->tail + 1;
}
-/** Remove and return the first item from the ring buffer. */
+/**
+ * @brief Remove and return the first item from a ring buffer.
+ * @param rb Buffer to remove from, must contain at least one element.
+ */
static inline uint8 rb_remove(ring_buffer *rb) {
uint8 ch = rb->buf[rb->head];
rb->head = (rb->head == rb->size) ? 0 : rb->head + 1;
@@ -80,8 +127,24 @@ static inline uint8 rb_remove(ring_buffer *rb) {
}
/**
- * If rb is not full, appends element and returns true; otherwise,
- * does nothing and returns false. */
+ * @brief Attempt to remove the first item from a ring buffer.
+ *
+ * If the ring buffer is nonempty, removes and returns its first item.
+ * If it is empty, does nothing and returns a negative value.
+ *
+ * @param rb Buffer to attempt to remove from.
+ */
+static inline int16 rb_safe_remove(ring_buffer *rb) {
+ return rb_is_empty(rb) ? -1 : rb_remove(rb);
+}
+
+/**
+ * @brief Attempt to insert an element into a ring buffer.
+ *
+ * @param rb Buffer to insert into.
+ * @param element Value to insert into rb.
+ * @sideeffect If rb is not full, appends element onto buffer.
+ * @return If element was appended, then true; otherwise, false. */
static inline int rb_safe_insert(ring_buffer *rb, uint8 element) {
if (rb_is_full(rb)) {
return 0;
@@ -91,12 +154,16 @@ static inline int rb_safe_insert(ring_buffer *rb, uint8 element) {
}
/**
- * Append an item onto the end of a non-full ring buffer. If the
- * buffer is full, removes its first item, then inserts the new
+ * @brief Append an item onto the end of a non-full ring buffer.
+ *
+ * If the buffer is full, removes its first item, then inserts the new
* element at the end.
*
- * On success, returns -1. If an element was popped, returns the
- * popped value. */
+ * @param rb Ring buffer to insert into.
+ * @param element Value to insert into ring buffer.
+ * @return On success, returns -1. If an element was popped, returns
+ * the popped value.
+ */
static inline int rb_push_insert(ring_buffer *rb, uint8 element) {
int ret = -1;
if (rb_is_full(rb)) {
@@ -106,7 +173,10 @@ static inline int rb_push_insert(ring_buffer *rb, uint8 element) {
return ret;
}
-/** Discard all items from the buffer */
+/**
+ * @brief Discard all items from a ring buffer.
+ * @param rb Ring buffer to discard all items from.
+ */
static inline void rb_reset(ring_buffer *rb) {
rb->tail = rb->head;
}
@@ -115,7 +185,5 @@ static inline void rb_reset(ring_buffer *rb) {
} // extern "C"
#endif
-
-
#endif
diff --git a/libmaple/rules.mk b/libmaple/rules.mk
index b87595d..2eaea55 100644
--- a/libmaple/rules.mk
+++ b/libmaple/rules.mk
@@ -9,7 +9,7 @@ BUILDDIRS += $(BUILD_PATH)/$(d)/usb/usb_lib
LIBMAPLE_INCLUDES := -I$(LIBMAPLE_PATH) -I$(LIBMAPLE_PATH)/usb -I$(LIBMAPLE_PATH)/usb/usb_lib
# Local flags
-CFLAGS_$(d) = -I$(d) $(LIBMAPLE_INCLUDES) -D$(VECT_BASE_ADDR)
+CFLAGS_$(d) = -I$(d) $(LIBMAPLE_INCLUDES)
# Local rules and targets
cSRCS_$(d) := adc.c \
@@ -22,11 +22,13 @@ cSRCS_$(d) := adc.c \
gpio.c \
iwdg.c \
nvic.c \
+ pwr.c \
+ i2c.c \
rcc.c \
spi.c \
syscalls.c \
systick.c \
- timers.c \
+ timer.c \
usart.c \
util.c \
usb/descriptors.c \
diff --git a/libmaple/scb.h b/libmaple/scb.h
new file mode 100644
index 0000000..1911435
--- /dev/null
+++ b/libmaple/scb.h
@@ -0,0 +1,60 @@
+/* *****************************************************************************
+ * The MIT License
+ *
+ * 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.
+ * ****************************************************************************/
+
+/**
+ * @brief System control block header
+ */
+
+#ifndef _SCB_H_
+#define _SCB_H_
+
+/** System control block register map */
+typedef struct scb_reg_map {
+ __io uint32 CPUID; /**< CPU ID Base Register */
+ __io uint32 ICSR; /**< Interrupt Control State Register */
+ __io uint32 VTOR; /**< Vector Table Offset Register */
+ __io uint32 AIRCR; /**< Application Interrupt / Reset Control Register */
+ __io uint32 SCR; /**< System Control Register */
+ __io uint32 CCR; /**< Configuration Control Register */
+ __io uint8 SHP[12]; /**< System Handlers Priority Registers
+ (4-7, 8-11, 12-15) */
+ __io uint32 SHCSR; /**< System Handler Control and State Register */
+ __io uint32 CFSR; /**< Configurable Fault Status Register */
+ __io uint32 HFSR; /**< Hard Fault Status Register */
+ __io uint32 DFSR; /**< Debug Fault Status Register */
+ __io uint32 MMFAR; /**< Mem Manage Address Register */
+ __io uint32 BFAR; /**< Bus Fault Address Register */
+ __io uint32 AFSR; /**< Auxiliary Fault Status Register */
+ __io uint32 PFR[2]; /**< Processor Feature Register */
+ __io uint32 DFR; /**< Debug Feature Register */
+ __io uint32 ADR; /**< Auxiliary Feature Register */
+ __io uint32 MMFR[4]; /**< Memory Model Feature Register */
+ __io uint32 ISAR[5]; /**< ISA Feature Register */
+} scb_reg_map;
+
+/** System control block register map base pointer */
+#define SCB_BASE ((struct scb_reg_map*)0xE000ED00)
+
+#endif
+
diff --git a/libmaple/spi.c b/libmaple/spi.c
index 8bba0d6..e58ae91 100644
--- a/libmaple/spi.c
+++ b/libmaple/spi.c
@@ -3,159 +3,235 @@
*
* 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
+ * 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 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.
+ * 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.
*****************************************************************************/
/**
- * @brief libmaple serial peripheral interface (SPI) definitions
- *
- * Initial implementation for the SPI interface.
- *
- * This driver implements only the bare essentials of a polling driver at the
- * moment. Master mode only, 8-bit data frames, and polling.
- *
- * The caller is responsible for controlling the chip select line.
- *
- * TODO: interrupt based driver, DMA.
- *
+ * @file spi.c
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief Serial Peripheral Interface (SPI) support.
+ * Currently, there is no Integrated Interchip Sound (I2S) support.
*/
-#include "libmaple.h"
-#include "gpio.h"
-#include "rcc.h"
#include "spi.h"
+#include "bitband.h"
-typedef struct spi_dev {
- SPI *base;
- GPIO_Port *port;
- uint8 sck_pin;
- uint8 miso_pin;
- uint8 mosi_pin;
-} spi_dev;
-
-static const spi_dev spi_dev1 = {
- .base = (SPI*)SPI1_BASE,
- .port = GPIOA_BASE,
- .sck_pin = 5,
- .miso_pin = 6,
- .mosi_pin = 7
-};
+static void spi_reconfigure(spi_dev *dev, uint32 cr1_config);
+
+/*
+ * SPI devices
+ */
-static const spi_dev spi_dev2 = {
- .base = (SPI*)SPI2_BASE,
- .port = GPIOB_BASE,
- .sck_pin = 13,
- .miso_pin = 14,
- .mosi_pin = 15
+static spi_dev spi1 = {
+ .regs = SPI1_BASE,
+ .clk_id = RCC_SPI1,
+ .irq_num = NVIC_SPI1,
};
+spi_dev *SPI1 = &spi1;
-static void spi_gpio_cfg(const spi_dev *dev);
+static spi_dev spi2 = {
+ .regs = SPI2_BASE,
+ .clk_id = RCC_SPI2,
+ .irq_num = NVIC_SPI2,
+};
+spi_dev *SPI2 = &spi2;
-/**
- * @brief Initialize a spi peripheral
- * @param spi_num which spi to turn on, SPI1 or SPI2?
- * @param prescale prescale factor on the input clock.
- * @param endian data frame format (LSBFIRST?)
- * @param mode SPI mode number
- */
-void spi_init(uint32 spi_num,
- uint32 prescale,
- uint32 endian,
- uint32 mode) {
- ASSERT(spi_num == 1 || spi_num == 2);
- ASSERT(mode < 4);
-
- SPI *spi;
- uint32 cr1 = 0;
-
- switch (spi_num) {
- case 1:
- /* limit to 18 mhz max speed */
- ASSERT(prescale != CR1_BR_PRESCALE_2);
- spi = (SPI*)SPI1_BASE;
- rcc_clk_enable(RCC_SPI1);
- spi_gpio_cfg(&spi_dev1);
- break;
- case 2:
- spi = (SPI*)SPI2_BASE;
- rcc_clk_enable(RCC_SPI2);
- spi_gpio_cfg(&spi_dev2);
- break;
- }
+#ifdef STM32_HIGH_DENSITY
+static spi_dev spi3 = {
+ .regs = SPI3_BASE,
+ .clk_id = RCC_SPI3,
+ .irq_num = NVIC_SPI3,
+};
+spi_dev *SPI3 = &spi3;
+#endif
- cr1 = prescale | endian | mode | CR1_MSTR | CR1_SSI | CR1_SSM;
- spi->CR1 = cr1;
+/*
+ * SPI convenience routines
+ */
- /* Peripheral enable */
- spi->CR1 |= CR1_SPE;
+/**
+ * @brief Initialize and reset a SPI device.
+ * @param dev Device to initialize and reset.
+ */
+void spi_init(spi_dev *dev) {
+ rcc_clk_enable(dev->clk_id);
+ rcc_reset_dev(dev->clk_id);
}
/**
- * @brief SPI synchronous 8-bit write, blocking.
- * @param spi_num which spi to send on
- * @return data shifted back from the slave
+ * @brief Configure GPIO bit modes for use as a SPI port's pins.
+ * @param as_master If true, configure bits for use as a bus master.
+ * Otherwise, configure bits for use as slave.
+ * @param nss_dev NSS pin's GPIO device
+ * @param comm_dev SCK, MISO, MOSI pins' GPIO device
+ * @param nss_bit NSS pin's GPIO bit on nss_dev
+ * @param sck_bit SCK pin's GPIO bit on comm_dev
+ * @param miso_bit MISO pin's GPIO bit on comm_dev
+ * @param mosi_bit MOSI pin's GPIO bit on comm_dev
*/
-uint8 spi_tx_byte(uint32 spi_num, uint8 data) {
- SPI *spi;
+void spi_gpio_cfg(uint8 as_master,
+ gpio_dev *nss_dev,
+ uint8 nss_bit,
+ gpio_dev *comm_dev,
+ uint8 sck_bit,
+ uint8 miso_bit,
+ uint8 mosi_bit) {
+ if (as_master) {
+ gpio_set_mode(nss_dev, nss_bit, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(comm_dev, sck_bit, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(comm_dev, miso_bit, GPIO_INPUT_FLOATING);
+ gpio_set_mode(comm_dev, mosi_bit, GPIO_AF_OUTPUT_PP);
+ } else {
+ gpio_set_mode(nss_dev, nss_bit, GPIO_INPUT_FLOATING);
+ gpio_set_mode(comm_dev, sck_bit, GPIO_INPUT_FLOATING);
+ gpio_set_mode(comm_dev, miso_bit, GPIO_AF_OUTPUT_PP);
+ gpio_set_mode(comm_dev, mosi_bit, GPIO_INPUT_FLOATING);
+ }
+}
- spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE;
+/**
+ * @brief Configure and enable a SPI device as bus master.
+ *
+ * The device's peripheral will be disabled before being reconfigured.
+ *
+ * @param dev Device to configure as bus master
+ * @param baud Bus baud rate
+ * @param mode SPI mode
+ * @param flags Logical OR of spi_cfg_flag values.
+ * @see spi_cfg_flag
+ */
+void spi_master_enable(spi_dev *dev,
+ spi_baud_rate baud,
+ spi_mode mode,
+ uint32 flags) {
+ spi_reconfigure(dev, baud | flags | SPI_CR1_MSTR | mode);
+}
- while (!(spi->SR & SR_TXE))
- ;
+/**
+ * @brief Configure and enable a SPI device as a bus slave.
+ *
+ * The device's peripheral will be disabled before being reconfigured.
+ *
+ * @param dev Device to configure as a bus slave
+ * @param mode SPI mode
+ * @param flags Logical OR of spi_cfg_flag values.
+ * @see spi_cfg_flag
+ */
+void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) {
+ spi_reconfigure(dev, flags | mode);
+}
- spi->DR = data;
+/**
+ * @brief Nonblocking SPI transmit.
+ * @param dev SPI port to use for transmission
+ * @param buf Buffer to transmit. The sizeof buf's elements are
+ * inferred from dev's data frame format (i.e., are
+ * correctly treated as 8-bit or 16-bit quantities).
+ * @param len Maximum number of elements to transmit.
+ * @return Number of elements transmitted.
+ */
+uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len) {
+ uint32 txed = 0;
+ uint8 byte_frame = spi_dff(dev) == SPI_DFF_8_BIT;
+ while (spi_is_tx_empty(dev) && (txed < len)) {
+ if (byte_frame) {
+ dev->regs->DR = ((const uint8*)buf)[txed++];
+ } else {
+ dev->regs->DR = ((const uint16*)buf)[txed++];
+ }
+ }
+ return txed;
+}
- while (!(spi->SR & SR_RXNE))
- ;
+/**
+ * @brief Call a function on each SPI port
+ * @param fn Function to call.
+ */
+void spi_foreach(void (*fn)(spi_dev*)) {
+ fn(SPI1);
+ fn(SPI2);
+#ifdef STM32_HIGH_DENSITY
+ fn(SPI3);
+#endif
+}
- return spi->DR;
+/**
+ * @brief Enable a SPI peripheral
+ * @param dev Device to enable
+ */
+void spi_peripheral_enable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR1, SPI_CR1_SPE_BIT, 1);
}
-uint8 spi_tx(uint32 spi_num, uint8 *buf, uint32 len) {
- SPI *spi;
- uint32 i = 0;
- uint8 rc;
+/**
+ * @brief Disable a SPI peripheral
+ * @param dev Device to disable
+ */
+void spi_peripheral_disable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR1, SPI_CR1_SPE_BIT, 0);
+}
- ASSERT(spi_num == 1 || spi_num == 2);
- spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE;
+/**
+ * @brief Enable DMA requests whenever the transmit buffer is empty
+ * @param dev SPI device on which to enable TX DMA requests
+ */
+void spi_tx_dma_enable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_TXDMAEN_BIT, 1);
+}
- if (!len) {
- return 0;
- }
+/**
+ * @brief Disable DMA requests whenever the transmit buffer is empty
+ * @param dev SPI device on which to disable TX DMA requests
+ */
+void spi_tx_dma_disable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_TXDMAEN_BIT, 0);
+}
- while (i < len) {
- while (!(spi->SR & SR_TXE))
- ;
+/**
+ * @brief Enable DMA requests whenever the receive buffer is empty
+ * @param dev SPI device on which to enable RX DMA requests
+ */
+void spi_rx_dma_enable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_RXDMAEN_BIT, 1);
+}
- spi->DR = buf[i];
+/**
+ * @brief Disable DMA requests whenever the receive buffer is empty
+ * @param dev SPI device on which to disable RX DMA requests
+ */
+void spi_rx_dma_disable(spi_dev *dev) {
+ bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_RXDMAEN_BIT, 0);
+}
- while (!(spi->SR & SR_RXNE))
- ;
+/*
+ * SPI auxiliary routines
+ */
- rc = spi->DR;
- i++;
- }
- return rc;
+static void spi_reconfigure(spi_dev *dev, uint32 cr1_config) {
+ spi_irq_disable(dev, SPI_INTERRUPTS_ALL);
+ spi_peripheral_disable(dev);
+ dev->regs->CR1 = cr1_config;
+ spi_peripheral_enable(dev);
}
-static void spi_gpio_cfg(const spi_dev *dev) {
- gpio_set_mode(dev->port, dev->sck_pin, GPIO_MODE_AF_OUTPUT_PP);
- gpio_set_mode(dev->port, dev->miso_pin, GPIO_MODE_AF_OUTPUT_PP);
- gpio_set_mode(dev->port, dev->mosi_pin, GPIO_MODE_AF_OUTPUT_PP);
-}
+/*
+ * IRQ handlers (TODO)
+ */
diff --git a/libmaple/spi.h b/libmaple/spi.h
index db8aa9c..37ca6ef 100644
--- a/libmaple/spi.h
+++ b/libmaple/spi.h
@@ -24,96 +24,434 @@
/**
* @file spi.h
- * @brief libmaple serial peripheral interface (SPI) prototypes and
- * declarations
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief Serial Peripheral Interface (SPI) and Integrated
+ * Interchip Sound (I2S) peripheral support.
+ *
+ * I2S support is currently limited to register maps and bit definitions.
*/
#ifndef _SPI_H_
#define _SPI_H_
+#include "libmaple_types.h"
+#include "rcc.h"
+#include "nvic.h"
+#include "gpio.h"
+#include "util.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-/* peripheral addresses */
-#define SPI1_BASE 0x40013000
-#define SPI2_BASE 0x40003800
-#define SPI3_BASE 0x40003C00
-
-/* baud rate prescaler bits */
-#define CR1_BR 0x00000038
-#define CR1_BR_PRESCALE_2 0x00000000
-#define CR1_BR_PRESCALE_4 0x00000008
-#define CR1_BR_PRESCALE_8 0x00000010
-#define CR1_BR_PRESCALE_16 0x00000018
-#define CR1_BR_PRESCALE_32 0x00000020
-#define CR1_BR_PRESCALE_64 0x00000028
-#define CR1_BR_PRESCALE_128 0x00000030
-#define CR1_BR_PRESCALE_256 0x00000038
-
-#define CR1_LSBFIRST BIT(7) // data frame format
-#define CR1_MSTR BIT(2) // master selection
-#define CR1_SSM BIT(9) // software slave management
-#define CR1_SSI BIT(8) // internal slave select
-#define CR1_SPE BIT(6) // peripheral enable
-
-/* Status register bits */
-#define SR_RXNE BIT(0) // rx buffer not empty
-#define SR_TXE BIT(1) // transmit buffer empty
-#define SR_BSY BIT(7) // busy flag
-
-typedef struct SPI {
- __io uint16 CR1;
- uint16 pad0;
- __io uint8 CR2;
- uint8 pad1[3];
- __io uint8 SR;
- uint8 pad2[3];
- __io uint16 DR;
- uint16 pad3;
- __io uint16 CRCPR;
- uint16 pad4;
- __io uint16 RXCRCR;
- uint16 pad5;
- __io uint16 TXCRCR;
- uint16 pad6;
-} SPI;
-
-enum {
- SPI_MSBFIRST = 0,
- SPI_LSBFIRST = BIT(7),
-};
-
-enum {
- SPI_PRESCALE_2 = (0x0 << 3),
- SPI_PRESCALE_4 = (0x1 << 3),
- SPI_PRESCALE_8 = (0x2 << 3),
- SPI_PRESCALE_16 = (0x3 << 3),
- SPI_PRESCALE_32 = (0x4 << 3),
- SPI_PRESCALE_64 = (0x5 << 3),
- SPI_PRESCALE_128 = (0x6 << 3),
- SPI_PRESCALE_256 = (0x7 << 3)
-};
-
-void spi_init(uint32 spi_num,
- uint32 prescale,
- uint32 endian,
- uint32 mode);
-uint8 spi_tx_byte(uint32 spi_num, uint8 data);
-uint8 spi_tx(uint32 spi_num, uint8 *buf, uint32 len);
-
-static inline uint8 spi_rx(uint32 spi_num) {
- SPI *spi;
-
- ASSERT(spi_num == 1 || spi_num == 2);
- spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE;
-
- return spi->DR;
+/*
+ * Register maps
+ */
+
+/** SPI register map type. */
+typedef struct spi_reg_map {
+ __io uint32 CR1; /**< Control register 1 */
+ __io uint32 CR2; /**< Control register 2 */
+ __io uint32 SR; /**< Status register */
+ __io uint32 DR; /**< Data register */
+ __io uint32 CRCPR; /**< CRC polynomial register */
+ __io uint32 RXCRCR; /**< RX CRC register */
+ __io uint32 TXCRCR; /**< TX CRC register */
+ __io uint32 I2SCFGR; /**< I2S configuration register */
+ __io uint32 I2SPR; /**< I2S prescaler register */
+} spi_reg_map;
+
+/** SPI1 register map base pointer */
+#define SPI1_BASE ((struct spi_reg_map*)0x40013000)
+/** SPI2 register map base pointer */
+#define SPI2_BASE ((struct spi_reg_map*)0x40003800)
+/** SPI3 register map base pointer */
+#define SPI3_BASE ((struct spi_reg_map*)0x40003C00)
+
+/*
+ * Register bit definitions
+ */
+
+/* Control register 1 */
+
+#define SPI_CR1_BIDIMODE_BIT 15
+#define SPI_CR1_BIDIOE_BIT 14
+#define SPI_CR1_CRCEN_BIT 13
+#define SPI_CR1_CRCNEXT_BIT 12
+#define SPI_CR1_DFF_BIT 11
+#define SPI_CR1_RXONLY_BIT 10
+#define SPI_CR1_SSM_BIT 9
+#define SPI_CR1_SSI_BIT 8
+#define SPI_CR1_LSBFIRST_BIT 7
+#define SPI_CR1_SPE_BIT 6
+#define SPI_CR1_MSTR_BIT 2
+#define SPI_CR1_CPOL_BIT 1
+#define SPI_CR1_CPHA_BIT 0
+
+#define SPI_CR1_BIDIMODE BIT(SPI_CR1_BIDIMODE_BIT)
+#define SPI_CR1_BIDIMODE_2_LINE (0x0 << SPI_CR1_BIDIMODE_BIT)
+#define SPI_CR1_BIDIMODE_1_LINE (0x1 << SPI_CR1_BIDIMODE_BIT)
+#define SPI_CR1_BIDIOE BIT(SPI_CR1_BIDIOE_BIT)
+#define SPI_CR1_CRCEN BIT(SPI_CR1_CRCEN_BIT)
+#define SPI_CR1_CRCNEXT BIT(SPI_CR1_CRCNEXT_BIT)
+#define SPI_CR1_DFF BIT(SPI_CR1_DFF_BIT)
+#define SPI_CR1_DFF_8_BIT (0x0 << SPI_CR1_DFF_BIT)
+#define SPI_CR1_DFF_16_BIT (0x1 << SPI_CR1_DFF_BIT)
+#define SPI_CR1_RXONLY BIT(SPI_CR1_RXONLY_BIT)
+#define SPI_CR1_SSM BIT(SPI_CR1_SSM_BIT)
+#define SPI_CR1_SSI BIT(SPI_CR1_SSI_BIT)
+#define SPI_CR1_LSBFIRST BIT(SPI_CR1_LSBFIRST_BIT)
+#define SPI_CR1_SPE BIT(SPI_CR1_SPE_BIT)
+#define SPI_CR1_BR (0x7 << 3)
+#define SPI_CR1_BR_PCLK_DIV_2 (0x0 << 3)
+#define SPI_CR1_BR_PCLK_DIV_4 (0x1 << 3)
+#define SPI_CR1_BR_PCLK_DIV_8 (0x2 << 3)
+#define SPI_CR1_BR_PCLK_DIV_16 (0x3 << 3)
+#define SPI_CR1_BR_PCLK_DIV_32 (0x4 << 3)
+#define SPI_CR1_BR_PCLK_DIV_64 (0x5 << 3)
+#define SPI_CR1_BR_PCLK_DIV_128 (0x6 << 3)
+#define SPI_CR1_BR_PCLK_DIV_256 (0x7 << 3)
+#define SPI_CR1_MSTR BIT(SPI_CR1_MSTR_BIT)
+#define SPI_CR1_CPOL BIT(SPI_CR1_CPOL_BIT)
+#define SPI_CR1_CPOL_LOW (0x0 << SPI_CR1_CPOL_BIT)
+#define SPI_CR1_CPOL_HIGH (0x1 << SPI_CR1_CPOL_BIT)
+#define SPI_CR1_CPHA BIT(SPI_CR1_CPHA_BIT)
+
+/* Control register 2 */
+
+/* RM0008-ism: SPI CR2 has "TXDMAEN" and "RXDMAEN" bits, while the
+ * USARTs have CR3 "DMAR" and "DMAT" bits. */
+
+#define SPI_CR2_TXEIE_BIT 7
+#define SPI_CR2_RXNEIE_BIT 6
+#define SPI_CR2_ERRIE_BIT 5
+#define SPI_CR2_SSOE_BIT 2
+#define SPI_CR2_TXDMAEN_BIT 1
+#define SPI_CR2_RXDMAEN_BIT 0
+
+#define SPI_CR2_TXEIE BIT(SPI_CR2_TXEIE_BIT)
+#define SPI_CR2_RXNEIE BIT(SPI_CR2_RXNEIE_BIT)
+#define SPI_CR2_ERRIE BIT(SPI_CR2_ERRIE_BIT)
+#define SPI_CR2_SSOE BIT(SPI_CR2_SSOE_BIT)
+#define SPI_CR2_TXDMAEN BIT(SPI_CR2_TXDMAEN_BIT)
+#define SPI_CR2_RXDMAEN BIT(SPI_CR2_RXDMAEN_BIT)
+
+/* Status register */
+
+#define SPI_SR_BSY_BIT 7
+#define SPI_SR_OVR_BIT 6
+#define SPI_SR_MODF_BIT 5
+#define SPI_SR_CRCERR_BIT 4
+#define SPI_SR_UDR_BIT 3
+#define SPI_SR_CHSIDE_BIT 2
+#define SPI_SR_TXE_BIT 1
+#define SPI_SR_RXNE_BIT 0
+
+#define SPI_SR_BSY BIT(SPI_SR_BSY_BIT)
+#define SPI_SR_OVR BIT(SPI_SR_OVR_BIT)
+#define SPI_SR_MODF BIT(SPI_SR_MODF_BIT)
+#define SPI_SR_CRCERR BIT(SPI_SR_CRCERR_BIT)
+#define SPI_SR_UDR BIT(SPI_SR_UDR_BIT)
+#define SPI_SR_CHSIDE BIT(SPI_SR_CHSIDE_BIT)
+#define SPI_SR_CHSIDE_LEFT (0x0 << SPI_SR_CHSIDE_BIT)
+#define SPI_SR_CHSIDE_RIGHT (0x1 << SPI_SR_CHSIDE_BIT)
+#define SPI_SR_TXE BIT(SPI_SR_TXE_BIT)
+#define SPI_SR_RXNE BIT(SPI_SR_RXNE_BIT)
+
+/* I2S configuration register */
+
+/* RM0008-ism: CR1 has "CPOL", I2SCFGR has "CKPOL". */
+
+#define SPI_I2SCFGR_I2SMOD_BIT 11
+#define SPI_I2SCFGR_I2SE_BIT 10
+#define SPI_I2SCFGR_PCMSYNC_BIT 7
+#define SPI_I2SCFGR_CKPOL_BIT 3
+#define SPI_I2SCFGR_CHLEN_BIT 0
+
+#define SPI_I2SCFGR_I2SMOD BIT(SPI_I2SCFGR_I2SMOD_BIT)
+#define SPI_I2SCFGR_I2SMOD_SPI (0x0 << SPI_I2SCFGR_I2SMOD_BIT)
+#define SPI_I2SCFGR_I2SMOD_I2S (0x1 << SPI_I2SCFGR_I2SMOD_BIT)
+#define SPI_I2SCFGR_I2SE BIT(SPI_I2SCFGR_I2SE_BIT)
+#define SPI_I2SCFGR_I2SCFG (0x3 << 8)
+#define SPI_I2SCFGR_I2SCFG_SLAVE_TX (0x0 << 8)
+#define SPI_I2SCFGR_I2SCFG_SLAVE_RX (0x1 << 8)
+#define SPI_I2SCFGR_I2SCFG_MASTER_TX (0x2 << 8)
+#define SPI_I2SCFGR_I2SCFG_MASTER_RX (0x3 << 8)
+#define SPI_I2SCFGR_PCMSYNC BIT(SPI_I2SCFGR_PCMSYNC_BIT)
+#define SPI_I2SCFGR_PCMSYNC_SHORT (0x0 << SPI_I2SCFGR_PCMSYNC_BIT)
+#define SPI_I2SCFGR_PCMSYNC_LONG (0x1 << SPI_I2SCFGR_PCMSYNC_BIT)
+#define SPI_I2SCFGR_I2SSTD (0x3 << 4)
+#define SPI_I2SCFGR_I2SSTD_PHILLIPS (0x0 << 4)
+#define SPI_I2SCFGR_I2SSTD_MSB (0x1 << 4)
+#define SPI_I2SCFGR_I2SSTD_LSB (0x2 << 4)
+#define SPI_I2SCFGR_I2SSTD_PCM (0x3 << 4)
+#define SPI_I2SCFGR_CKPOL BIT(SPI_I2SCFGR_CKPOL_BIT)
+#define SPI_I2SCFGR_CKPOL_LOW (0x0 << SPI_I2SCFGR_CKPOL_BIT)
+#define SPI_I2SCFGR_CKPOL_HIGH (0x1 << SPI_I2SCFGR_CKPOL_BIT)
+#define SPI_I2SCFGR_DATLEN (0x3 << 1)
+#define SPI_I2SCFGR_DATLEN_16_BIT (0x0 << 1)
+#define SPI_I2SCFGR_DATLEN_24_BIT (0x1 << 1)
+#define SPI_I2SCFGR_DATLEN_32_BIT (0x2 << 1)
+#define SPI_I2SCFGR_CHLEN BIT(SPI_I2SCFGR_CHLEN_BIT)
+#define SPI_I2SCFGR_CHLEN_16_BIT (0x0 << SPI_I2SCFGR_CHLEN_BIT)
+#define SPI_I2SCFGR_CHLEN_32_BIT (0x1 << SPI_I2SCFGR_CHLEN_BIT)
+
+/*
+ * Devices
+ */
+
+/** SPI device type */
+typedef struct spi_dev {
+ spi_reg_map *regs;
+ rcc_clk_id clk_id;
+ nvic_irq_num irq_num;
+} spi_dev;
+
+/** SPI device 1 */
+extern spi_dev *SPI1;
+/** SPI device 2 */
+extern spi_dev *SPI2;
+#ifdef STM32_HIGH_DENSITY
+/** SPI device 3 */
+extern spi_dev *SPI3;
+#endif
+
+/*
+ * SPI Convenience functions
+ */
+
+void spi_init(spi_dev *dev);
+
+void spi_gpio_cfg(uint8 as_master,
+ gpio_dev *nss_dev,
+ uint8 nss_bit,
+ gpio_dev *comm_dev,
+ uint8 sck_bit,
+ uint8 miso_bit,
+ uint8 mosi_bit);
+
+/**
+ * @brief SPI mode configuration.
+ *
+ * Determines a combination of clock polarity (CPOL), which determines
+ * idle state of the clock line, and clock phase (CPHA), which
+ * determines which clock edge triggers data capture.
+ */
+typedef enum {
+ SPI_MODE_0, /**< Clock line idles low (0), data capture on first
+ clock transition. */
+ SPI_MODE_1, /**< Clock line idles low (0), data capture on second
+ clock transition */
+ SPI_MODE_2, /**< Clock line idles high (1), data capture on first
+ clock transition. */
+ SPI_MODE_3 /**< Clock line idles high (1), data capture on
+ second clock transition. */
+} spi_mode;
+
+/**
+ * @brief SPI baud rate configuration, as a divisor of f_PCLK, the
+ * PCLK clock frequency.
+ */
+typedef enum {
+ SPI_BAUD_PCLK_DIV_2 = SPI_CR1_BR_PCLK_DIV_2, /**< f_PCLK/2 */
+ SPI_BAUD_PCLK_DIV_4 = SPI_CR1_BR_PCLK_DIV_4, /**< f_PCLK/4 */
+ SPI_BAUD_PCLK_DIV_8 = SPI_CR1_BR_PCLK_DIV_8, /**< f_PCLK/8 */
+ SPI_BAUD_PCLK_DIV_16 = SPI_CR1_BR_PCLK_DIV_16, /**< f_PCLK/16 */
+ SPI_BAUD_PCLK_DIV_32 = SPI_CR1_BR_PCLK_DIV_32, /**< f_PCLK/32 */
+ SPI_BAUD_PCLK_DIV_64 = SPI_CR1_BR_PCLK_DIV_64, /**< f_PCLK/64 */
+ SPI_BAUD_PCLK_DIV_128 = SPI_CR1_BR_PCLK_DIV_128, /**< f_PCLK/128 */
+ SPI_BAUD_PCLK_DIV_256 = SPI_CR1_BR_PCLK_DIV_256, /**< f_PCLK/256 */
+} spi_baud_rate;
+
+/**
+ * @brief SPI initialization flags.
+ * @see spi_master_enable()
+ * @see spi_slave_enable()
+ */
+typedef enum {
+ SPI_BIDIMODE = SPI_CR1_BIDIMODE, /**< Bidirectional mode enable */
+ SPI_BIDIOE = SPI_CR1_BIDIOE, /**< Output enable in bidirectional
+ mode */
+ SPI_CRCEN = SPI_CR1_CRCEN, /**< Cyclic redundancy check (CRC)
+ enable */
+ SPI_DFF_8_BIT = SPI_CR1_DFF_8_BIT, /**< 8-bit data frame format (this is
+ the default) */
+ SPI_DFF_16_BIT = SPI_CR1_DFF_16_BIT, /**< 16-bit data frame format */
+ SPI_RX_ONLY = SPI_CR1_RXONLY, /**< Receive only */
+ SPI_SW_SLAVE = SPI_CR1_SSM, /**< Software slave management */
+ SPI_SOFT_SS = SPI_CR1_SSI, /**< Software (internal) slave
+ select. This flag only has an
+ effect when used in combination
+ with SPI_SW_SLAVE. */
+ SPI_FRAME_LSB = SPI_CR1_LSBFIRST, /**< LSB-first (little-endian) frame
+ format */
+ SPI_FRAME_MSB = 0, /**< MSB-first (big-endian) frame
+ format (this is the default) */
+} spi_cfg_flag;
+
+void spi_master_enable(spi_dev *dev,
+ spi_baud_rate baud,
+ spi_mode mode,
+ uint32 flags);
+
+void spi_slave_enable(spi_dev *dev,
+ spi_mode mode,
+ uint32 flags);
+
+uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len);
+
+void spi_foreach(void (*fn)(spi_dev (*dev)));
+
+void spi_peripheral_enable(spi_dev *dev);
+void spi_peripheral_disable(spi_dev *dev);
+
+void spi_tx_dma_enable(spi_dev *dev);
+void spi_tx_dma_disable(spi_dev *dev);
+
+void spi_rx_dma_enable(spi_dev *dev);
+void spi_rx_dma_disable(spi_dev *dev);
+
+/**
+ * @brief Determine if a SPI peripheral is enabled.
+ * @param dev SPI device
+ * @return True, if and only if dev's peripheral is enabled.
+ */
+static inline uint8 spi_is_enabled(spi_dev *dev) {
+ return dev->regs->CR1 & SPI_CR1_SPE_BIT;
+}
+
+/**
+ * @brief Disable all SPI peripherals
+ */
+static inline void spi_peripheral_disable_all(void) {
+ spi_foreach(spi_peripheral_disable);
+}
+
+/** Available SPI interrupts */
+typedef enum {
+ SPI_TXE_INTERRUPT = SPI_CR2_TXEIE, /**< TX buffer empty interrupt */
+ SPI_RXNE_INTERRUPT = SPI_CR2_RXNEIE, /**< RX buffer not empty interrupt */
+ SPI_ERR_INTERRUPT = SPI_CR2_ERRIE /**<
+ * Error interrupt (CRC, overrun,
+ * and mode fault errors for SPI;
+ * underrun, overrun errors for I2S)
+ */
+} spi_interrupt;
+
+/**
+ * @brief Mask for all spi_interrupt values
+ * @see spi_interrupt
+ */
+#define SPI_INTERRUPTS_ALL (SPI_TXE_INTERRUPT | \
+ SPI_RXNE_INTERRUPT | \
+ SPI_ERR_INTERRUPT)
+
+/**
+ * @brief Enable SPI interrupt requests
+ * @param dev SPI device
+ * @param interrupt_flags Bitwise OR of spi_interrupt values to enable
+ * @see spi_interrupt
+ */
+static inline void spi_irq_enable(spi_dev *dev, uint32 interrupt_flags) {
+ dev->regs->CR2 |= interrupt_flags;
+ nvic_irq_enable(dev->irq_num);
+}
+
+/**
+ * @brief Disable SPI interrupt requests
+ * @param dev SPI device
+ * @param interrupt_flags Bitwise OR of spi_interrupt values to disable
+ * @see spi_interrupt
+ */
+static inline void spi_irq_disable(spi_dev *dev, uint32 interrupt_flags) {
+ dev->regs->CR2 &= ~interrupt_flags;
+}
+
+/**
+ * @brief Get the data frame format flags with which a SPI port is
+ * configured.
+ * @param dev SPI device whose data frame format to get.
+ * @return SPI_DFF_8_BIT, if dev has an 8-bit data frame format.
+ * Otherwise, SPI_DFF_16_BIT.
+ */
+static inline spi_cfg_flag spi_dff(spi_dev *dev) {
+ return ((dev->regs->CR1 & SPI_CR1_DFF) == SPI_CR1_DFF_8_BIT ?
+ SPI_DFF_8_BIT :
+ SPI_DFF_16_BIT);
+}
+
+/**
+ * @brief Determine whether the device's peripheral receive (RX)
+ * register is empty.
+ * @param dev SPI device
+ * @return true, iff dev's RX register is empty.
+ */
+static inline uint8 spi_is_rx_nonempty(spi_dev *dev) {
+ return dev->regs->SR & SPI_SR_RXNE;
+}
+
+/**
+ * @brief Retrieve the contents of the device's peripheral receive
+ * (RX) register.
+ *
+ * You may only call this function when the RX register is nonempty.
+ * Calling this function clears the contents of the RX register.
+ *
+ * @param dev SPI device
+ * @return Contents of dev's peripheral RX register
+ * @see spi_is_rx_reg_nonempty()
+ */
+static inline uint16 spi_rx_reg(spi_dev *dev) {
+ return (uint16)dev->regs->DR;
+}
+
+/**
+ * @brief Determine whether the device's peripheral transmit (TX)
+ * register is empty.
+ * @param dev SPI device
+ * @return true, iff dev's TX register is empty.
+ */
+static inline uint8 spi_is_tx_empty(spi_dev *dev) {
+ return dev->regs->SR & SPI_SR_TXE;
+}
+
+/**
+ * @brief Load a value into the device's peripheral transmit (TX) register.
+ *
+ * You may only call this function when the TX register is empty.
+ * Calling this function loads val into the peripheral's TX register.
+ * If the device is properly configured, this will initiate a
+ * transmission, the completion of which will cause the TX register to
+ * be empty again.
+ *
+ * @param dev SPI device
+ * @param val Value to load into the TX register. If the SPI data
+ * frame format is 8 bit, the value must be right-aligned.
+ * @see spi_is_tx_reg_empty()
+ * @see spi_init()
+ * @see spi_master_enable()
+ * @see spi_slave_enable()
+ */
+static inline void spi_tx_reg(spi_dev *dev, uint16 val) {
+ dev->regs->DR = val;
+}
+
+/**
+ * @brief Determine whether the device's peripheral busy (SPI_SR_BSY)
+ * flag is set.
+ * @param dev SPI device
+ * @return true, iff dev's BSY flag is set.
+ */
+static inline uint8 spi_is_busy(spi_dev *dev) {
+ return dev->regs->SR & SPI_SR_BSY;
}
+/*
+ * I2S convenience functions (TODO)
+ */
+
#ifdef __cplusplus
}
#endif
#endif
-
diff --git a/libmaple/stm32.h b/libmaple/stm32.h
new file mode 100644
index 0000000..21c18df
--- /dev/null
+++ b/libmaple/stm32.h
@@ -0,0 +1,22 @@
+/**
+ * @brief General STM32 specific definitions
+ */
+
+#ifndef _STM32_H_
+#define _STM32_H_
+
+#define PCLK1 36000000U
+#define PCLK2 72000000U
+
+#ifdef STM32_MEDIUM_DENSITY
+ #define NR_INTERRUPTS 43
+#else
+#ifdef STM32_HIGH_DENSITY
+ #define NR_INTERRUPTS 60
+#else
+#error "No STM32 board type defined!"
+#endif
+#endif
+
+#endif
+
diff --git a/libmaple/systick.c b/libmaple/systick.c
index b056001..c04f4f3 100644
--- a/libmaple/systick.c
+++ b/libmaple/systick.c
@@ -26,44 +26,46 @@
* @brief System timer interrupt handler and initialization routines
*/
-#include "libmaple.h"
#include "systick.h"
-#define SYSTICK_RELOAD 0xE000E014 // Reload value register
-#define SYSTICK_CNT 0xE000E018 // Current value register
-#define SYSTICK_CALIB 0xE000E01C // Calibration value register
-
-#define SYSTICK_SRC_HCLK BIT(2) // Use core clock
-#define SYSTICK_TICKINT BIT(1) // Interrupt on systick countdown
-#define SYSTICK_ENABLE BIT(0) // Turn on the counter
-
volatile uint32 systick_timer_millis;
+/**
+ * @brief Initialize and enable SysTick.
+ *
+ * Clocks the system timer with the core clock, turns it on, and
+ * enables interrupts.
+ *
+ * @param reload_val Appropriate reload counter to tick every 1 ms.
+ */
void systick_init(uint32 reload_val) {
- /* Set the reload counter to tick every 1ms */
- __write(SYSTICK_RELOAD, reload_val);
-
- /* Clock the system timer with the core clock and turn it on,
- * interrrupt every 1ms to keep track of millis() */
- __write(SYSTICK_CSR, SYSTICK_SRC_HCLK |
- SYSTICK_ENABLE |
- SYSTICK_TICKINT);
+ SYSTICK_BASE->RVR = reload_val;
+ systick_enable();
}
+/**
+ * Clock the system timer with the core clock, but don't turn it
+ * on or enable interrupt.
+ */
void systick_disable() {
- /* clock the system timer with the core clock, but don't turn it
- on or enable interrupt. */
- __write(SYSTICK_CSR, SYSTICK_SRC_HCLK);
+ SYSTICK_BASE->CSR = SYSTICK_CSR_CLKSOURCE_CORE;
}
-void systick_resume() {
- /* re-enable init registers without changing reload val */
- __write(SYSTICK_CSR, SYSTICK_SRC_HCLK |
- SYSTICK_ENABLE |
- SYSTICK_TICKINT);
+/**
+ * Clock the system timer with the core clock and turn it on;
+ * interrupt every 1 ms, for systick_timer_millis.
+ */
+void systick_enable() {
+ /* re-enables init registers without changing reload val */
+ SYSTICK_BASE->CSR = (SYSTICK_CSR_CLKSOURCE_CORE |
+ SYSTICK_CSR_ENABLE |
+ SYSTICK_CSR_TICKINT_PEND);
}
-/** SysTick interrupt handler. Bumps up the tick counter. */
-void SysTickHandler(void) {
+/*
+ * SysTick ISR
+ */
+
+void __exc_systick(void) {
systick_timer_millis++;
}
diff --git a/libmaple/systick.h b/libmaple/systick.h
index 33a3cf8..35b4cb9 100644
--- a/libmaple/systick.h
+++ b/libmaple/systick.h
@@ -31,30 +31,60 @@
#ifndef _SYSTICK_H_
#define _SYSTICK_H_
-#include "libmaple.h"
+#include "libmaple_types.h"
+#include "util.h"
#ifdef __cplusplus
extern "C"{
#endif
-#define SYSTICK_CSR 0xE000E010 // Control and status register
-#define SYSTICK_CNT 0xE000E018 // Current value register
+/** SysTick register map type */
+typedef struct systick_reg_map {
+ __io uint32 CSR; /**< Control and status register */
+ __io uint32 RVR; /**< Reload value register */
+ __io uint32 CNT; /**< Current value register ("count") */
+ __io uint32 CVR; /**< Calibration value register */
+} systick_reg_map;
-#define SYSTICK_CSR_COUNTFLAG BIT(16)
+/** SysTick register map base pointer */
+#define SYSTICK_BASE ((struct systick_reg_map*)0xE000E010)
-/** System elapsed time in milliseconds */
+/*
+ * Register bit definitions.
+ */
+
+/* Control and status register */
+
+#define SYSTICK_CSR_COUNTFLAG BIT(16)
+#define SYSTICK_CSR_CLKSOURCE BIT(2)
+#define SYSTICK_CSR_CLKSOURCE_EXTERNAL 0
+#define SYSTICK_CSR_CLKSOURCE_CORE BIT(2)
+#define SYSTICK_CSR_TICKINT BIT(1)
+#define SYSTICK_CSR_TICKINT_PEND BIT(1)
+#define SYSTICK_CSR_TICKINT_NO_PEND 0
+#define SYSTICK_CSR_ENABLE BIT(0)
+#define SYSTICK_CSR_ENABLE_MULTISHOT BIT(0)
+#define SYSTICK_CSR_ENABLE_DISABLED 0
+
+/* Calibration value register */
+
+#define SYSTICK_CVR_NOREF BIT(31)
+#define SYSTICK_CVR_SKEW BIT(30)
+#define SYSTICK_CVR_TENMS 0xFFFFFF
+
+/** System elapsed time, in milliseconds */
extern volatile uint32 systick_timer_millis;
void systick_init(uint32 reload_val);
void systick_disable();
-void systick_resume();
+void systick_enable();
static inline uint32 systick_get_count(void) {
- return __read(SYSTICK_CNT);
+ return SYSTICK_BASE->CNT;
}
static inline uint32 systick_check_underflow(void) {
- return (__read(SYSTICK_CSR) & SYSTICK_CSR_COUNTFLAG);
+ return SYSTICK_BASE->CSR & SYSTICK_CSR_COUNTFLAG;
}
#ifdef __cplusplus
diff --git a/libmaple/timer.c b/libmaple/timer.c
new file mode 100644
index 0000000..b22ffb4
--- /dev/null
+++ b/libmaple/timer.c
@@ -0,0 +1,443 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2011 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 timer.c
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief New-style timer interface
+ */
+
+#include "timer.h"
+
+/* Just like the corresponding DIER bits:
+ * [0] = Update handler;
+ * [1,2,3,4] = capture/compare 1,2,3,4 handlers, respectively;
+ * [5] = COM;
+ * [6] = TRG;
+ * [7] = BRK. */
+#define NR_ADV_HANDLERS 8
+/* Update, capture/compare 1,2,3,4; <junk>; trigger. */
+#define NR_GEN_HANDLERS 6
+/* Update only. */
+#define NR_BAS_HANDLERS 1
+
+static timer_dev timer1 = {
+ .regs = { .adv = TIMER1_BASE },
+ .clk_id = RCC_TIMER1,
+ .type = TIMER_ADVANCED,
+ .handlers = { [NR_ADV_HANDLERS - 1] = 0 },
+};
+timer_dev *TIMER1 = &timer1;
+
+static timer_dev timer2 = {
+ .regs = { .gen = TIMER2_BASE },
+ .clk_id = RCC_TIMER2,
+ .type = TIMER_GENERAL,
+ .handlers = { [NR_GEN_HANDLERS - 1] = 0 },
+};
+timer_dev *TIMER2 = &timer2;
+
+static timer_dev timer3 = {
+ .regs = { .gen = TIMER3_BASE },
+ .clk_id = RCC_TIMER3,
+ .type = TIMER_GENERAL,
+ .handlers = { [NR_GEN_HANDLERS - 1] = 0 },
+};
+timer_dev *TIMER3 = &timer3;
+
+static timer_dev timer4 = {
+ .regs = { .gen = TIMER4_BASE },
+ .clk_id = RCC_TIMER4,
+ .type = TIMER_GENERAL,
+ .handlers = { [NR_GEN_HANDLERS - 1] = 0 },
+};
+timer_dev *TIMER4 = &timer4;
+
+#ifdef STM32_HIGH_DENSITY
+static timer_dev timer5 = {
+ .regs = { .gen = TIMER5_BASE },
+ .clk_id = RCC_TIMER5,
+ .type = TIMER_GENERAL,
+ .handlers = { [NR_GEN_HANDLERS - 1] = 0 },
+};
+timer_dev *TIMER5 = &timer5;
+
+static timer_dev timer6 = {
+ .regs = { .bas = TIMER6_BASE },
+ .clk_id = RCC_TIMER6,
+ .type = TIMER_BASIC,
+ .handlers = { [NR_BAS_HANDLERS - 1] = 0 },
+};
+timer_dev *TIMER6 = &timer6;
+
+static timer_dev timer7 = {
+ .regs = { .bas = TIMER7_BASE },
+ .clk_id = RCC_TIMER7,
+ .type = TIMER_BASIC,
+ .handlers = { [NR_BAS_HANDLERS - 1] = 0 },
+};
+timer_dev *TIMER7 = &timer7;
+
+static timer_dev timer8 = {
+ .regs = { .adv = TIMER8_BASE },
+ .clk_id = RCC_TIMER8,
+ .type = TIMER_ADVANCED,
+ .handlers = { [NR_ADV_HANDLERS - 1] = 0 },
+};
+timer_dev *TIMER8 = &timer8;
+#endif
+
+/*
+ * Convenience routines
+ */
+
+static void disable_channel(timer_dev *dev, uint8 channel);
+static void pwm_mode(timer_dev *dev, uint8 channel);
+static void output_compare_mode(timer_dev *dev, uint8 channel);
+
+static inline void enable_irq(timer_dev *dev, uint8 interrupt);
+
+/**
+ * Initialize a timer, and reset its register map.
+ * @param dev Timer to initialize
+ */
+void timer_init(timer_dev *dev) {
+ rcc_clk_enable(dev->clk_id);
+ rcc_reset_dev(dev->clk_id);
+}
+
+/**
+ * @brief Disable a timer.
+ *
+ * The timer will stop counting, all DMA requests and interrupts will
+ * be disabled, and no state changes will be output.
+ *
+ * @param dev Timer to disable.
+ */
+void timer_disable(timer_dev *dev) {
+ (dev->regs).bas->CR1 = 0;
+ (dev->regs).bas->DIER = 0;
+ switch (dev->type) {
+ case TIMER_ADVANCED: /* fall-through */
+ case TIMER_GENERAL:
+ (dev->regs).gen->CCER = 0;
+ break;
+ case TIMER_BASIC:
+ break;
+ }
+}
+
+/**
+ * Sets the mode of an individual timer channel.
+ *
+ * Note that not all timers can be configured in every mode. For
+ * example, basic timers cannot be configured to output compare mode.
+ * Be sure to use a timer which is appropriate for the mode you want.
+ *
+ * @param dev Timer whose channel mode to set
+ * @param channel Relevant channel
+ * @param mode New timer mode for channel
+ */
+void timer_set_mode(timer_dev *dev, uint8 channel, timer_mode mode) {
+ ASSERT_FAULT(channel > 0 && channel <= 4);
+
+ /* TODO decide about the basic timers */
+ ASSERT(dev->type != TIMER_BASIC);
+ if (dev->type == TIMER_BASIC)
+ return;
+
+ switch (mode) {
+ case TIMER_DISABLED:
+ disable_channel(dev, channel);
+ break;
+ case TIMER_PWM:
+ pwm_mode(dev, channel);
+ break;
+ case TIMER_OUTPUT_COMPARE:
+ output_compare_mode(dev, channel);
+ break;
+ }
+}
+
+/**
+ * @brief Call a function on timer devices.
+ * @param fn Function to call on each timer device.
+ */
+void timer_foreach(void (*fn)(timer_dev*)) {
+ fn(TIMER1);
+ fn(TIMER2);
+ fn(TIMER3);
+ fn(TIMER4);
+#ifdef STM32_HIGH_DENSITY
+ fn(TIMER5);
+ fn(TIMER6);
+ fn(TIMER7);
+ fn(TIMER8);
+#endif
+}
+
+/**
+ * @brief Attach a timer interrupt.
+ * @param dev Timer device
+ * @param interrupt Interrupt number to attach to; this may be any
+ * timer_interrupt_id or timer_channel value appropriate
+ * for the timer.
+ * @param handler Handler to attach to the given interrupt.
+ * @see timer_interrupt_id
+ * @see timer_channel
+ */
+void timer_attach_interrupt(timer_dev *dev,
+ uint8 interrupt,
+ voidFuncPtr handler) {
+ dev->handlers[interrupt] = handler;
+ timer_enable_interrupt(dev, interrupt);
+ enable_irq(dev, interrupt);
+}
+
+/**
+ * @brief Detach a timer interrupt.
+ * @param dev Timer device
+ * @param interrupt Interrupt number to detach; this may be any
+ * timer_interrupt_id or timer_channel value appropriate
+ * for the timer.
+ * @see timer_interrupt_id
+ * @see timer_channel
+ */
+void timer_detach_interrupt(timer_dev *dev, uint8 interrupt) {
+ timer_disable_interrupt(dev, interrupt);
+ dev->handlers[interrupt] = NULL;
+}
+
+/*
+ * IRQ handlers
+ */
+
+static inline void dispatch_adv_brk(timer_dev *dev);
+static inline void dispatch_adv_up(timer_dev *dev);
+static inline void dispatch_adv_trg_com(timer_dev *dev);
+static inline void dispatch_adv_cc(timer_dev *dev);
+static inline void dispatch_general(timer_dev *dev);
+static inline void dispatch_basic(timer_dev *dev);
+
+void __irq_tim1_brk(void) {
+ dispatch_adv_brk(TIMER1);
+}
+
+void __irq_tim1_up(void) {
+ dispatch_adv_up(TIMER1);
+}
+
+void __irq_tim1_trg_com(void) {
+ dispatch_adv_trg_com(TIMER1);
+}
+
+void __irq_tim1_cc(void) {
+ dispatch_adv_cc(TIMER1);
+}
+
+void __irq_tim2(void) {
+ dispatch_general(TIMER2);
+}
+
+void __irq_tim3(void) {
+ dispatch_general(TIMER3);
+}
+
+void __irq_tim4(void) {
+ dispatch_general(TIMER4);
+}
+
+#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
+
+void __irq_tim5(void) {
+ dispatch_general(TIMER5);
+}
+
+void __irq_tim6(void) {
+ dispatch_basic(TIMER6);
+}
+
+void __irq_tim7(void) {
+ dispatch_basic(TIMER7);
+}
+
+void __irq_tim8_brk(void) {
+ dispatch_adv_brk(TIMER8);
+}
+
+void __irq_tim8_up(void) {
+ dispatch_adv_up(TIMER8);
+}
+
+void __irq_tim8_trg_com(void) {
+ dispatch_adv_trg_com(TIMER8);
+}
+
+void __irq_tim8_cc(void) {
+ dispatch_adv_cc(TIMER8);
+}
+#endif
+
+static inline void dispatch_irq(timer_dev *dev, uint8 iid, uint8 sr_bit);
+static inline void dispatch_cc_irqs(timer_dev *dev);
+
+static inline void dispatch_adv_brk(timer_dev *dev) {
+ dispatch_irq(dev, TIMER_BREAK_INTERRUPT, TIMER_SR_BIF_BIT);
+}
+
+static inline void dispatch_adv_up(timer_dev *dev) {
+ dispatch_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF_BIT);
+}
+
+static inline void dispatch_adv_trg_com(timer_dev *dev) {
+ dispatch_irq(dev, TIMER_TRG_INTERRUPT, TIMER_SR_TIF_BIT);
+ dispatch_irq(dev, TIMER_COM_INTERRUPT, TIMER_SR_COMIF_BIT);
+}
+
+static inline void dispatch_adv_cc(timer_dev *dev) {
+ dispatch_cc_irqs(dev);
+}
+
+static inline void dispatch_general(timer_dev *dev) {
+ dispatch_irq(dev, TIMER_TRG_INTERRUPT, TIMER_SR_TIF_BIT);
+ dispatch_cc_irqs(dev);
+ dispatch_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF_BIT);
+}
+
+static inline void dispatch_basic(timer_dev *dev) {
+ dispatch_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF_BIT);
+}
+
+static inline void dispatch_irq(timer_dev *dev, uint8 iid, uint8 sr_bit) {
+ __io uint32 *sr = &(dev->regs).bas->SR;
+ if (bb_peri_get_bit(sr, sr_bit)) {
+ if (dev->handlers[iid])
+ (dev->handlers[iid])();
+ bb_peri_set_bit(sr, sr_bit, 0);
+ }
+}
+
+static inline void dispatch_cc_irqs(timer_dev *dev) {
+ uint32 sr = (dev->regs).gen->SR;
+ uint32 sr_clear = 0;
+ uint32 b;
+
+ ASSERT_FAULT(sr & (TIMER_SR_CC1IF | TIMER_SR_CC2IF |
+ TIMER_SR_CC3IF | TIMER_SR_CC4IF));
+
+ for (b = TIMER_SR_CC1IF_BIT; b <= TIMER_SR_CC4IF_BIT; b++) {
+ uint32 mask = BIT(b);
+ if (sr & mask) {
+ if (dev->handlers[b])
+ (dev->handlers[b])();
+ sr_clear |= mask;
+ }
+ }
+
+ (dev->regs).gen->SR &= ~sr_clear;
+}
+
+/*
+ * Utilities
+ */
+
+static void disable_channel(timer_dev *dev, uint8 channel) {
+ timer_detach_interrupt(dev, channel);
+ timer_cc_disable(dev, channel);
+}
+
+static void pwm_mode(timer_dev *dev, uint8 channel) {
+ timer_disable_interrupt(dev, channel);
+ timer_oc_set_mode(dev, channel, TIMER_OC_MODE_PWM_1, TIMER_OC_PE);
+ timer_cc_enable(dev, channel);
+}
+
+static void output_compare_mode(timer_dev *dev, uint8 channel) {
+ timer_oc_set_mode(dev, channel, TIMER_OC_MODE_ACTIVE_ON_MATCH, 0);
+ timer_cc_enable(dev, channel);
+}
+
+static void enable_advanced_irq(timer_dev *dev, timer_interrupt_id id);
+static void enable_nonmuxed_irq(timer_dev *dev);
+
+static inline void enable_irq(timer_dev *dev, timer_interrupt_id iid) {
+ if (dev->type == TIMER_ADVANCED) {
+ enable_advanced_irq(dev, iid);
+ } else {
+ enable_nonmuxed_irq(dev);
+ }
+}
+
+static void enable_advanced_irq(timer_dev *dev, timer_interrupt_id id) {
+ uint8 is_timer1 = dev->clk_id == RCC_TIMER1;
+
+ switch (id) {
+ case TIMER_UPDATE_INTERRUPT:
+ nvic_irq_enable(is_timer1 ? NVIC_TIMER1_UP : NVIC_TIMER8_UP);
+ break;
+ case TIMER_CC1_INTERRUPT:
+ case TIMER_CC2_INTERRUPT:
+ case TIMER_CC3_INTERRUPT:
+ case TIMER_CC4_INTERRUPT:
+ nvic_irq_enable(is_timer1 ? NVIC_TIMER1_CC : NVIC_TIMER8_CC);
+ break;
+ case TIMER_COM_INTERRUPT:
+ case TIMER_TRG_INTERRUPT:
+ nvic_irq_enable(is_timer1 ? NVIC_TIMER1_TRG_COM : NVIC_TIMER8_TRG_COM);
+ break;
+ case TIMER_BREAK_INTERRUPT:
+ nvic_irq_enable(is_timer1 ? NVIC_TIMER1_BRK : NVIC_TIMER8_BRK);
+ break;
+ }
+}
+
+static void enable_nonmuxed_irq(timer_dev *dev) {
+ switch (dev->clk_id) {
+ case RCC_TIMER2:
+ nvic_irq_enable(NVIC_TIMER2);
+ break;
+ case RCC_TIMER3:
+ nvic_irq_enable(NVIC_TIMER3);
+ break;
+ case RCC_TIMER4:
+ nvic_irq_enable(NVIC_TIMER4);
+ break;
+#ifdef STM32_HIGH_DENSITY
+ case RCC_TIMER5:
+ nvic_irq_enable(NVIC_TIMER5);
+ break;
+ case RCC_TIMER6:
+ nvic_irq_enable(NVIC_TIMER6);
+ break;
+ case RCC_TIMER7:
+ nvic_irq_enable(NVIC_TIMER7);
+ break;
+#endif
+ default:
+ ASSERT_FAULT(0);
+ break;
+ }
+}
diff --git a/libmaple/timer.h b/libmaple/timer.h
new file mode 100644
index 0000000..befc026
--- /dev/null
+++ b/libmaple/timer.h
@@ -0,0 +1,1011 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2011 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 timer.h
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief New-style timer interface.
+ *
+ * Replaces old timers.h implementation.
+ */
+
+#ifndef _TIMERS_H_
+#define _TIMERS_H_
+
+#include "libmaple.h"
+#include "rcc.h"
+#include "nvic.h"
+#include "bitband.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/*
+ * Register maps and devices
+ */
+
+/** Advanced control timer register map type */
+typedef struct timer_adv_reg_map {
+ __io uint32 CR1; /**< Control register 1 */
+ __io uint32 CR2; /**< Control register 2 */
+ __io uint32 SMCR; /**< Slave mode control register */
+ __io uint32 DIER; /**< DMA/Interrupt enable register */
+ __io uint32 SR; /**< Status register */
+ __io uint32 EGR; /**< Event generation register */
+ __io uint32 CCMR1; /**< Capture/compare mode register 1 */
+ __io uint32 CCMR2; /**< Capture/compare mode register 2 */
+ __io uint32 CCER; /**< Capture/compare enable register */
+ __io uint32 CNT; /**< Counter */
+ __io uint32 PSC; /**< Prescaler */
+ __io uint32 ARR; /**< Auto-reload register */
+ __io uint32 RCR; /**< Repetition counter register */
+ __io uint32 CCR1; /**< Capture/compare register 1 */
+ __io uint32 CCR2; /**< Capture/compare register 2 */
+ __io uint32 CCR3; /**< Capture/compare register 3 */
+ __io uint32 CCR4; /**< Capture/compare register 4 */
+ __io uint32 BDTR; /**< Break and dead-time register */
+ __io uint32 DCR; /**< DMA control register */
+ __io uint32 DMAR; /**< DMA address for full transfer */
+} timer_adv_reg_map;
+
+/** General purpose timer register map type */
+typedef struct timer_gen_reg_map {
+ __io uint32 CR1; /**< Control register 1 */
+ __io uint32 CR2; /**< Control register 2 */
+ __io uint32 SMCR; /**< Slave mode control register */
+ __io uint32 DIER; /**< DMA/Interrupt enable register */
+ __io uint32 SR; /**< Status register */
+ __io uint32 EGR; /**< Event generation register */
+ __io uint32 CCMR1; /**< Capture/compare mode register 1 */
+ __io uint32 CCMR2; /**< Capture/compare mode register 2 */
+ __io uint32 CCER; /**< Capture/compare enable register */
+ __io uint32 CNT; /**< Counter */
+ __io uint32 PSC; /**< Prescaler */
+ __io uint32 ARR; /**< Auto-reload register */
+ const uint32 RESERVED1; /**< Reserved */
+ __io uint32 CCR1; /**< Capture/compare register 1 */
+ __io uint32 CCR2; /**< Capture/compare register 2 */
+ __io uint32 CCR3; /**< Capture/compare register 3 */
+ __io uint32 CCR4; /**< Capture/compare register 4 */
+ const uint32 RESERVED2; /**< Reserved */
+ __io uint32 DCR; /**< DMA control register */
+ __io uint32 DMAR; /**< DMA address for full transfer */
+} timer_gen_reg_map;
+
+/** Basic timer register map type */
+typedef struct timer_bas_reg_map {
+ __io uint32 CR1; /**< Control register 1 */
+ __io uint32 CR2; /**< Control register 2 */
+ const uint32 RESERVED1; /**< Reserved */
+ __io uint32 DIER; /**< DMA/Interrupt enable register */
+ __io uint32 SR; /**< Status register */
+ __io uint32 EGR; /**< Event generation register */
+ const uint32 RESERVED2; /**< Reserved */
+ const uint32 RESERVED3; /**< Reserved */
+ const uint32 RESERVED4; /**< Reserved */
+ __io uint32 CNT; /**< Counter */
+ __io uint32 PSC; /**< Prescaler */
+ __io uint32 ARR; /**< Auto-reload register */
+} timer_bas_reg_map;
+
+/** Timer 1 register map base pointer */
+#define TIMER1_BASE ((struct timer_adv_reg_map*)0x40012C00)
+/** Timer 2 register map base pointer */
+#define TIMER2_BASE ((struct timer_gen_reg_map*)0x40000000)
+/** Timer 3 register map base pointer */
+#define TIMER3_BASE ((struct timer_gen_reg_map*)0x40000400)
+/** Timer 4 register map base pointer */
+#define TIMER4_BASE ((struct timer_gen_reg_map*)0x40000800)
+#ifdef STM32_HIGH_DENSITY
+/** Timer 5 register map base pointer */
+#define TIMER5_BASE ((struct timer_gen_reg_map*)0x40000C00)
+/** Timer 6 register map base pointer */
+#define TIMER6_BASE ((struct timer_bas_reg_map*)0x40001000)
+/** Timer 7 register map base pointer */
+#define TIMER7_BASE ((struct timer_bas_reg_map*)0x40001400)
+/** Timer 8 register map base pointer */
+#define TIMER8_BASE ((struct timer_adv_reg_map*)0x40013400)
+#endif
+
+/*
+ * Timer devices
+ */
+
+typedef union {
+ timer_adv_reg_map *adv;
+ timer_gen_reg_map *gen;
+ timer_bas_reg_map *bas;
+} timer_reg_map_union;
+
+typedef enum {
+ TIMER_ADVANCED,
+ TIMER_GENERAL,
+ TIMER_BASIC
+} timer_type;
+
+/** Timer device type */
+typedef struct timer_dev {
+ timer_reg_map_union regs;
+ rcc_clk_id clk_id;
+ timer_type type;
+ voidFuncPtr handlers[];
+} timer_dev;
+
+/** Timer 1 device (advanced) */
+extern timer_dev *TIMER1;
+/** Timer 2 device (general-purpose) */
+extern timer_dev *TIMER2;
+/** Timer 3 device (general-purpose) */
+extern timer_dev *TIMER3;
+/** Timer 4 device (general-purpose) */
+extern timer_dev *TIMER4;
+#ifdef STM32_HIGH_DENSITY
+/** Timer 5 device (general-purpose) */
+extern timer_dev *TIMER5;
+/** Timer 6 device (basic) */
+extern timer_dev *TIMER6;
+/** Timer 7 device (basic) */
+extern timer_dev *TIMER7;
+/** Timer 8 device (advanced) */
+extern timer_dev *TIMER8;
+#endif
+
+/*
+ * Register bit definitions
+ */
+
+/* Control register 1 (CR1) */
+
+#define TIMER_CR1_ARPE_BIT 7
+#define TIMER_CR1_DIR_BIT 4
+#define TIMER_CR1_OPM_BIT 3
+#define TIMER_CR1_URS_BIT 2
+#define TIMER_CR1_UDIS_BIT 1
+#define TIMER_CR1_CEN_BIT 0
+
+#define TIMER_CR1_CKD (0x3 << 8)
+#define TIMER_CR1_CKD_1TCKINT (0x0 << 8)
+#define TIMER_CR1_CKD_2TCKINT (0x1 << 8)
+#define TIMER_CR1_CKD_4TICKINT (0x2 << 8)
+#define TIMER_CR1_ARPE BIT(TIMER_CR1_ARPE_BIT)
+#define TIMER_CR1_CKD_CMS (0x3 << 5)
+#define TIMER_CR1_CKD_CMS_EDGE (0x0 << 5)
+#define TIMER_CR1_CKD_CMS_CENTER1 (0x1 << 5)
+#define TIMER_CR1_CKD_CMS_CENTER2 (0x2 << 5)
+#define TIMER_CR1_CKD_CMS_CENTER3 (0x3 << 5)
+#define TIMER_CR1_DIR BIT(TIMER_CR1_DIR_BIT)
+#define TIMER_CR1_OPM BIT(TIMER_CR1_OPM_BIT)
+#define TIMER_CR1_URS BIT(TIMER_CR1_URS_BIT)
+#define TIMER_CR1_UDIS BIT(TIMER_CR1_UDIS_BIT)
+#define TIMER_CR1_CEN BIT(TIMER_CR1_CEN_BIT)
+
+/* Control register 2 (CR2) */
+
+#define TIMER_CR2_OIS4_BIT 14
+#define TIMER_CR2_OIS3N_BIT 13
+#define TIMER_CR2_OIS3_BIT 12
+#define TIMER_CR2_OIS2N_BIT 11
+#define TIMER_CR2_OIS2_BIT 10
+#define TIMER_CR2_OIS1N_BIT 9
+#define TIMER_CR2_OIS1_BIT 8
+#define TIMER_CR2_TI1S_BIT 7 /* tills? yikes */
+#define TIMER_CR2_CCDS_BIT 3
+#define TIMER_CR2_CCUS_BIT 2
+#define TIMER_CR2_CCPC_BIT 0
+
+#define TIMER_CR2_OIS4 BIT(TIMER_CR2_OIS4_BIT)
+#define TIMER_CR2_OIS3N BIT(TIMER_CR2_OIS3N_BIT)
+#define TIMER_CR2_OIS3 BIT(TIMER_CR2_OIS3_BIT)
+#define TIMER_CR2_OIS2N BIT(TIMER_CR2_OIS2N_BIT)
+#define TIMER_CR2_OIS2 BIT(TIMER_CR2_OIS2_BIT)
+#define TIMER_CR2_OIS1N BIT(TIMER_CR2_OIS1N_BIT)
+#define TIMER_CR2_OIS1 BIT(TIMER_CR2_OIS1_BIT)
+#define TIMER_CR2_TI1S BIT(TIMER_CR2_TI1S_BIT)
+#define TIMER_CR2_MMS (0x7 << 4)
+#define TIMER_CR2_MMS_RESET (0x0 << 4)
+#define TIMER_CR2_MMS_ENABLE (0x1 << 4)
+#define TIMER_CR2_MMS_UPDATE (0x2 << 4)
+#define TIMER_CR2_MMS_COMPARE_PULSE (0x3 << 4)
+#define TIMER_CR2_MMS_COMPARE_OC1REF (0x4 << 4)
+#define TIMER_CR2_MMS_COMPARE_OC2REF (0x5 << 4)
+#define TIMER_CR2_MMS_COMPARE_OC3REF (0x6 << 4)
+#define TIMER_CR2_MMS_COMPARE_OC4REF (0x7 << 4)
+#define TIMER_CR2_CCDS BIT(TIMER_CR2_CCDS_BIT)
+#define TIMER_CR2_CCUS BIT(TIMER_CR2_CCUS_BIT)
+#define TIMER_CR2_CCPC BIT(TIMER_CR2_CCPC_BIT)
+
+/* Slave mode control register (SMCR) */
+
+#define TIMER_SMCR_ETP_BIT 15
+#define TIMER_SMCR_ECE_BIT 14
+#define TIMER_SMCR_MSM_BIT 7
+
+#define TIMER_SMCR_ETP BIT(TIMER_SMCR_ETP_BIT)
+#define TIMER_SMCR_ECE BIT(TIMER_SMCR_ECE_BIT)
+#define TIMER_SMCR_ETPS (0x3 << 12)
+#define TIMER_SMCR_ETPS_OFF (0x0 << 12)
+#define TIMER_SMCR_ETPS_DIV2 (0x1 << 12)
+#define TIMER_SMCR_ETPS_DIV4 (0x2 << 12)
+#define TIMER_SMCR_ETPS_DIV8 (0x3 << 12)
+#define TIMER_SMCR_ETF (0xF << 12)
+#define TIMER_SMCR_MSM BIT(TIMER_SMCR_MSM_BIT)
+#define TIMER_SMCR_TS (0x3 << 4)
+#define TIMER_SMCR_TS_ITR0 (0x0 << 4)
+#define TIMER_SMCR_TS_ITR1 (0x1 << 4)
+#define TIMER_SMCR_TS_ITR2 (0x2 << 4)
+#define TIMER_SMCR_TS_ITR3 (0x3 << 4)
+#define TIMER_SMCR_TS_TI1F_ED (0x4 << 4)
+#define TIMER_SMCR_TS_TI1FP1 (0x5 << 4)
+#define TIMER_SMCR_TS_TI2FP2 (0x6 << 4)
+#define TIMER_SMCR_TS_ETRF (0x7 << 4)
+#define TIMER_SMCR_SMS 0x3
+#define TIMER_SMCR_SMS_DISABLED 0x0
+#define TIMER_SMCR_SMS_ENCODER1 0x1
+#define TIMER_SMCR_SMS_ENCODER2 0x2
+#define TIMER_SMCR_SMS_ENCODER3 0x3
+#define TIMER_SMCR_SMS_RESET 0x4
+#define TIMER_SMCR_SMS_GATED 0x5
+#define TIMER_SMCR_SMS_TRIGGER 0x6
+#define TIMER_SMCR_SMS_EXTERNAL 0x7
+
+/* DMA/Interrupt enable register (DIER) */
+
+#define TIMER_DIER_TDE_BIT 14
+#define TIMER_DIER_CC4DE_BIT 12
+#define TIMER_DIER_CC3DE_BIT 11
+#define TIMER_DIER_CC2DE_BIT 10
+#define TIMER_DIER_CC1DE_BIT 9
+#define TIMER_DIER_UDE_BIT 8
+#define TIMER_DIER_TIE_BIT 6
+#define TIMER_DIER_CC4IE_BIT 4
+#define TIMER_DIER_CC3IE_BIT 3
+#define TIMER_DIER_CC2IE_BIT 2
+#define TIMER_DIER_CC1IE_BIT 1
+#define TIMER_DIER_UIE_BIT 0
+
+#define TIMER_DIER_TDE BIT(TIMER_DIER_TDE_BIT)
+#define TIMER_DIER_CC4DE BIT(TIMER_DIER_CC4DE_BIT)
+#define TIMER_DIER_CC3DE BIT(TIMER_DIER_CC3DE_BIT)
+#define TIMER_DIER_CC2DE BIT(TIMER_DIER_CC2DE_BIT)
+#define TIMER_DIER_CC1DE BIT(TIMER_DIER_CC1DE_BIT)
+#define TIMER_DIER_UDE BIT(TIMER_DIER_UDE_BIT)
+#define TIMER_DIER_TIE BIT(TIMER_DIER_TIE_BIT)
+#define TIMER_DIER_CC4IE BIT(TIMER_DIER_CC4IE_BIT)
+#define TIMER_DIER_CC3IE BIT(TIMER_DIER_CC3IE_BIT)
+#define TIMER_DIER_CC2IE BIT(TIMER_DIER_CC2IE_BIT)
+#define TIMER_DIER_CC1IE BIT(TIMER_DIER_CC1IE_BIT)
+#define TIMER_DIER_UIE BIT(TIMER_DIER_UIE_BIT)
+
+/* Status register (SR) */
+
+#define TIMER_SR_CC4OF_BIT 12
+#define TIMER_SR_CC3OF_BIT 11
+#define TIMER_SR_CC2OF_BIT 10
+#define TIMER_SR_CC1OF_BIT 9
+#define TIMER_SR_BIF_BIT 7
+#define TIMER_SR_TIF_BIT 6
+#define TIMER_SR_COMIF_BIT 5
+#define TIMER_SR_CC4IF_BIT 4
+#define TIMER_SR_CC3IF_BIT 3
+#define TIMER_SR_CC2IF_BIT 2
+#define TIMER_SR_CC1IF_BIT 1
+#define TIMER_SR_UIF_BIT 0
+
+#define TIMER_SR_CC4OF BIT(TIMER_SR_CC4OF_BIT)
+#define TIMER_SR_CC3OF BIT(TIMER_SR_CC3OF_BIT)
+#define TIMER_SR_CC2OF BIT(TIMER_SR_CC2OF_BIT)
+#define TIMER_SR_CC1OF BIT(TIMER_SR_CC1OF_BIT)
+#define TIMER_SR_BIF BIT(TIMER_SR_BIF_BIT)
+#define TIMER_SR_TIF BIT(TIMER_SR_TIF_BIT)
+#define TIMER_SR_COMIF BIT(TIMER_SR_COMIF_BIT)
+#define TIMER_SR_CC4IF BIT(TIMER_SR_CC4IF_BIT)
+#define TIMER_SR_CC3IF BIT(TIMER_SR_CC3IF_BIT)
+#define TIMER_SR_CC2IF BIT(TIMER_SR_CC2IF_BIT)
+#define TIMER_SR_CC1IF BIT(TIMER_SR_CC1IF_BIT)
+#define TIMER_SR_UIF BIT(TIMER_SR_UIF_BIT)
+
+/* Event generation register (EGR) */
+
+#define TIMER_EGR_TG_BIT 6
+#define TIMER_EGR_CC4G_BIT 4
+#define TIMER_EGR_CC3G_BIT 3
+#define TIMER_EGR_CC2G_BIT 2
+#define TIMER_EGR_CC1G_BIT 1
+#define TIMER_EGR_UG_BIT 0
+
+#define TIMER_EGR_TG BIT(TIMER_EGR_TG_BIT)
+#define TIMER_EGR_CC4G BIT(TIMER_EGR_CC4G_BIT)
+#define TIMER_EGR_CC3G BIT(TIMER_EGR_CC3G_BIT)
+#define TIMER_EGR_CC2G BIT(TIMER_EGR_CC2G_BIT)
+#define TIMER_EGR_CC1G BIT(TIMER_EGR_CC1G_BIT)
+#define TIMER_EGR_UG BIT(TIMER_EGR_UG_BIT)
+
+/* Capture/compare mode registers, common values */
+
+#define TIMER_CCMR_CCS_OUTPUT 0x0
+#define TIMER_CCMR_CCS_INPUT_TI1 0x1
+#define TIMER_CCMR_CCS_INPUT_TI2 0x2
+#define TIMER_CCMR_CCS_INPUT_TRC 0x3
+
+/* Capture/compare mode register 1 (CCMR1) */
+
+#define TIMER_CCMR1_OC2CE_BIT 15
+#define TIMER_CCMR1_OC2PE_BIT 11
+#define TIMER_CCMR1_OC2FE_BIT 10
+#define TIMER_CCMR1_OC1CE_BIT 7
+#define TIMER_CCMR1_OC1PE_BIT 3
+#define TIMER_CCMR1_OC1FE_BIT 2
+
+#define TIMER_CCMR1_OC2CE BIT(TIMER_CCMR1_OC2CE_BIT)
+#define TIMER_CCMR1_OC2M (0x3 << 12)
+#define TIMER_CCMR1_IC2F (0xF << 12)
+#define TIMER_CCMR1_OC2PE BIT(TIMER_CCMR1_OC2PE_BIT)
+#define TIMER_CCMR1_OC2FE BIT(TIMER_CCMR1_OC2FE_BIT)
+#define TIMER_CCMR1_IC2PSC (0x3 << 10)
+#define TIMER_CCMR1_CC2S (0x3 << 8)
+#define TIMER_CCMR1_CC2S_OUTPUT (TIMER_CCMR_CCS_OUTPUT << 8)
+#define TIMER_CCMR1_CC2S_INPUT_TI1 (TIMER_CCMR_CCS_INPUT_TI1 << 8)
+#define TIMER_CCMR1_CC2S_INPUT_TI2 (TIMER_CCMR_CCS_INPUT_TI2 << 8)
+#define TIMER_CCMR1_CC2S_INPUT_TRC (TIMER_CCMR_CCS_INPUT_TRC << 8)
+#define TIMER_CCMR1_OC1CE BIT(TIMER_CCMR1_OC1CE_BIT)
+#define TIMER_CCMR1_OC1M (0x3 << 4)
+#define TIMER_CCMR1_IC1F (0xF << 4)
+#define TIMER_CCMR1_OC1PE BIT(TIMER_CCMR1_OC1PE_BIT)
+#define TIMER_CCMR1_OC1FE BIT(TIMER_CCMR1_OC1FE_BIT)
+#define TIMER_CCMR1_IC1PSC (0x3 << 2)
+#define TIMER_CCMR1_CC1S 0x3
+#define TIMER_CCMR1_CC1S_OUTPUT TIMER_CCMR_CCS_OUTPUT
+#define TIMER_CCMR1_CC1S_INPUT_TI1 TIMER_CCMR_CCS_INPUT_TI1
+#define TIMER_CCMR1_CC1S_INPUT_TI2 TIMER_CCMR_CCS_INPUT_TI2
+#define TIMER_CCMR1_CC1S_INPUT_TRC TIMER_CCMR_CCS_INPUT_TRC
+
+/* Capture/compare mode register 2 (CCMR2) */
+
+#define TIMER_CCMR2_OC4CE_BIT 15
+#define TIMER_CCMR2_OC4PE_BIT 11
+#define TIMER_CCMR2_OC4FE_BIT 10
+#define TIMER_CCMR2_OC3CE_BIT 7
+#define TIMER_CCMR2_OC3PE_BIT 3
+#define TIMER_CCMR2_OC3FE_BIT 2
+
+#define TIMER_CCMR2_OC4CE BIT(TIMER_CCMR2_OC4CE_BIT)
+#define TIMER_CCMR2_OC4M (0x3 << 12)
+#define TIMER_CCMR2_IC2F (0xF << 12)
+#define TIMER_CCMR2_OC4PE BIT(TIMER_CCMR2_OC4PE_BIT)
+#define TIMER_CCMR2_OC4FE BIT(TIMER_CCMR2_OC4FE_BIT)
+#define TIMER_CCMR2_IC2PSC (0x3 << 10)
+#define TIMER_CCMR2_CC4S (0x3 << 8)
+#define TIMER_CCMR1_CC4S_OUTPUT (TIMER_CCMR_CCS_OUTPUT << 8)
+#define TIMER_CCMR1_CC4S_INPUT_TI1 (TIMER_CCMR_CCS_INPUT_TI1 << 8)
+#define TIMER_CCMR1_CC4S_INPUT_TI2 (TIMER_CCMR_CCS_INPUT_TI2 << 8)
+#define TIMER_CCMR1_CC4S_INPUT_TRC (TIMER_CCMR_CCS_INPUT_TRC << 8)
+#define TIMER_CCMR2_OC3CE BIT(TIMER_CCMR2_OC3CE_BIT)
+#define TIMER_CCMR2_OC3M (0x3 << 4)
+#define TIMER_CCMR2_IC1F (0xF << 4)
+#define TIMER_CCMR2_OC3PE BIT(TIMER_CCMR2_OC3PE_BIT)
+#define TIMER_CCMR2_OC3FE BIT(TIMER_CCMR2_OC3FE_BIT)
+#define TIMER_CCMR2_IC1PSC (0x3 << 2)
+#define TIMER_CCMR2_CC3S 0x3
+#define TIMER_CCMR1_CC3S_OUTPUT TIMER_CCMR_CCS_OUTPUT
+#define TIMER_CCMR1_CC3S_INPUT_TI1 TIMER_CCMR_CCS_INPUT_TI1
+#define TIMER_CCMR1_CC3S_INPUT_TI2 TIMER_CCMR_CCS_INPUT_TI2
+#define TIMER_CCMR1_CC3S_INPUT_TRC TIMER_CCMR_CCS_INPUT_TRC
+
+/* Capture/compare enable register (CCER) */
+
+#define TIMER_CCER_CC4P_BIT 13
+#define TIMER_CCER_CC4E_BIT 12
+#define TIMER_CCER_CC3P_BIT 9
+#define TIMER_CCER_CC3E_BIT 8
+#define TIMER_CCER_CC2P_BIT 5
+#define TIMER_CCER_CC2E_BIT 4
+#define TIMER_CCER_CC1P_BIT 1
+#define TIMER_CCER_CC1E_BIT 0
+
+#define TIMER_CCER_CC4P BIT(TIMER_CCER_CC4P_BIT)
+#define TIMER_CCER_CC4E BIT(TIMER_CCER_CC4E_BIT)
+#define TIMER_CCER_CC3P BIT(TIMER_CCER_CC3P_BIT)
+#define TIMER_CCER_CC3E BIT(TIMER_CCER_CC3E_BIT)
+#define TIMER_CCER_CC2P BIT(TIMER_CCER_CC2P_BIT)
+#define TIMER_CCER_CC2E BIT(TIMER_CCER_CC2E_BIT)
+#define TIMER_CCER_CC1P BIT(TIMER_CCER_CC1P_BIT)
+#define TIMER_CCER_CC1E BIT(TIMER_CCER_CC1E_BIT)
+
+/* Break and dead-time register (BDTR) */
+
+#define TIMER_BDTR_MOE_BIT 15
+#define TIMER_BDTR_AOE_BIT 14
+#define TIMER_BDTR_BKP_BIT 13
+#define TIMER_BDTR_BKE_BIT 12
+#define TIMER_BDTR_OSSR_BIT 11
+#define TIMER_BDTR_OSSI_BIT 10
+
+#define TIMER_BDTR_MOE BIT(TIMER_BDTR_MOE_BIT)
+#define TIMER_BDTR_AOE BIT(TIMER_BDTR_AOE_BIT)
+#define TIMER_BDTR_BKP BIT(TIMER_BDTR_BKP_BIT)
+#define TIMER_BDTR_BKE BIT(TIMER_BDTR_BKE_BIT)
+#define TIMER_BDTR_OSSR BIT(TIMER_BDTR_OSSR_BIT)
+#define TIMER_BDTR_OSSI BIT(TIMER_BDTR_OSSI_BIT)
+#define TIMER_BDTR_LOCK (0x3 << 8)
+#define TIMER_BDTR_LOCK_OFF (0x0 << 8)
+#define TIMER_BDTR_LOCK_LEVEL1 (0x1 << 8)
+#define TIMER_BDTR_LOCK_LEVEL2 (0x2 << 8)
+#define TIMER_BDTR_LOCK_LEVEL3 (0x3 << 8)
+#define TIMER_BDTR_DTG 0xFF
+
+/* DMA control register (DCR) */
+
+#define TIMER_DCR_DBL (0x1F << 8)
+#define TIMER_DCR_DBL_1BYTE (0x0 << 8)
+#define TIMER_DCR_DBL_2BYTE (0x1 << 8)
+#define TIMER_DCR_DBL_3BYTE (0x2 << 8)
+#define TIMER_DCR_DBL_4BYTE (0x3 << 8)
+#define TIMER_DCR_DBL_5BYTE (0x4 << 8)
+#define TIMER_DCR_DBL_6BYTE (0x5 << 8)
+#define TIMER_DCR_DBL_7BYTE (0x6 << 8)
+#define TIMER_DCR_DBL_8BYTE (0x7 << 8)
+#define TIMER_DCR_DBL_9BYTE (0x8 << 8)
+#define TIMER_DCR_DBL_10BYTE (0x9 << 8)
+#define TIMER_DCR_DBL_11BYTE (0xA << 8)
+#define TIMER_DCR_DBL_12BYTE (0xB << 8)
+#define TIMER_DCR_DBL_13BYTE (0xC << 8)
+#define TIMER_DCR_DBL_14BYTE (0xD << 8)
+#define TIMER_DCR_DBL_15BYTE (0xE << 8)
+#define TIMER_DCR_DBL_16BYTE (0xF << 8)
+#define TIMER_DCR_DBL_17BYTE (0x10 << 8)
+#define TIMER_DCR_DBL_18BYTE (0x11 << 8)
+#define TIMER_DCR_DBA 0x1F
+#define TIMER_DCR_DBA_CR1 0x0
+#define TIMER_DCR_DBA_CR2 0x1
+#define TIMER_DCR_DBA_SMCR 0x2
+#define TIMER_DCR_DBA_DIER 0x3
+#define TIMER_DCR_DBA_SR 0x4
+#define TIMER_DCR_DBA_EGR 0x5
+#define TIMER_DCR_DBA_CCMR1 0x6
+#define TIMER_DCR_DBA_CCMR2 0x7
+#define TIMER_DCR_DBA_CCER 0x8
+#define TIMER_DCR_DBA_CNT 0x9
+#define TIMER_DCR_DBA_PSC 0xA
+#define TIMER_DCR_DBA_ARR 0xB
+#define TIMER_DCR_DBA_RCR 0xC
+#define TIMER_DCR_DBA_CCR1 0xD
+#define TIMER_DCR_DBA_CCR2 0xE
+#define TIMER_DCR_DBA_CCR3 0xF
+#define TIMER_DCR_DBA_CCR4 0x10
+#define TIMER_DCR_DBA_BDTR 0x11
+#define TIMER_DCR_DBA_DCR 0x12
+#define TIMER_DCR_DBA_DMAR 0x13
+
+/*
+ * Convenience routines
+ */
+
+/**
+ * Used to configure the behavior of a timer channel. Note that not
+ * all timers can be configured in every mode.
+ */
+/* TODO TIMER_PWM_CENTER_ALIGNED, TIMER_INPUT_CAPTURE, TIMER_ONE_PULSE */
+typedef enum timer_mode {
+ TIMER_DISABLED, /**< In this mode, the timer stops counting,
+ channel interrupts are detached, and no state
+ changes are output. */
+ TIMER_PWM, /**< PWM output mode. This is the default mode for pins
+ after initialization. */
+ /* TIMER_PWM_CENTER_ALIGNED, /\**< Center-aligned PWM output mode. *\/ */
+ TIMER_OUTPUT_COMPARE, /**< In this mode, the timer counts from 0
+ to its reload value repeatedly; every
+ time the counter value reaches one of
+ the channel compare values, the
+ corresponding interrupt is fired. */
+ /* TIMER_INPUT_CAPTURE, /\**< In this mode, the timer can measure the */
+ /* pulse lengths of input signals. *\/ */
+ /* TIMER_ONE_PULSE /\**< In this mode, the timer can generate a single */
+ /* pulse on a GPIO pin for a specified amount of */
+ /* time. *\/ */
+} timer_mode;
+
+/** Timer channel numbers */
+typedef enum timer_channel {
+ TIMER_CH1 = 1, /**< Channel 1 */
+ TIMER_CH2 = 2, /**< Channel 2 */
+ TIMER_CH3 = 3, /**< Channel 3 */
+ TIMER_CH4 = 4 /**< Channel 4 */
+} timer_channel;
+
+/*
+ * Note: Don't require timer_channel arguments! We want to be able to say
+ *
+ * for (int channel = 1; channel <= 4; channel++) {
+ * ...
+ * }
+ *
+ * without the compiler yelling at us.
+ */
+
+void timer_init(timer_dev *dev);
+void timer_disable(timer_dev *dev);
+void timer_set_mode(timer_dev *dev, uint8 channel, timer_mode mode);
+void timer_foreach(void (*fn)(timer_dev*));
+
+/**
+ * @brief Timer interrupt number.
+ *
+ * Not all timers support all of these values; see the descriptions
+ * for each value.
+ */
+typedef enum timer_interrupt_id {
+ TIMER_UPDATE_INTERRUPT, /**< Update interrupt, available on all timers. */
+ TIMER_CC1_INTERRUPT, /**< Capture/compare 1 interrupt, available
+ on general and advanced timers only. */
+ TIMER_CC2_INTERRUPT, /**< Capture/compare 2 interrupt, general and
+ advanced timers only. */
+ TIMER_CC3_INTERRUPT, /**< Capture/compare 3 interrupt, general and
+ advanced timers only. */
+ TIMER_CC4_INTERRUPT, /**< Capture/compare 4 interrupt, general and
+ advanced timers only. */
+ TIMER_COM_INTERRUPT, /**< COM interrupt, advanced timers only */
+ TIMER_TRG_INTERRUPT, /**< Trigger interrupt, general and advanced
+ timers only */
+ TIMER_BREAK_INTERRUPT /**< Break interrupt, advanced timers only. */
+} timer_interrupt_id;
+
+void timer_attach_interrupt(timer_dev *dev,
+ uint8 interrupt,
+ voidFuncPtr handler);
+void timer_detach_interrupt(timer_dev *dev, uint8 interrupt);
+
+/**
+ * Initialize all timer devices on the chip.
+ */
+static inline void timer_init_all(void) {
+ timer_foreach(timer_init);
+}
+
+/**
+ * Disables all timers on the device.
+ */
+static inline void timer_disable_all(void) {
+ timer_foreach(timer_disable);
+}
+
+/**
+ * @brief Stop a timer's counter from changing.
+ *
+ * Does not affect the timer's mode or other settings.
+ *
+ * @param dev Device whose counter to pause.
+ */
+static inline void timer_pause(timer_dev *dev) {
+ *bb_perip(&(dev->regs).bas->CR1, TIMER_CR1_CEN_BIT) = 0;
+}
+
+/**
+ * @brief Start a timer's counter.
+ *
+ * Does not affect the timer's mode or other settings.
+ *
+ * @param dev Device whose counter to resume
+ */
+static inline void timer_resume(timer_dev *dev) {
+ *bb_perip(&(dev->regs).bas->CR1, TIMER_CR1_CEN_BIT) = 1;
+}
+
+/**
+ * @brief Returns the timer's counter value.
+ *
+ * This value is likely to be inaccurate if the counter is running
+ * with a low prescaler.
+ *
+ * @param dev Timer whose counter to return
+ */
+static inline uint16 timer_get_count(timer_dev *dev) {
+ return (uint16)(dev->regs).bas->CNT;
+}
+
+/**
+ * @brief Sets the counter value for the given timer.
+ * @param dev Timer whose counter to set
+ * @param value New counter value
+ */
+static inline void timer_set_count(timer_dev *dev, uint16 value) {
+ (dev->regs).bas->CNT = value;
+}
+
+/**
+ * @brief Returns the given timer's prescaler.
+ *
+ * Note that if the timer's prescaler is set (e.g. via
+ * timer_set_prescaler() or accessing a TIMx_PSC register), the value
+ * returned by this function will reflect the new setting, but the
+ * timer's counter will only reflect the new prescaler at the next
+ * update event.
+ *
+ * @param dev Timer whose prescaler to return
+ * @see timer_generate_update()
+ */
+static inline uint16 timer_get_prescaler(timer_dev *dev) {
+ return (uint16)(dev->regs).bas->PSC;
+}
+
+/**
+ * @brief Set a timer's prescale value.
+ *
+ * The new value will not take effect until the next update event.
+ *
+ * @param dev Timer whose prescaler to set
+ * @param psc New prescaler value
+ * @see timer_generate_update()
+ */
+static inline void timer_set_prescaler(timer_dev *dev, uint16 psc) {
+ (dev->regs).bas->PSC = psc;
+}
+
+/**
+ * @brief Returns a timer's reload value.
+ * @param dev Timer whose reload value to return
+ */
+static inline uint16 timer_get_reload(timer_dev *dev) {
+ return (uint16)(dev->regs).bas->ARR;
+}
+
+/**
+ * @brief Set a timer's reload value.
+ * @param dev Timer whose reload value to set
+ * @param arr New reload value to use. Takes effect at next update event.
+ * @see timer_generate_update()
+ */
+static inline void timer_set_reload(timer_dev *dev, uint16 arr) {
+ (dev->regs).bas->ARR = arr;
+}
+
+/**
+ * @brief Get the compare value for the given timer channel.
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param channel Channel whose compare value to get.
+ */
+static inline uint16 timer_get_compare(timer_dev *dev, uint8 channel) {
+ __io uint32 *ccr = &(dev->regs).gen->CCR1 + (channel - 1);
+ return *ccr;
+}
+
+/**
+ * @brief Set the compare value for the given timer channel.
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param channel Channel whose compare value to set.
+ * @param value New compare value.
+ */
+static inline void timer_set_compare(timer_dev *dev,
+ uint8 channel,
+ uint16 value) {
+ __io uint32 *ccr = &(dev->regs).gen->CCR1 + (channel - 1);
+ *ccr = value;
+}
+
+/**
+ * @brief Generate an update event for the given timer.
+ *
+ * Normally, this will cause the prescaler and auto-reload values in
+ * the PSC and ARR registers to take immediate effect. However, this
+ * function will do nothing if the UDIS bit is set in the timer's CR1
+ * register (UDIS is cleared by default).
+ *
+ * @param dev Timer device to generate an update for.
+ */
+static inline void timer_generate_update(timer_dev *dev) {
+ *bb_perip(&(dev->regs).bas->EGR, TIMER_EGR_UG_BIT) = 1;
+}
+
+/**
+ * @brief Enable a timer's trigger DMA request
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL
+ */
+static inline void timer_trigger_dma_enable_request(timer_dev *dev) {
+ *bb_perip(&(dev->regs).gen->DIER, TIMER_DIER_TDE_BIT) = 1;
+}
+
+/**
+ * @brief Disable a timer's trigger DMA request
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL
+ */
+static inline void timer_trigger_dma_disable_request(timer_dev *dev) {
+ *bb_perip(&(dev->regs).gen->DIER, TIMER_DIER_TDE_BIT) = 0;
+}
+
+/**
+ * @brief Enable a timer channel's DMA request.
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL
+ * @param channel Channel whose DMA request to enable.
+ */
+static inline void timer_dma_enable_request(timer_dev *dev, uint8 channel) {
+ *bb_perip(&(dev->regs).gen->DIER, channel + 8) = 1;
+}
+
+/**
+ * @brief Disable a timer channel's DMA request.
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param channel Channel whose DMA request to disable.
+ */
+static inline void timer_dma_disable_request(timer_dev *dev, uint8 channel) {
+ *bb_perip(&(dev->regs).gen->DIER, channel + 8) = 0;
+}
+
+/**
+ * @brief Enable a timer interrupt.
+ * @param dev Timer device.
+ * @param interrupt Interrupt number to enable; this may be any
+ * timer_interrupt_id value appropriate for the timer.
+ * @see timer_interrupt_id
+ * @see timer_channel
+ */
+static inline void timer_enable_interrupt(timer_dev *dev, uint8 interrupt) {
+ *bb_perip(&(dev->regs).adv->DIER, interrupt) = 1;
+}
+
+/**
+ * @brief Disable a timer interrupt.
+ * @param dev Timer device.
+ * @param interrupt Interrupt number to disable; this may be any
+ * timer_interrupt_id value appropriate for the timer.
+ * @see timer_interrupt_id
+ * @see timer_channel
+ */
+static inline void timer_disable_interrupt(timer_dev *dev, uint8 interrupt) {
+ *bb_perip(&(dev->regs).adv->DIER, interrupt) = 0;
+}
+
+/**
+ * @brief Enable a timer channel's capture/compare signal.
+ *
+ * If the channel is configured as output, the corresponding output
+ * compare signal will be output on the corresponding output pin. If
+ * the channel is configured as input, enables capture of the counter
+ * value into the input capture/compare register.
+ *
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param channel Channel to enable, from 1 to 4.
+ */
+static inline void timer_cc_enable(timer_dev *dev, uint8 channel) {
+ *bb_perip(&(dev->regs).gen->CCER, 4 * (channel - 1)) = 1;
+}
+
+/**
+ * @brief Disable a timer channel's output compare or input capture signal.
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param channel Channel to disable, from 1 to 4.
+ * @see timer_cc_enable()
+ */
+static inline void timer_cc_disable(timer_dev *dev, uint8 channel) {
+ *bb_perip(&(dev->regs).gen->CCER, 4 * (channel - 1)) = 0;
+}
+
+/**
+ * @brief Get a channel's capture/compare output polarity
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param channel Channel whose capture/compare output polarity to get.
+ * @return Polarity, either 0 or 1.
+ * @see timer_cc_set_polarity()
+ */
+static inline uint8 timer_cc_get_polarity(timer_dev *dev, uint8 channel) {
+ return *bb_perip(&(dev->regs).gen->CCER, 4 * (channel - 1) + 1);
+}
+
+/**
+ * @brief Set a timer channel's capture/compare output polarity.
+ *
+ * If the timer channel is configured as output: polarity == 0 means
+ * the output channel will be active high; polarity == 1 means active
+ * low.
+ *
+ * If the timer channel is configured as input: polarity == 0 means
+ * capture is done on the rising edge of ICn; when used as an external
+ * trigger, ICn is non-inverted. polarity == 1 means capture is done
+ * on the falling edge of ICn; when used as an external trigger, ICn
+ * is inverted.
+ *
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param channel Channel whose capture/compare output polarity to set.
+ * @param pol New polarity, 0 or 1.
+ */
+static inline void timer_cc_set_polarity(timer_dev *dev,
+ uint8 channel,
+ uint8 pol) {
+ *bb_perip(&(dev->regs).gen->CCER, 4 * (channel - 1) + 1) = pol;
+}
+
+/**
+ * @brief Get a timer's DMA burst length.
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @return Number of bytes to be transferred per DMA request, from 1 to 18.
+ */
+static inline uint8 timer_get_dma_burst_length(timer_dev *dev) {
+ uint32 dbl = ((dev->regs).gen->DCR & TIMER_DCR_DBL) >> 8;
+ return dbl + 1; /* 0 means 1 byte, etc. */
+}
+
+/**
+ * @brief Set a timer's DMA burst length.
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param length DMA burst length; i.e., number of bytes to transfer
+ * per DMA request, from 1 to 18.
+ */
+static inline void timer_set_dma_burst_length(timer_dev *dev, uint8 length) {
+ uint32 tmp = (dev->regs).gen->DCR;
+ tmp &= ~TIMER_DCR_DBL;
+ tmp |= (length - 1) << 8;
+ (dev->regs).gen->DCR = tmp;
+}
+
+/**
+ * @brief Timer DMA base address.
+ *
+ * Defines the base address for DMA transfers.
+ */
+typedef enum timer_dma_base_address {
+ TIMER_DMA_BASE_CR1 = TIMER_DCR_DBA_CR1, /**< Base is control register 1 */
+ TIMER_DMA_BASE_CR2 = TIMER_DCR_DBA_CR2, /**< Base is control register 2 */
+ TIMER_DMA_BASE_SMCR = TIMER_DCR_DBA_SMCR, /**< Base is slave mode
+ control register */
+ TIMER_DMA_BASE_DIER = TIMER_DCR_DBA_DIER, /**< Base is DMA interrupt enable
+ register */
+ TIMER_DMA_BASE_SR = TIMER_DCR_DBA_SR, /**< Base is status register */
+ TIMER_DMA_BASE_EGR = TIMER_DCR_DBA_EGR, /**< Base is event generation
+ register */
+ TIMER_DMA_BASE_CCMR1 = TIMER_DCR_DBA_CCMR1, /**< Base is capture/compare
+ mode register 1 */
+ TIMER_DMA_BASE_CCMR2 = TIMER_DCR_DBA_CCMR2, /**< Base is capture/compare
+ mode register 2 */
+ TIMER_DMA_BASE_CCER = TIMER_DCR_DBA_CCER, /**< Base is capture/compare
+ enable register */
+ TIMER_DMA_BASE_CNT = TIMER_DCR_DBA_CNT, /**< Base is counter */
+ TIMER_DMA_BASE_PSC = TIMER_DCR_DBA_PSC, /**< Base is prescaler */
+ TIMER_DMA_BASE_ARR = TIMER_DCR_DBA_ARR, /**< Base is auto-reload
+ register */
+ TIMER_DMA_BASE_RCR = TIMER_DCR_DBA_RCR, /**< Base is repetition
+ counter register */
+ TIMER_DMA_BASE_CCR1 = TIMER_DCR_DBA_CCR1, /**< Base is capture/compare
+ register 1 */
+ TIMER_DMA_BASE_CCR2 = TIMER_DCR_DBA_CCR2, /**< Base is capture/compare
+ register 2 */
+ TIMER_DMA_BASE_CCR3 = TIMER_DCR_DBA_CCR3, /**< Base is capture/compare
+ register 3 */
+ TIMER_DMA_BASE_CCR4 = TIMER_DCR_DBA_CCR4, /**< Base is capture/compare
+ register 4 */
+ TIMER_DMA_BASE_BDTR = TIMER_DCR_DBA_BDTR, /**< Base is break and
+ dead-time register */
+ TIMER_DMA_BASE_DCR = TIMER_DCR_DBA_DCR, /**< Base is DMA control
+ register */
+ TIMER_DMA_BASE_DMAR = TIMER_DCR_DBA_DMAR /**< Base is DMA address for
+ full transfer */
+} timer_dma_base_address;
+
+/**
+ * @brief Get the timer's DMA base address.
+ *
+ * Some restrictions apply; see ST RM0008.
+ *
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @return DMA base address
+ */
+static inline timer_dma_base_address
+timer_get_dma_base_address(timer_dev *dev) {
+ uint32 dcr = (dev->regs).gen->DCR;
+ return (timer_dma_base_address)(dcr & TIMER_DCR_DBA);
+}
+
+/**
+ * @brief Set the timer's DMA base address.
+ *
+ * Some restrictions apply; see ST RM0008.
+ *
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param dma_base DMA base address.
+ */
+static inline void
+timer_set_dma_base_address(timer_dev *dev, timer_dma_base_address dma_base) {
+ uint32 tmp = (dev->regs).gen->DCR;
+ tmp &= ~TIMER_DCR_DBA;
+ tmp |= dma_base;
+ (dev->regs).gen->DCR = tmp;
+}
+
+/**
+ * Timer output compare modes.
+ */
+typedef enum timer_oc_mode {
+ TIMER_OC_MODE_FROZEN = 0 << 4, /**< Frozen: comparison between output
+ compare register and counter has no
+ effect on the outputs. */
+ TIMER_OC_MODE_ACTIVE_ON_MATCH = 1 << 4, /**< OCxREF signal is forced
+ high when the count matches
+ the channel capture/compare
+ register. */
+ TIMER_OC_MODE_INACTIVE_ON_MATCH = 2 << 4, /**< OCxREF signal is forced
+ low when the counter matches
+ the channel capture/compare
+ register. */
+ TIMER_OC_MODE_TOGGLE = 3 << 4, /**< OCxREF toggles when counter
+ matches the cannel capture/compare
+ register. */
+ TIMER_OC_MODE_FORCE_INACTIVE = 4 << 4, /**< OCxREF is forced low. */
+ TIMER_OC_MODE_FORCE_ACTIVE = 5 << 4, /**< OCxREF is forced high. */
+ TIMER_OC_MODE_PWM_1 = 6 << 4, /**< PWM mode 1. In upcounting, channel is
+ active as long as count is less than
+ channel capture/compare register, else
+ inactive. In downcounting, channel is
+ inactive as long as count exceeds
+ capture/compare register, else
+ active. */
+ TIMER_OC_MODE_PWM_2 = 7 << 4 /**< PWM mode 2. In upcounting, channel is
+ inactive as long as count is less than
+ capture/compare register, else active.
+ In downcounting, channel is active as
+ long as count exceeds capture/compare
+ register, else inactive. */
+} timer_oc_mode;
+
+/**
+ * Timer output compare mode flags.
+ * @see timer_oc_set_mode()
+ */
+typedef enum timer_oc_mode_flags {
+ TIMER_OC_CE = BIT(7), /**< Output compare clear enable. */
+ TIMER_OC_PE = BIT(3), /**< Output compare preload enable. */
+ TIMER_OC_FE = BIT(2) /**< Output compare fast enable. */
+} timer_oc_mode_flags;
+
+/**
+ * @brief Configure a channel's output compare mode.
+ *
+ * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL.
+ * @param channel Channel to configure in output compare mode.
+ * @param mode Timer mode to set.
+ * @param flags OR of timer_oc_mode_flags.
+ * @see timer_oc_mode
+ * @see timer_oc_mode_flags
+ */
+static inline void timer_oc_set_mode(timer_dev *dev,
+ uint8 channel,
+ timer_oc_mode mode,
+ uint8 flags) {
+ uint8 bit0 = channel & 1;
+ uint8 bit1 = (channel >> 1) & 1;
+ /* channel == 1,2 -> CCMR1; channel == 3,4 -> CCMR2 */
+ __io uint32 *ccmr = &(dev->regs).gen->CCMR1 + bit1;
+ /* channel == 1,3 -> shift = 0, channel == 2,4 -> shift = 8 */
+ uint8 shift = 8 * (1 - bit0);
+
+ uint32 tmp = *ccmr;
+ tmp &= ~(0xFF << shift);
+ tmp |= (mode | flags | TIMER_CCMR_CCS_OUTPUT) << shift;
+ *ccmr = tmp;
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
diff --git a/libmaple/timers.c b/libmaple/timers.c
deleted file mode 100644
index 29aeeba..0000000
--- a/libmaple/timers.c
+++ /dev/null
@@ -1,530 +0,0 @@
-/******************************************************************************
- * The MIT License
- *
- * 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.
- *****************************************************************************/
-
-/**
- * @brief General timer routines
- */
-
-/* TODO: actually support timer5 and timer8 */
-
-#include "libmaple.h"
-#include "rcc.h"
-#include "nvic.h"
-#include "timers.h"
-
-/* Timer descriptor table */
-struct timer_dev timer_dev_table[] = {
- [TIMER1] = {
- .base = (timer_port*)TIMER1_BASE,
- .rcc_dev_num = RCC_TIMER1,
- .nvic_dev_num = NVIC_TIMER1
- },
- [TIMER2] = {
- .base = (timer_port*)TIMER2_BASE,
- .rcc_dev_num = RCC_TIMER2,
- .nvic_dev_num = NVIC_TIMER2
- },
- [TIMER3] = {
- .base = (timer_port*)TIMER3_BASE,
- .rcc_dev_num = RCC_TIMER3,
- .nvic_dev_num = NVIC_TIMER3
- },
- [TIMER4] = {
- .base = (timer_port*)TIMER4_BASE,
- .rcc_dev_num = RCC_TIMER4,
- .nvic_dev_num = NVIC_TIMER4
- },
-#if NR_TIMERS >= 8
- /* High density devices only (eg, Maple Native) */
- [TIMER5] = {
- .base = (timer_port*)TIMER5_BASE,
- .rcc_dev_num = RCC_TIMER5,
- .nvic_dev_num = NVIC_TIMER5
- },
- [TIMER8] = {
- .base = (timer_port*)TIMER8_BASE,
- .rcc_dev_num = RCC_TIMER8,
- .nvic_dev_num = NVIC_TIMER8
- },
-#endif
-};
-
-/* This function should probably be rewriten to take (timer_num, mode)
- * and have prescaler set elsewhere. The mode can be passed through to
- * set_mode at the end */
-void timer_init(timer_dev_num timer_num, uint16 prescale) {
- /* TODO: doesn't catch 6+7 */
-
- timer_port *timer = timer_dev_table[timer_num].base;
- uint8 is_advanced = 0;
-
- if (timer_num == TIMER1) {
- is_advanced = 1;
- }
-#if NR_TIMERS >= 8
- if (timer_num == TIMER8) {
- is_advanced = 1;
- }
-#endif
-
- rcc_clk_enable(timer_dev_table[timer_num].rcc_dev_num);
-
- timer->CR1 = ARPE; // No clock division
- // Do not buffer auto-reload preload
- // Edge aligned
- // Upcounter
- // Do not stop counter at update event
- // Update events enabled (etc, see bits [1:2])
- // Counter disabled for now
-
- timer->PSC = prescale; // Prescaling by prescale (duh)
- timer->ARR = 0xFFFF; // Max reload cont
-
- /* initialize all the channels to 50% duty cycle,
- * TODO: none of them actually get output unless the gpio pin
- * is set, this will probably consume a bit more power but
- * we'll worry about that later. */
- timer->CCR1 = 0x8FFF; // PWM start value
- timer->CCMR1 |= 0x68; // PWM mode 1, enable preload register.
-
- timer->CCR2 = 0x8FFF; // PWM start value
- timer->CCMR1 |= (0x68 << 8);// PWM mode 1, enable preload register.
-
- timer->CCR3 = 0x8FFF; // PWM start value
- timer->CCMR2 |= 0x68; // PWM mode 1, enable preload register.
-
- timer->CCR4 = 0x8FFF; // PWM start value
- timer->CCMR2 |= (0x68 << 8);// PWM mode 1, enable preload register.
-
- /* Advanced timer? */
- if (is_advanced) {
- timer->BDTR = 0x8000; // moe enable
- }
-
- timer->SR = 0; // clear it
- timer->DIER = 0; // disable update interrupt
- timer->EGR = 1; // Initialize update event and shadow registers
- timer->CR1 |= 1; // Enable timer
-}
-
-/* Stops the counter; the mode and settings are not modified */
-void timer_pause(timer_dev_num timer_num) {
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer->CR1 &= ~(0x0001); // CEN
-}
-
-/* Starts the counter; the mode and settings are not modified */
-void timer_resume(timer_dev_num timer_num) {
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer->CR1 |= 0x0001; // CEN
-}
-
-/* Returns the current timer counter value. Probably very inaccurate
- * if the counter is running with a low prescaler. */
-uint16 timer_get_count(timer_dev_num timer_num) {
- timer_port *timer = timer_dev_table[timer_num].base;
-
- return timer->CNT;
-}
-
-/* This function sets the counter value via register for the specified
- * timer. Can't think of specific usecases except for resetting to
- * zero but it's easy to implement and allows for "creative"
- * programming */
-void timer_set_count(timer_dev_num timer_num, uint16 value) {
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer->CNT = value;
-}
-
-/* Get the prescaler buffer value (remember, the actual prescaler
- * doesn't get set until an update event). */
-uint16 timer_get_prescaler(timer_dev_num timer_num) {
- timer_port *timer = timer_dev_table[timer_num].base;
- return timer->PSC;
-}
-
-/* Sets the prescaler */
-void timer_set_prescaler(timer_dev_num timer_num, uint16 prescale) {
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer->PSC = prescale;
-}
-
-/* Get the reload value for the entire timer. */
-uint16 timer_get_reload(timer_dev_num timer_num) {
- timer_port *timer = timer_dev_table[timer_num].base;
- return timer->ARR;
-}
-
-/* This sets the "reload" or "overflow" value for the entire timer. We
- * should probably settle on either "reload" or "overflow" to prevent
- * confusion? */
-void timer_set_reload(timer_dev_num timer_num, uint16 max_reload) {
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer->ARR = max_reload;
-}
-
-/* This quickly disables all 4 timers, presumably as part of a system shutdown
- * or similar to prevent interrupts and PWM output without 16 seperate function
- * calls to timer_set_mode */
-void timer_disable_all(void) {
- // TODO: refactor
-
- /* Note: this must be very robust because it gets called from,
- e.g., ASSERT */
- timer_port *timer;
-#if NR_TIMERS >= 8
- timer_port *timers[6] = { (timer_port*)TIMER1_BASE,
- (timer_port*)TIMER2_BASE,
- (timer_port*)TIMER3_BASE,
- (timer_port*)TIMER4_BASE,
- (timer_port*)TIMER5_BASE,
- (timer_port*)TIMER8_BASE,
- };
- uint8 i;
- for (i = 0; i < 6; i++) {
- timer = timers[i];
- timer->CR1 = 0;
- timer->CCER = 0;
- }
-#else
- timer_port *timers[4] = { (timer_port*)TIMER1_BASE,
- (timer_port*)TIMER2_BASE,
- (timer_port*)TIMER3_BASE,
- (timer_port*)TIMER4_BASE,
- };
- uint8 i;
- for (i = 0; i < 4; i++) {
- timer = timers[i];
- timer->CR1 = 0;
- timer->CCER = 0;
- }
-#endif
-}
-
-/* Sets the mode of individual timer channels, including a DISABLE mode */
-void timer_set_mode(timer_dev_num timer_num, uint8 channel, TimerMode mode) {
- timer_port *timer = timer_dev_table[timer_num].base;
- ASSERT(channel >= 1);
-
- switch(mode) {
- case TIMER_DISABLED:
- /* Disable the channel
- * Disable any interrupt
- * Clear interrupt SR? (TODO) */
- timer->DIER &= ~(1 << channel); // 1-indexed compare nums
- timer_detach_interrupt(timer_num, channel);
- timer->CCER &= ~(1 << (4*(channel - 1))); // 0-indexed
- break;
- case TIMER_PWM:
- /* Set CCMR mode
- * Keep existing reload value
- * Disable any interrupt
- * Clear interrupt SR? (TODO)
- * Enable channel */
- timer->DIER &= ~(1 << channel); // 1-indexed compare nums
- switch (channel) {
- case 1:
- timer->CCMR1 &= ~(0xFF);
- timer->CCMR1 |= 0x68; // PWM mode 1, enable preload register.
- break;
- case 2:
- timer->CCMR1 &= ~(0xFF00);
- timer->CCMR1 |= (0x68 << 8);// PWM mode 1, enable preload register.
- break;
- case 3:
- timer->CCMR2 &= ~(0xFF);
- timer->CCMR2 |= 0x68; // PWM mode 1, enable preload register.
- break;
- case 4:
- timer->CCMR2 &= ~(0xFF00);
- timer->CCMR2 |= (0x68 << 8);// PWM mode 1, enable preload register.
- break;
- default:
- ASSERT(0);
- }
- timer->CCER |= (1 << (4*(channel - 1))); // Enable
- break;
- case TIMER_OUTPUTCOMPARE:
- /* Set CCMR mode
- * Keep existing reload value
- * Don't modify interrupt (needs to be attached to enable)
- * Clear interrupt SR? (TODO)
- * Enable channel */
- switch (channel) {
- case 1:
- timer->CCMR1 &= ~(0xFF);
- timer->CCMR1 |= 0x0010; // PWM mode 1, enable preload register.
- break;
- case 2:
- timer->CCMR1 &= ~(0xFF00);
- timer->CCMR1 |= 0x1000; // PWM mode 1, enable preload register.
- break;
- case 3:
- timer->CCMR2 &= ~(0xFF);
- timer->CCMR2 |= 0x0010; // PWM mode 1, enable preload register.
- break;
- case 4:
- timer->CCMR2 &= ~(0xFF00);
- timer->CCMR2 |= 0x1000; // PWM mode 1, enable preload register.
- break;
- default:
- ASSERT(0);
- }
- timer->CCER |= (1 << (4*(channel - 1))); // Enable
- break;
- default:
- ASSERT(0);
- }
-}
-
-uint16 timer_get_compare_value(timer_dev_num timer_num, uint8 channel_num) {
- /* faster: just read TIMERx_CHy_CCR (see timers.h) */
- ASSERT(channel_num > 0 && channel_num <= 4);
- timer_port *timer = timer_dev_table[timer_num].base;
- switch(channel_num) {
- case 1:
- return timer->CCR1;
- case 2:
- return timer->CCR2;
- case 3:
- return timer->CCR3;
- case 4:
- return timer->CCR4;
- default: /* in case ASSERT is disabled */
- return 0;
- }
-}
-
-/* This sets the compare value (aka the trigger) for a given timer
- * channel */
-void timer_set_compare_value(timer_dev_num timer_num,
- uint8 channel_num,
- uint16 value) {
- ASSERT(channel_num > 0 && channel_num <= 4);
-
- /* The faster version of this function is the inline
- timer_pwm_write_ccr */
- timer_port *timer = timer_dev_table[timer_num].base;
-
- switch(channel_num) {
- case 1:
- timer->CCR1 = value;
- break;
- case 2:
- timer->CCR2 = value;
- break;
- case 3:
- timer->CCR3 = value;
- break;
- case 4:
- timer->CCR4 = value;
- break;
- }
-}
-
-/* Stores a pointer to the passed usercode interrupt function and configures
- * the actual ISR so that it will actually be called */
-void timer_attach_interrupt(timer_dev_num timer_num,
- uint8 compare_num,
- voidFuncPtr handler) {
- ASSERT(compare_num > 0 && compare_num <= 4);
-
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer_dev_table[timer_num].handlers[compare_num-1] = handler;
- timer->DIER |= (1 << compare_num); // 1-indexed compare nums
- nvic_irq_enable(timer_dev_table[timer_num].nvic_dev_num);
-}
-
-void timer_detach_interrupt(timer_dev_num timer_num, uint8 compare_num) {
- ASSERT(compare_num > 0 && compare_num <= 4);
-
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer_dev_table[timer_num].handlers[compare_num-1] = 0;
- timer->DIER &= ~(1 << compare_num); // 1-indexed compare nums
-}
-
-void timer_generate_update(timer_dev_num timer_num) {
- /* cause update event by setting UG bit in EGR. updates prescaler
- ratio etc. */
- timer_port *timer = timer_dev_table[timer_num].base;
- timer->EGR |= 0x1;
-}
-
-/* The following are the actual interrupt handlers; 1 for each timer which must
- * determine which actual compare value (aka channel) was triggered.
- *
- * These ISRs get called when the timer interrupt is enabled, the
- * timer is running, and the timer count equals any of the CCR
- * registers /or/ has overflowed.
- *
- * This is a rather long implementation... */
-void TIM1_CC_IRQHandler(void) {
- timer_port *timer = (timer_port*)TIMER1_BASE;
- uint16 sr_buffer;
- sr_buffer = timer->SR;
-
- /* Simply switch/case-ing here doesn't work because multiple
- * CC flags may be high. */
- if(sr_buffer & 0x10){ // CC4 flag
- timer->SR &= ~(0x10);
- if(timer_dev_table[TIMER1].handlers[3]) {
- timer_dev_table[TIMER1].handlers[3]();
- }
- }
- if(sr_buffer & 0x8){ // CC3 flag
- timer->SR &= ~(0x8);
- if(timer_dev_table[TIMER1].handlers[2]) {
- timer_dev_table[TIMER1].handlers[2]();
- }
- }
- if(sr_buffer & 0x4){ // CC2 flag
- timer->SR &= ~(0x4);
- if(timer_dev_table[TIMER1].handlers[1]) {
- timer_dev_table[TIMER1].handlers[1]();
- }
- }
- if(sr_buffer & 0x2){ // CC1 flag
- timer->SR &= ~(0x2);
- if(timer_dev_table[TIMER1].handlers[0]) {
- timer_dev_table[TIMER1].handlers[0]();
- }
- }
- if(sr_buffer & 0x1){ // Update flag
- timer->SR &= ~(0x1);
- //timer->EGR = 1;
- }
-}
-void TIM2_IRQHandler(void) {
- /* This is a rather long implementation... */
- timer_port *timer = (timer_port*)TIMER2_BASE;
- uint16 sr_buffer;
- sr_buffer = timer->SR;
-
- if(sr_buffer & 0x10){ // CC4 flag
- timer->SR &= ~(0x10);
- if(timer_dev_table[TIMER2].handlers[3]) {
- timer_dev_table[TIMER2].handlers[3]();
- }
- }
- if(sr_buffer & 0x8){ // CC3 flag
- timer->SR &= ~(0x8);
- if(timer_dev_table[TIMER2].handlers[2]) {
- timer_dev_table[TIMER2].handlers[2]();
- }
- }
- if(sr_buffer & 0x4){ // CC2 flag
- timer->SR &= ~(0x4);
- if(timer_dev_table[TIMER2].handlers[1]) {
- timer_dev_table[TIMER2].handlers[1]();
- }
- }
- if(sr_buffer & 0x2){ // CC1 flag
- timer->SR &= ~(0x2);
- if(timer_dev_table[TIMER2].handlers[0]) {
- timer_dev_table[TIMER2].handlers[0]();
- }
- }
- if(sr_buffer & 0x1){ // Update flag
- timer->SR &= ~(0x1);
- //timer->EGR = 1;
- }
-}
-void TIM3_IRQHandler(void) {
- /* This is a rather long implementation... */
- timer_port *timer = (timer_port*)TIMER3_BASE;
- uint16 sr_buffer;
- sr_buffer = timer->SR;
-
- if(sr_buffer & 0x10){ // CC4 flag
- timer->SR &= ~(0x10);
- if(timer_dev_table[TIMER3].handlers[3]) {
- timer_dev_table[TIMER3].handlers[3]();
- }
- }
- if(sr_buffer & 0x8){ // CC3 flag
- timer->SR &= ~(0x8);
- if(timer_dev_table[TIMER3].handlers[2]) {
- timer_dev_table[TIMER3].handlers[2]();
- }
- }
- if(sr_buffer & 0x4){ // CC2 flag
- timer->SR &= ~(0x4);
- if(timer_dev_table[TIMER3].handlers[1]) {
- timer_dev_table[TIMER3].handlers[1]();
- }
- }
- if(sr_buffer & 0x2){ // CC1 flag
- timer->SR &= ~(0x2);
- if(timer_dev_table[TIMER3].handlers[0]) {
- timer_dev_table[TIMER3].handlers[0]();
- }
- }
- if(sr_buffer & 0x1){ // Update flag
- timer->SR &= ~(0x1);
- //timer->EGR = 1;
- }
-}
-
-void TIM4_IRQHandler(void) {
- /* This is a rather long implementation... */
- timer_port*timer = (timer_port*)TIMER4_BASE;
- uint16 sr_buffer;
- sr_buffer = timer->SR;
-
- if(sr_buffer & 0x10){ // CC4 flag
- timer->SR &= ~(0x10);
- if(timer_dev_table[TIMER4].handlers[3]) {
- timer_dev_table[TIMER4].handlers[3]();
- }
- }
- if(sr_buffer & 0x8){ // CC3 flag
- timer->SR &= ~(0x8);
- if(timer_dev_table[TIMER4].handlers[2]) {
- timer_dev_table[TIMER4].handlers[2]();
- }
- }
- if(sr_buffer & 0x4){ // CC2 flag
- timer->SR &= ~(0x4);
- if(timer_dev_table[TIMER4].handlers[1]) {
- timer_dev_table[TIMER4].handlers[1]();
- }
- }
- if(sr_buffer & 0x2){ // CC1 flag
- timer->SR &= ~(0x2);
- if(timer_dev_table[TIMER4].handlers[0]) {
- timer_dev_table[TIMER4].handlers[0]();
- }
- }
- if(sr_buffer & 0x1){ // Update flag
- timer->SR &= ~(0x1);
- //timer->EGR = 1;
- }
-}
diff --git a/libmaple/timers.h b/libmaple/timers.h
deleted file mode 100644
index 99bcab6..0000000
--- a/libmaple/timers.h
+++ /dev/null
@@ -1,435 +0,0 @@
-/******************************************************************************
- * The MIT License
- *
- * 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 timers.h
- *
- * @brief Timer prototypes and various definitions
- */
-
-/* Note to self:
- * The timer clock frequencies are automatically fixed by hardware.
- * There are two cases:
- * 1. if the APB prescaler is 1, the timer clock frequencies are
- * set to the same frequency as that of the APB domain to which
- * the timers are connected.
- * 2. otherwise, they are set to twice (x2) the frequency of the
- * APB domain to which the timers are connected.
- * See stm32 manual, 77/995
- *
- * hence, 72 mhz timers
- */
-
-/* Maple Timer channels:
- * Timer Maple Pin STM32 Pin Type
- * TIM1_CH1 D6 PA8 Advanced
- * TIM1_CH2 D7 PA9 Advanced
- * TIM1_CH3 D8 PA10 Advanced
- *
- * TIM2_CH1 D2 PA0
- * TIM2_CH2 D3 PA1
- * TIM2_CH3 D1 PA2
- * TIM2_CH4 D0 PA3
- *
- * TIM3_CH1 D12 PA6
- * TIM3_CH2 D11 PA7
- * TIM3_CH3 EXT7 PB0
- * TIM3_CH4 EXT8 PB1
- *
- * TIM4_CH1 EXT5 PB6
- * TIM4_CH1 EXT9 PB7
- * TIM4_CH1 EXT15 PB8
- * TIM4_CH1 EXT4 PB9
- *
- * Not supported:
- * TIM1_CH4 USBDM, not available PA11 Advanced
- * TIM1_CH1_N EXT12 PB13
- * TIM1_CH2_N EXT13 PB14
- * TIM1_CH3_N EXT14 PB15
- * */
-
-/* I don't like the Arduino API for dealing with pin modes.
- * How about...
- *
- * pinMode(digitalPin, PWM);
- * pwmWrite(digitalPin) */
-
-#ifndef _TIMERS_H_
-#define _TIMERS_H_
-
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-typedef volatile uint16* TimerCCR;
-
-#define TIMER1_BASE 0x40012C00
-#define TIMER2_BASE 0x40000000
-#define TIMER3_BASE 0x40000400
-#define TIMER4_BASE 0x40000800
-#define TIMER5_BASE 0x40000C00 // High-density devices only
-#define TIMER6_BASE 0x40001000 // High-density devices only
-#define TIMER7_BASE 0x40001400 // High-density devices only
-#define TIMER8_BASE 0x40013400 // High-density devices only
-
-#define ARPE BIT(7) // Auto-reload preload enable
-#define NOT_A_TIMER 0
-
-#define TIMER_CCR(NUM,CHAN) (TIMER ## NUM ## _CH ## CHAN ## _CRR)
-
-/* Timers 1-4 are present on the entire STM32 line. */
-
-#define TIMER1_CH1_CCR ((TimerCCR)(TIMER1_BASE + 0x34))
-#define TIMER1_CH2_CCR ((TimerCCR)(TIMER1_BASE + 0x38))
-#define TIMER1_CH3_CCR ((TimerCCR)(TIMER1_BASE + 0x3C))
-#define TIMER1_CH4_CCR ((TimerCCR)(TIMER1_BASE + 0x40))
-
-#define TIMER2_CH1_CCR ((TimerCCR)(TIMER2_BASE + 0x34))
-#define TIMER2_CH2_CCR ((TimerCCR)(TIMER2_BASE + 0x38))
-#define TIMER2_CH3_CCR ((TimerCCR)(TIMER2_BASE + 0x3C))
-#define TIMER2_CH4_CCR ((TimerCCR)(TIMER2_BASE + 0x40))
-
-#define TIMER3_CH1_CCR ((TimerCCR)(TIMER3_BASE + 0x34))
-#define TIMER3_CH2_CCR ((TimerCCR)(TIMER3_BASE + 0x38))
-#define TIMER3_CH3_CCR ((TimerCCR)(TIMER3_BASE + 0x3C))
-#define TIMER3_CH4_CCR ((TimerCCR)(TIMER3_BASE + 0x40))
-
-#define TIMER4_CH1_CCR ((TimerCCR)(TIMER4_BASE + 0x34))
-#define TIMER4_CH2_CCR ((TimerCCR)(TIMER4_BASE + 0x38))
-#define TIMER4_CH3_CCR ((TimerCCR)(TIMER4_BASE + 0x3C))
-#define TIMER4_CH4_CCR ((TimerCCR)(TIMER4_BASE + 0x40))
-
-/* Timers 5 and 8 are in high-density devices only (such as Maple
- Native). Timers 6 and 7 in these devices have no output compare
- pins. */
-
-#define TIMER5_CH1_CCR ((TimerCCR)(TIMER5_BASE + 0x34))
-#define TIMER5_CH2_CCR ((TimerCCR)(TIMER5_BASE + 0x38))
-#define TIMER5_CH3_CCR ((TimerCCR)(TIMER5_BASE + 0x3C))
-#define TIMER5_CH4_CCR ((TimerCCR)(TIMER5_BASE + 0x40))
-
-#define TIMER8_CH1_CCR ((TimerCCR)(TIMER8_BASE + 0x34))
-#define TIMER8_CH2_CCR ((TimerCCR)(TIMER8_BASE + 0x38))
-#define TIMER8_CH3_CCR ((TimerCCR)(TIMER8_BASE + 0x3C))
-#define TIMER8_CH4_CCR ((TimerCCR)(TIMER8_BASE + 0x40))
-
-/**
- * Used to configure the behavior of a timer.
- */
-typedef enum TimerMode {
- TIMER_DISABLED, /**< In this mode, the timer stops counting,
- interrupts are not called, and no state changes
- are output. */
- TIMER_PWM, /**< This is the default mode for pins after
- initialization. */
- TIMER_OUTPUTCOMPARE, /**< In this mode, the timer counts from 0 to
- its reload value repeatedly; every time
- the counter value reaches one of the
- channel compare values, the corresponding
- interrupt is fired. */
-} TimerMode;
-
-typedef struct {
- /* Fields up to ARR common to general purpose (2,3,4,5), advanced
- control (1,8) and basic (6, 7) timers: */
- volatile uint16 CR1;
- uint16 RESERVED0;
- volatile uint16 CR2;
- uint16 RESERVED1;
- volatile uint16 SMCR;
- uint16 RESERVED2;
- volatile uint16 DIER;
- uint16 RESERVED3;
- volatile uint16 SR;
- uint16 RESERVED4;
- volatile uint16 EGR;
- uint16 RESERVED5;
- volatile uint16 CCMR1;
- uint16 RESERVED6;
- volatile uint16 CCMR2;
- uint16 RESERVED7;
- volatile uint16 CCER;
- uint16 RESERVED8;
- volatile uint16 CNT;
- uint16 RESERVED9;
- volatile uint16 PSC;
- uint16 RESERVED10;
- volatile uint16 ARR;
- uint16 RESERVED11;
- /* Basic timers have none of the following: */
- volatile uint16 RCR; /* Advanced control timers only */
- uint16 RESERVED12; /* Advanced control timers only */
- volatile uint16 CCR1;
- uint16 RESERVED13;
- volatile uint16 CCR2;
- uint16 RESERVED14;
- volatile uint16 CCR3;
- uint16 RESERVED15;
- volatile uint16 CCR4;
- uint16 RESERVED16;
- volatile uint16 BDTR; /* Advanced control timers only */
- uint16 RESERVED17; /* Advanced control timers only */
- volatile uint16 DCR;
- uint16 RESERVED18;
- volatile uint16 DMAR;
- uint16 RESERVED19;
-} timer_port;
-
-/**
- * Timer device numbers. See STM32 reference manual, chapters 13-15.
- */
-/* several locations depend on TIMER1=0, etc.; don't change the
- enumerator values to start at 1. */
-typedef enum {
- TIMER1, /*< Advanced control timer TIM1 */
- TIMER2, /*< General purpose timer TIM2 */
- TIMER3, /*< General purpose timer TIM3 */
- TIMER4, /*< General purpose timer TIM4 */
-#if NR_TIMERS >= 8
- TIMER5, /*< General purpose timer TIM5; high density only */
- /* FIXME maple native: put timers 6 and 7 back in and make the
- corresponding changes to timers.c */
- /* TIMER6, /\*< Basic timer TIM6; high density only *\/ */
- /* TIMER7, /\*< Basic timer TIM7; high density only *\/ */
- TIMER8, /*< Advanced control timer TIM8; high density only */
-#endif
- TIMER_INVALID /* FIXME: this is starting to seem like a bad idea */
-} timer_dev_num;
-
-/* timer descriptor */
-struct timer_dev {
- timer_port *base;
- const uint8 rcc_dev_num;
- const uint8 nvic_dev_num;
- volatile voidFuncPtr handlers[4];
-};
-
-extern struct timer_dev timer_dev_table[];
-
-/**
- * Initializes timer with prescale as the clock divisor.
- *
- * @param timer_num Timer number.
- *
- * @param prescale value in the range 1--65535 to use as a prescaler
- * for timer counter increment frequency.
- *
- * @see timer_dev_num
- * @see timer_set_prescaler()
- * @see timer_set_mode()
- */
-void timer_init(timer_dev_num timer_num, uint16 prescale);
-
-/**
- * Quickly disable all timers. Calling this function is faster than,
- * e.g., calling timer_set_mode() for all available timers/channels.
- */
-void timer_disable_all(void);
-
-/**
- * Returns the timer's counter value. Due to function call overhead,
- * this value is likely to be inaccurate if the counter is running
- * with a low prescaler.
- *
- * @param timer_num the timer whose counter to return.
- *
- * @pre Timer has been initialized.
- */
-uint16 timer_get_count(timer_dev_num timer_num);
-
-/**
- * Sets the counter value for the given timer.
- *
- * @param timer_num the timer whose counter to set.
- *
- * @param value the new counter value.
- *
- * @pre Timer has been initialized.
- */
-void timer_set_count(timer_dev_num timer_num, uint16 value);
-
-/**
- * Stops the timer's counter from incrementing. Does not modify the
- * timer's mode or settings.
- *
- * @param timer_num the timer to pause.
- *
- * @see timer_resume()
- *
- * @pre Timer has been initialized.
- */
-void timer_pause(timer_dev_num timer_num);
-
-/**
- * Starts the counter for the given timer. Does not modify the
- * timer's mode or settings. The timer will begin counting on the
- * first rising clock cycle after it has been re-enabled using this
- * function.
- *
- * @param timer_num the timer to resume.
- *
- * @see timer_pause()
- *
- * @pre Timer has been initialized.
- */
-void timer_resume(timer_dev_num timer_num);
-
-/**
- * Returns the prescaler for the given timer.
- *
- * @param timer_num the timer whose prescaler to return.
- *
- * @see timer_set_prescaler()
- *
- * @pre Timer has been initialized.
- */
-uint16 timer_get_prescaler(timer_dev_num timer_num);
-
-/**
- * Sets the prescaler for the given timer. This value goes into the
- * PSC register, so it's 0-based (i.e., a prescale of 0 counts 1 tick
- * per clock cycle). This prescale does not take effect until the
- * next update event.
- *
- * @param timer_num the timer whose prescaler to set.
- *
- * @param prescale the new prescaler.
- *
- * @pre Timer has been initialized.
- */
-void timer_set_prescaler(timer_dev_num timer_num, uint16 prescale);
-
-/**
- * Gets the reload value for the timer.
- *
- * @see timer_set_reload()
- *
- * @pre Timer has been initialized.
- */
-uint16 timer_get_reload(timer_dev_num timer_num);
-
-/**
- * Sets the reload value for the timer.
- *
- * After this function returns, the timer's counter will reset to 0
- * after it has reached the value max_reload.
- *
- * @pre Timer has been initialized.
- */
-void timer_set_reload(timer_dev_num timer_num, uint16 max_reload);
-
-/* TODO: timer_get_mode */
-
-/**
- * Set the mode of an individual timer channel.
- *
- * @see timer_disable_all()
- * @see TimerMode
- * @see timer_dev_num
- * @pre Timer has been initialized.
- */
-void timer_set_mode(timer_dev_num timer_num, uint8 channel, TimerMode mode);
-
-/**
- * Get the compare value for the given timer channel.
- * @see timer_set_compare_value()
- * @see timer_dev_num
- * @pre Timer has been initialized.
- */
-uint16 timer_get_compare_value(timer_dev_num timer_num, uint8 channel);
-
-/**
- * Sets the compare value for a given timer channel. Useful for
- * scheduling when interrupt handlers will be called.
- *
- * @see timer_attach_interrupt()
- * @see timer_detach_interrupt()
- * @see timer_set_reload()
- * @see timer_dev_num
- * @pre Timer has been initialized.
- */
-void timer_set_compare_value(timer_dev_num timer_num, uint8 channel,
- uint16 value);
-
-/**
- * Detach the interrupt handler for the given timer channel, if any.
- * After this function returns, any handler attached to the given
- * channel will no longer be called.
- *
- * @see timer_attach_interrupt()
- * @pre Timer has been initialized.
- * @see timer_dev_num
- */
-void timer_detach_interrupt(timer_dev_num timer_num, uint8 channel);
-
-/**
- * Attach an interrupt handler for the given timer and channel. The
- * given ISR, handler, will be called whenever the timer's counter
- * reaches the compare value for the given timer and channel.
- *
- * @see timer_set_compare_value()
- * @see timer_detach_interrupt()
- * @see timer_set_mode()
- * @see timer_dev_num
- * @see voidFuncPtr
- * @pre Timer has been initialized.
- * @pre The channel's mode must be set to TIMER_OUTPUTCOMPARE, or the
- * interrupt handler will not get called.
- */
-void timer_attach_interrupt(timer_dev_num timer_num, uint8 channel,
- voidFuncPtr handler);
-
-/**
- * Programmatically generate an update event on the given timer. This
- * updates the prescaler, reloads the compare value (in upcounting
- * mode, etc.).
- *
- * @pre Timer has been initialized.
- */
-void timer_generate_update(timer_dev_num timer_num);
-
-/**
- * Turn on PWM with duty_cycle.
- *
- * @param ccr TIMERx_CHn_CCR, where x goes from 1 to NR_TIMERS,
- * and n goes from 1 to 4.
- *
- * @param duty_cycle: A number between 0 and
- * timer_get_compare_value(TIMERx, y), where x and y are as above.
- *
- * @pre Pin has been set to alternate function output.
- *
- * @pre Timer has been initialized.
- */
-static inline void timer_pwm_write_ccr(TimerCCR ccr, uint16 duty_cycle) {
- *ccr = duty_cycle;
-}
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-
-#endif
-
diff --git a/libmaple/usart.c b/libmaple/usart.c
index 44a5c92..fbd4d70 100644
--- a/libmaple/usart.c
+++ b/libmaple/usart.c
@@ -3,228 +3,246 @@
*
* 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
+ * 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 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.
+ * 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 usart.c
+ * @author Marti Bolivar <mbolivar@leaflabs.com,
+ * Perry Hung <perry@leaflabs.com>
* @brief USART control routines
*/
-#include "libmaple.h"
-#include "rcc.h"
-#include "nvic.h"
#include "usart.h"
-#define USART1_BASE 0x40013800
-#define USART2_BASE 0x40004400
-#define USART3_BASE 0x40004800
-#define UART4_BASE 0x40004C00 // High-density devices only (Maple Native)
-#define UART5_BASE 0x40005000 // High-density devices only (Maple Native)
-
-#define USART_UE BIT(13)
-#define USART_M BIT(12)
-#define USART_TE BIT(3)
-#define USART_RE BIT(2)
-#define USART_RXNEIE BIT(5) // read data register not empty interrupt enable
-#define USART_TC BIT(6)
-
-/* usart descriptor table */
-struct usart_dev usart_dev_table[] = {
- [USART1] = {
- .base = (usart_port*)USART1_BASE,
- .rcc_dev_num = RCC_USART1,
- .nvic_dev_num = NVIC_USART1
- },
- [USART2] = {
- .base = (usart_port*)USART2_BASE,
- .rcc_dev_num = RCC_USART2,
- .nvic_dev_num = NVIC_USART2
- },
- [USART3] = {
- .base = (usart_port*)USART3_BASE,
- .rcc_dev_num = RCC_USART3,
- .nvic_dev_num = NVIC_USART3
- },
-#if NR_USART >= 5
- /* TODO test */
- [UART4] = {
- .base = (usart_port*)UART4_BASE,
- .rcc_dev_num = RCC_UART4,
- .nvic_dev_num = NVIC_UART4
- },
- [UART5] = {
- .base = (usart_port*)UART5_BASE,
- .rcc_dev_num = RCC_UART5,
- .nvic_dev_num = NVIC_UART5
- },
-#endif
-};
-
/*
- * Usart interrupt handlers.
+ * Devices
*/
-static inline void usart_irq(int usart_num) {
-#ifdef USART_SAFE_INSERT
- /* Ignore old bytes if the user defines USART_SAFE_INSERT. */
- rb_safe_insert(&(usart_dev_table[usart_num].rb),
- (uint8)((usart_dev_table[usart_num].base)->DR));
-#else
- /* By default, push bytes around in the ring buffer. */
- rb_push_insert(&(usart_dev_table[usart_num].rb),
- (uint8)((usart_dev_table[usart_num].base)->DR));
+static ring_buffer usart1_rb;
+static usart_dev usart1 = {
+ .regs = USART1_BASE,
+ .rb = &usart1_rb,
+ .max_baud = 4500000UL,
+ .clk_id = RCC_USART1,
+ .irq_num = NVIC_USART1
+};
+usart_dev *USART1 = &usart1;
+
+static ring_buffer usart2_rb;
+static usart_dev usart2 = {
+ .regs = USART2_BASE,
+ .rb = &usart2_rb,
+ .max_baud = 2250000UL,
+ .clk_id = RCC_USART2,
+ .irq_num = NVIC_USART2
+};
+usart_dev *USART2 = &usart2;
+
+static ring_buffer usart3_rb;
+static usart_dev usart3 = {
+ .regs = USART3_BASE,
+ .rb = &usart3_rb,
+ .max_baud = 2250000UL,
+ .clk_id = RCC_USART3,
+ .irq_num = NVIC_USART3
+};
+usart_dev *USART3 = &usart3;
+
+#ifdef STM32_HIGH_DENSITY
+static ring_buffer uart4_rb;
+static usart_dev uart4 = {
+ .regs = UART4_BASE,
+ .rb = &uart4_rb,
+ .max_baud = 2250000UL,
+ .clk_id = RCC_UART4,
+ .irq_num = NVIC_UART4
+};
+usart_dev *UART4 = &uart4;
+
+static ring_buffer uart5_rb;
+static usart_dev uart5 = {
+ .regs = UART5_BASE,
+ .rb = &uart5_rb,
+ .max_baud = 2250000UL,
+ .clk_id = RCC_UART5,
+ .irq_num = NVIC_UART5
+};
+usart_dev *UART5 = &uart5;
#endif
-}
-
-/* TODO: Check the disassembly for the following functions to make
- sure GCC inlined properly. */
-
-void USART1_IRQHandler(void) {
- usart_irq(USART1);
-}
-
-void USART2_IRQHandler(void) {
- usart_irq(USART2);
-}
-void USART3_IRQHandler(void) {
- usart_irq(USART3);
-}
-
-#if NR_USART >= 5
-void UART4_IRQHandler(void) {
- usart_irq(UART4);
-}
-
-void UART5_IRQHandler(void) {
- usart_irq(UART5);
+/**
+ * @brief Initialize a serial port.
+ * @param dev Serial port to be initialized
+ */
+void usart_init(usart_dev *dev) {
+ rb_init(dev->rb, USART_RX_BUF_SIZE, dev->rx_buf);
+ rcc_clk_enable(dev->clk_id);
+ nvic_irq_enable(dev->irq_num);
}
-#endif
/**
- * @brief Enable a USART in single buffer transmission mode, multibuffer
- * receiver mode.
- * @param usart_num USART to be initialized
- * @param baud Baud rate to be set at
+ * @brief Configure a serial port's baud rate.
+ *
+ * @param dev Serial port to be configured
+ * @param clock_speed Clock speed, in megahertz.
+ * @param baud Baud rate for transmit/receive.
*/
-void usart_init(uint8 usart_num, uint32 baud) {
- ASSERT(usart_num <= NR_USART);
- usart_port *port;
- ring_buffer *ring_buf;
-
- uint32 clk_speed;
+void usart_set_baud_rate(usart_dev *dev, uint32 clock_speed, uint32 baud) {
uint32 integer_part;
uint32 fractional_part;
uint32 tmp;
- port = usart_dev_table[usart_num].base;
- rcc_clk_enable(usart_dev_table[usart_num].rcc_dev_num);
- nvic_irq_enable(usart_dev_table[usart_num].nvic_dev_num);
-
- /* usart1 is mad fast */
- clk_speed = (usart_num == USART1) ? 72000000UL : 36000000UL;
-
- /* Initialize rx ring buffer */
- rb_init(&usart_dev_table[usart_num].rb,
- sizeof (usart_dev_table[usart_num].rx_buf),
- usart_dev_table[usart_num].rx_buf);
-
- /* Set baud rate */
- integer_part = ((25 * clk_speed) / (4 * baud));
+ /* See ST RM0008 for the details on configuring the baud rate register */
+ integer_part = (25 * clock_speed) / (4 * baud);
tmp = (integer_part / 100) << 4;
-
fractional_part = integer_part - (100 * (tmp >> 4));
tmp |= (((fractional_part * 16) + 50) / 100) & ((uint8)0x0F);
- port->BRR = (uint16)tmp;
-
- port->CR1 = USART_TE | // transmitter enable
- USART_RE | // receiver enable
- USART_RXNEIE; // receive interrupt enable
-
-
- /* Enable the USART and set mode to 8n1 */
- port->CR1 |= USART_UE;
+ dev->regs->BRR = (uint16)tmp;
}
/**
- * @brief Turn off all USARTs.
+ * @brief Enable a serial port.
+ *
+ * USART is enabled in single buffer transmission mode, multibuffer
+ * receiver mode, 8n1.
+ *
+ * Serial port must have a baud rate configured to work properly.
+ *
+ * @param dev Serial port to enable.
+ * @see usart_set_baud_rate()
*/
-void usart_disable_all() {
- usart_disable(USART1);
- usart_disable(USART2);
- usart_disable(USART3);
-#if NR_USART >= 5
- usart_disable(UART4);
- usart_disable(UART5);
-#endif
+void usart_enable(usart_dev *dev) {
+ usart_reg_map *regs = dev->regs;
+ regs->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
+ regs->CR1 |= USART_CR1_UE;
}
/**
- * @brief Turn off a USART.
- * @param USART to be disabled
+ * @brief Turn off a serial port.
+ * @param dev Serial port to be disabled
*/
-void usart_disable(uint8 usart_num) {
- usart_port *port = usart_dev_table[usart_num].base;
+void usart_disable(usart_dev *dev) {
+ /* FIXME this misbehaves if you try to use PWM on TX afterwards */
+ usart_reg_map *regs = dev->regs;
- /* TC bit must be high before disabling the usart */
- while((port->CR1 & USART_UE) && !(port->SR & USART_TC))
+ /* TC bit must be high before disabling the USART */
+ while((regs->CR1 & USART_CR1_UE) && !(regs->SR & USART_SR_TC))
;
/* Disable UE */
- port->CR1 = 0;
+ regs->CR1 &= ~USART_CR1_UE;
/* Clean up buffer */
- usart_reset_rx(usart_num);
+ usart_reset_rx(dev);
}
-
/**
- * @brief Print a null terminated string to the specified USART
- *
- * @param usart_num usart to send on
- * @param str string to send
+ * @brief Call a function on each USART.
+ * @param fn Function to call.
*/
-void usart_putstr(uint8 usart_num, const char* str) {
- char ch;
+void usart_foreach(void (*fn)(usart_dev*)) {
+ fn(USART1);
+ fn(USART2);
+ fn(USART3);
+#ifdef STM32_HIGH_DENSITY
+ fn(UART4);
+ fn(UART5);
+#endif
+}
- while((ch = *(str++)) != '\0') {
- usart_putc(usart_num, ch);
+/**
+ * @brief Nonblocking USART transmit
+ * @param dev Serial port to transmit over
+ * @param buf Buffer to transmit
+ * @param len Maximum number of bytes to transmit
+ * @return Number of bytes transmitted
+ */
+uint32 usart_tx(usart_dev *dev, const uint8 *buf, uint32 len) {
+ usart_reg_map *regs = dev->regs;
+ uint32 txed = 0;
+ while ((regs->SR & USART_SR_TXE) && (txed < len)) {
+ regs->DR = buf[txed++];
}
+ return txed;
}
/**
- * @brief Print an unsigned integer to the specified usart
+ * @brief Transmit an unsigned integer to the specified serial port in
+ * decimal format.
*
- * @param usart_num usart to send on
- * @param val number to print
+ * This function blocks until the integer's digits have been
+ * completely transmitted.
+ *
+ * @param dev Serial port to send on
+ * @param val Number to print
*/
-void usart_putudec(uint8 usart_num, uint32 val) {
+void usart_putudec(usart_dev *dev, uint32 val) {
char digits[12];
- int i;
+ int i = 0;
- i = 0;
do {
digits[i++] = val % 10 + '0';
val /= 10;
} while (val > 0);
+
while (--i >= 0) {
- usart_putc(usart_num, digits[i]);
+ usart_putc(dev, digits[i]);
}
}
+
+/*
+ * Interrupt handlers.
+ */
+
+static inline void usart_irq(usart_dev *dev) {
+#ifdef USART_SAFE_INSERT
+ /* If the buffer is full and the user defines USART_SAFE_INSERT,
+ * ignore new bytes. */
+ rb_safe_insert(dev->rb, (uint8)dev->regs->DR);
+#else
+ /* By default, push bytes around in the ring buffer. */
+ rb_push_insert(dev->rb, (uint8)dev->regs->DR);
+#endif
+}
+
+void __irq_usart1(void) {
+ usart_irq(USART1);
+}
+
+void __irq_usart2(void) {
+ usart_irq(USART2);
+}
+
+void __irq_usart3(void) {
+ usart_irq(USART3);
+}
+
+#ifdef STM32_HIGH_DENSITY
+void __irq_uart4(void) {
+ usart_irq(UART4);
+}
+
+void __irq_uart5(void) {
+ usart_irq(UART5);
+}
+#endif
diff --git a/libmaple/usart.h b/libmaple/usart.h
index 0ca3f55..0743c53 100644
--- a/libmaple/usart.h
+++ b/libmaple/usart.h
@@ -3,119 +3,332 @@
*
* 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
+ * 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 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.
+ * 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 usart.h
+ * @author Marti Bolivar <mbolivar@leaflabs.com>,
+ * Perry Hung <perry@leaflabs.com>
* @brief USART definitions and prototypes
*/
#ifndef _USART_H_
#define _USART_H_
+#include "libmaple_types.h"
+#include "util.h"
+#include "rcc.h"
+#include "nvic.h"
#include "ring_buffer.h"
#ifdef __cplusplus
extern "C"{
#endif
-#define USART_TXE BIT(7)
-
-/* usart device numbers */
-enum {
- USART1,
- USART2,
- USART3,
- UART4,
- UART5,
-};
-
-/* peripheral register struct */
-typedef struct usart_port {
- volatile uint32 SR; // Status register
- volatile uint32 DR; // Data register
- volatile uint32 BRR; // Baud rate register
- volatile uint32 CR1; // Control register 1
- volatile uint32 CR2; // Control register 2
- volatile uint32 CR3; // Control register 3
- volatile uint32 GTPR; // Guard time and prescaler register
-} usart_port;
-
-/* usart descriptor */
-struct usart_dev {
- usart_port *base;
- ring_buffer rb;
- uint8 rx_buf[64];
- const uint8 rcc_dev_num;
- const uint8 nvic_dev_num;
-};
-
-extern struct usart_dev usart_dev_table[];
+/*
+ * Register maps and devices
+ */
+
+/** USART register map type */
+typedef struct usart_reg_map {
+ __io uint32 SR; /**< Status register */
+ __io uint32 DR; /**< Data register */
+ __io uint32 BRR; /**< Baud rate register */
+ __io uint32 CR1; /**< Control register 1 */
+ __io uint32 CR2; /**< Control register 2 */
+ __io uint32 CR3; /**< Control register 3 */
+ __io uint32 GTPR; /**< Guard time and prescaler register */
+} usart_reg_map;
+
+/** USART1 register map base pointer */
+#define USART1_BASE ((struct usart_reg_map*)0x40013800)
+/** USART2 register map base pointer */
+#define USART2_BASE ((struct usart_reg_map*)0x40004400)
+/** USART3 register map base pointer */
+#define USART3_BASE ((struct usart_reg_map*)0x40004800)
+#ifdef STM32_HIGH_DENSITY
+/** UART4 register map base pointer */
+#define UART4_BASE ((struct usart_reg_map*)0x40004C00)
+/** UART5 register map base pointer */
+#define UART5_BASE ((struct usart_reg_map*)0x40005000)
+#endif
+
+/*
+ * Register bit definitions
+ */
+
+/* Status register */
+
+#define USART_SR_CTS_BIT 9
+#define USART_SR_LBD_BIT 8
+#define USART_SR_TXE_BIT 7
+#define USART_SR_TC_BIT 6
+#define USART_SR_RXNE_BIT 5
+#define USART_SR_IDLE_BIT 4
+#define USART_SR_ORE_BIT 3
+#define USART_SR_NE_BIT 2
+#define USART_SR_FE_BIT 1
+#define USART_SR_PE_BIT 0
+
+#define USART_SR_CTS BIT(USART_SR_CTS_BIT)
+#define USART_SR_LBD BIT(USART_SR_LBD_BIT)
+#define USART_SR_TXE BIT(USART_SR_TXE_BIT)
+#define USART_SR_TC BIT(USART_SR_TC_BIT)
+#define USART_SR_RXNE BIT(USART_SR_RXNE_BIT)
+#define USART_SR_IDLE BIT(USART_SR_IDLE_BIT)
+#define USART_SR_ORE BIT(USART_SR_ORE_BIT)
+#define USART_SR_NE BIT(USART_SR_NE_BIT)
+#define USART_SR_FE BIT(USART_SR_FE_BIT)
+#define USART_SR_PE BIT(USART_SR_PE_BIT)
+
+/* Data register */
+
+#define USART_DR_DR 0xFF
+
+/* Baud rate register */
+
+#define USART_BRR_DIV_MANTISSA (0xFFF << 4)
+#define USART_BRR_DIV_FRACTION 0xF
+
+/* Control register 1 */
+
+#define USART_CR1_UE_BIT 13
+#define USART_CR1_M_BIT 12
+#define USART_CR1_WAKE_BIT 11
+#define USART_CR1_PCE_BIT 10
+#define USART_CR1_PS_BIT 9
+#define USART_CR1_PEIE_BIT 8
+#define USART_CR1_TXEIE_BIT 7
+#define USART_CR1_TCIE_BIT 6
+#define USART_CR1_RXNEIE_BIT 5
+#define USART_CR1_IDLEIE_BIT 4
+#define USART_CR1_TE_BIT 3
+#define USART_CR1_RE_BIT 2
+#define USART_CR1_RWU_BIT 1
+#define USART_CR1_SBK_BIT 0
+
+#define USART_CR1_UE BIT(USART_CR1_UE_BIT)
+#define USART_CR1_M BIT(USART_CR1_M_BIT)
+#define USART_CR1_WAKE BIT(USART_CR1_WAKE_BIT)
+#define USART_CR1_WAKE_IDLE (0 << USART_CR1_WAKE_BIT)
+#define USART_CR1_WAKE_ADDR (1 << USART_CR1_WAKE_BIT)
+#define USART_CR1_PCE BIT(USART_CR1_PCE_BIT)
+#define USART_CR1_PS BIT(USART_CR1_PS_BIT)
+#define USART_CR1_PS_EVEN (0 << USART_CR1_PS_BIT)
+#define USART_CR1_PS_ODD (1 << USART_CR1_PS_BIT)
+#define USART_CR1_PEIE BIT(USART_CR1_PEIE_BIT)
+#define USART_CR1_TXEIE BIT(USART_CR1_TXEIE_BIT)
+#define USART_CR1_TCIE BIT(USART_CR1_TCIE_BIT)
+#define USART_CR1_RXNEIE BIT(USART_CR1_RXNEIE_BIT)
+#define USART_CR1_IDLEIE BIT(USART_CR1_IDLEIE_BIT)
+#define USART_CR1_TE BIT(USART_CR1_TE_BIT)
+#define USART_CR1_RE BIT(USART_CR1_RE_BIT)
+#define USART_CR1_RWU BIT(USART_CR1_RWU_BIT)
+#define USART_CR1_RWU_ACTIVE (0 << USART_CR1_RWU_BIT)
+#define USART_CR1_RWU_MUTE (1 << USART_CR1_RWU_BIT)
+#define USART_CR1_SBK BIT(USART_CR1_SBK_BIT)
+
+/* Control register 2 */
+
+#define USART_CR2_LINEN_BIT 14
+#define USART_CR2_CLKEN_BIT 11
+#define USART_CR2_CPOL_BIT 10
+#define USART_CR2_CPHA_BIT 9
+#define USART_CR2_LBCL_BIT 8
+#define USART_CR2_LBDIE_BIT 6
+#define USART_CR2_LBDL_BIT 5
+
+#define USART_CR2_LINEN BIT(USART_CR2_LINEN_BIT)
+#define USART_CR2_STOP (0x3 << 12)
+#define USART_CR2_STOP_BITS_1 (0x0 << 12)
+/* Not on UART4, UART5 */
+#define USART_CR2_STOP_BITS_POINT_5 (0x1 << 12)
+/* Not on UART4, UART5 */
+#define USART_CR2_STOP_BITS_1_POINT_5 (0x3 << 12)
+#define USART_CR2_STOP_BITS_2 (0x2 << 12)
+#define USART_CR2_CLKEN BIT(USART_CR2_CLKEN_BIT)
+/* Not on UART4, UART5 */
+#define USART_CR2_CPOL BIT(USART_CR2_CPOL_BIT)
+#define USART_CR2_CPOL_LOW (0x0 << USART_CR2_CLKEN_BIT)
+#define USART_CR2_CPOL_HIGH (0x1 << USART_CR2_CLKEN_BIT)
+/* Not on UART4, UART5 */
+#define USART_CR2_CPHA BIT(USART_CR2_CPHA_BIT)
+#define USART_CR2_CPHA_FIRST (0x0 << USART_CR2_CPHA_BIT)
+#define USART_CR2_CPHA_SECOND (0x1 << USART_CR2_CPHA_BIT)
+/* Not on UART4, UART5 */
+#define USART_CR2_LBCL BIT(USART_CR2_LBCL_BIT)
+#define USART_CR2_LBDIE BIT(USART_CR2_LBDIE_BIT)
+#define USART_CR2_LBDL BIT(USART_CR2_LBDL_BIT)
+#define USART_CR2_LBDL_10_BIT (0 << USART_CR2_LBDL_BIT)
+#define USART_CR2_LBDL_11_BIT (1 << USART_CR2_LBDL_BIT)
+#define USART_CR2_ADD 0xF
+
+/* Control register 3 */
+
+#define USART_CR3_CTSIE_BIT 10
+#define USART_CR3_CTSE_BIT 9
+#define USART_CR3_RTSE_BIT 8
+#define USART_CR3_DMAT_BIT 7
+#define USART_CR3_DMAR_BIT 6
+#define USART_CR3_SCEN_BIT 5
+#define USART_CR3_NACK_BIT 4
+#define USART_CR3_HDSEL_BIT 3
+#define USART_CR3_IRLP_BIT 2
+#define USART_CR3_IREN_BIT 1
+#define USART_CR3_EIE_BIT 0
+
+/* Not on UART4, UART5 */
+#define USART_CR3_CTSIE BIT(USART_CR3_CTSIE_BIT)
+/* Not on UART4, UART5 */
+#define USART_CR3_CTSE BIT(USART_CR3_CTSE_BIT)
+/* Not on UART4, UART5 */
+#define USART_CR3_RTSE BIT(USART_CR3_RTSE_BIT)
+/* Not on UART5 */
+#define USART_CR3_DMAT BIT(USART_CR3_DMAT_BIT)
+/* Not on UART5 */
+#define USART_CR3_DMAR BIT(USART_CR3_DMAR_BIT)
+/* Not on UART4, UART5 */
+#define USART_CR3_SCEN BIT(USART_CR3_SCEN_BIT)
+/* Not on UART4, UART5 */
+#define USART_CR3_NACK BIT(USART_CR3_NACK_BIT)
+#define USART_CR3_HDSEL BIT(USART_CR3_HDSEL_BIT)
+#define USART_CR3_IRLP BIT(USART_CR3_IRLP_BIT)
+#define USART_CR3_IRLP_NORMAL (0 << USART_CR3_IRLP_BIT)
+#define USART_CR3_IRLP_LOW_POWER (1 << USART_CR3_IRLP_BIT)
+#define USART_CR3_IREN BIT(USART_CR3_IREN_BIT)
+#define USART_CR3_EIE BIT(USART_CR3_EIE_BIT)
+
+/* Guard time and prescaler register */
+
+/* Not on UART4, UART5 */
+#define USART_GTPR_GT (0xFF << 8)
+/* Not on UART4, UART5 */
+#define USART_GTPR_PSC 0xFF
+
+/*
+ * Devices
+ */
+
+#define USART_RX_BUF_SIZE 64
+
+/** USART device type */
+typedef struct usart_dev {
+ usart_reg_map *regs;
+ ring_buffer *rb;
+ uint32 max_baud;
+ uint8 rx_buf[USART_RX_BUF_SIZE];
+ rcc_clk_id clk_id;
+ nvic_irq_num irq_num;
+} usart_dev;
+
+/** USART1 device */
+extern usart_dev *USART1;
+/** USART2 device */
+extern usart_dev *USART2;
+/** USART3 device */
+extern usart_dev *USART3;
+#ifdef STM32_HIGH_DENSITY
+/** UART4 device */
+extern usart_dev *UART4;
+/** UART5 device */
+extern usart_dev *UART5;
+#endif
+
+void usart_init(usart_dev *dev);
+void usart_set_baud_rate(usart_dev *dev, uint32 clock_speed, uint32 baud);
+void usart_enable(usart_dev *dev);
+void usart_disable(usart_dev *dev);
+void usart_foreach(void (*fn)(usart_dev *dev));
+uint32 usart_tx(usart_dev *dev, const uint8 *buf, uint32 len);
+void usart_putudec(usart_dev *dev, uint32 val);
/**
- * @brief send one character on a usart
- * @param usart_num usart to send on
- * @param byte byte to send
+ * @brief Disable all serial ports.
*/
-static inline void usart_putc(uint8 usart_num, uint8 byte) {
- usart_port *port = usart_dev_table[usart_num].base;
+static inline void usart_disable_all(void) {
+ usart_foreach(usart_disable);
+}
- /* Wait for the buffer to empty */
- while ((port->SR & USART_TXE) == 0)
+/**
+ * @brief Transmit one character on a serial port.
+ *
+ * This function blocks until the character has been successfully
+ * transmitted.
+ *
+ * @param dev Serial port to send on.
+ * @param byte Byte to transmit.
+ */
+static inline void usart_putc(usart_dev* dev, uint8 byte) {
+ uint8 buf[] = {byte};
+ while (!usart_tx(dev, buf, 1))
;
+}
- port->DR = byte;
+/**
+ * @brief Transmit a character string on a serial port.
+ *
+ * This function blocks until str is completely transmitted.
+ *
+ * @param dev Serial port to send on
+ * @param str String to send
+ */
+static inline void usart_putstr(usart_dev *dev, const char* str) {
+ uint32 i = 0;
+ while (str[i] != '\0') {
+ usart_putc(dev, str[i++]);
+ }
}
/**
- * @brief read one character from a usart
- * @param usart_num usart to read from
+ * @brief Read one character from a serial port.
+ *
+ * It's not safe to call this function if the serial port has no data
+ * available.
+ *
+ * @param dev Serial port to read from
* @return byte read
+ * @see usart_data_available()
*/
-static inline uint8 usart_getc(uint8 usart_num) {
- return rb_remove(&usart_dev_table[usart_num].rb);
+static inline uint8 usart_getc(usart_dev *dev) {
+ return rb_remove(dev->rb);
}
/**
- * @brief return the amount of data available in the rx buffer
- * @param usart_num which usart to check
- * @return number of bytes in the rx buffer
+ * @brief Return the amount of data available in a serial port's RX buffer.
+ * @param dev Serial port to check
+ * @return Number of bytes in dev's RX buffer.
*/
-static inline uint32 usart_data_available(uint8 usart_num) {
- return rb_full_count(&usart_dev_table[usart_num].rb);
+static inline uint32 usart_data_available(usart_dev *dev) {
+ return rb_full_count(dev->rb);
}
/**
- * @brief removes the contents of the rx fifo
- * @param usart_num which usart to reset
+ * @brief Discard the contents of a serial port's RX buffer.
+ * @param dev Serial port whose buffer to empty.
*/
-static inline void usart_reset_rx(uint8 usart_num) {
- rb_reset(&usart_dev_table[usart_num].rb);
+static inline void usart_reset_rx(usart_dev *dev) {
+ rb_reset(dev->rb);
}
-void usart_init(uint8 usart_num, uint32 baud);
-void usart_disable(uint8 usart_num);
-void usart_disable_all();
-void usart_putstr(uint8 usart_num, const char*);
-void usart_putudec(uint8 usart_num, uint32 val);
-
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/libmaple/usb/descriptors.c b/libmaple/usb/descriptors.c
index 360e6dd..8dd9521 100644
--- a/libmaple/usb/descriptors.c
+++ b/libmaple/usb/descriptors.c
@@ -150,43 +150,50 @@ const USB_Descriptor_Config usbVcomDescriptor_Config = {
// }
};
-/*
- String Identifiers:
+/*****************************************************************************
+ *****************************************************************************
+ ***
+ *** FIXME FIXME FIXME NOT THE RIGHT THING! MOVE ALL THIS INTO TO WIRISH!
+ ***
+ *****************************************************************************
+ *****************************************************************************/
- we may choose to specify any or none of the following string
- identifiers:
+const uint8 usbVcomDescriptor_LangID[USB_DESCRIPTOR_STRING_LEN(1)] = {
+ USB_DESCRIPTOR_STRING_LEN(1),
+ USB_DESCRIPTOR_TYPE_STRING,
+ 0x09,
+ 0x04
+};
- iManufacturer: LeafLabs
- iProduct: Maple R3
- iSerialNumber: NONE
- iConfiguration: NONE
- iInterface(CCI): NONE
- iInterface(DCI): NONE
+const uint8 usbVcomDescriptor_iManufacturer[USB_DESCRIPTOR_STRING_LEN(8)] = {
+ USB_DESCRIPTOR_STRING_LEN(8),
+ USB_DESCRIPTOR_TYPE_STRING,
+ 'L', 0, 'e', 0, 'a', 0, 'f', 0,
+ 'L', 0, 'a', 0, 'b', 0, 's', 0
+};
- additionally we must provide the unicode language identifier,
- which is 0x0409 for US English
-*/
+/*
+ String Identifiers:
-const uint8 usbVcomDescriptor_LangID[USB_DESCRIPTOR_STRING_LEN(1)] =
-{
- USB_DESCRIPTOR_STRING_LEN(1),
- USB_DESCRIPTOR_TYPE_STRING,
- 0x09,
- 0x04
-};
+ we may choose to specify any or none of the following string
+ identifiers:
-const uint8 usbVcomDescriptor_iManufacturer[USB_DESCRIPTOR_STRING_LEN(8)] =
-{
- USB_DESCRIPTOR_STRING_LEN(8),
- USB_DESCRIPTOR_TYPE_STRING,
- 'L', 0, 'e', 0, 'a', 0, 'f', 0,
- 'L', 0, 'a', 0, 'b', 0, 's', 0
-};
+ iManufacturer: LeafLabs
+ iProduct: Maple R3
+ iSerialNumber: NONE
+ iConfiguration: NONE
+ iInterface(CCI): NONE
+ iInterface(DCI): NONE
-const uint8 usbVcomDescriptor_iProduct[USB_DESCRIPTOR_STRING_LEN(8)] =
-{
- USB_DESCRIPTOR_STRING_LEN(8),
- USB_DESCRIPTOR_TYPE_STRING,
- 'M', 0, 'a', 0, 'p', 0, 'l', 0,
- 'e', 0, ' ', 0, 'R', 0, '3', 0
+ additionally we must provide the unicode language identifier,
+ which is 0x0409 for US English
+*/
+const uint8 usbVcomDescriptor_iProduct[USB_DESCRIPTOR_STRING_LEN(8)] = {
+ USB_DESCRIPTOR_STRING_LEN(8),
+ USB_DESCRIPTOR_TYPE_STRING,
+ 'M', 0, 'a', 0, 'p', 0, 'l', 0,
+ 'e', 0, ' ', 0, ' ', 0, ' ', 0
};
+
+/*****************************************************************************
+ *****************************************************************************/
diff --git a/libmaple/usb/descriptors.h b/libmaple/usb/descriptors.h
index 6f7d08b..460a88c 100644
--- a/libmaple/usb/descriptors.h
+++ b/libmaple/usb/descriptors.h
@@ -61,7 +61,7 @@ extern "C" {
uint16 bString[len]; \
}
-#define CDC_FUCNTIONAL_DESCRIPTOR(DataSize) \
+#define CDC_FUNCTIONAL_DESCRIPTOR(DataSize) \
struct \
{ \
uint8 bLength; \
diff --git a/libmaple/usb/usb.c b/libmaple/usb/usb.c
index d875785..b34c4b6 100644
--- a/libmaple/usb/usb.c
+++ b/libmaple/usb/usb.c
@@ -35,6 +35,7 @@
#include "usb_lib.h"
#include "gpio.h"
#include "usb_hardware.h"
+#include "delay.h"
#include "usb_config.h"
#include "usb_callbacks.h"
@@ -44,7 +45,7 @@
volatile uint32 bDeviceState = UNCONNECTED;
volatile uint16 wIstr = 0;
-volatile bIntPackSOF = 0;
+volatile uint32 bIntPackSOF = 0;
DEVICE Device_Table =
{NUM_ENDPTS,
@@ -99,15 +100,13 @@ struct {
} ResumeS;
void setupUSB (void) {
- gpio_set_mode(USB_DISC_BANK,
- USB_DISC_PIN,
- GPIO_MODE_OUTPUT_PP);
+ gpio_set_mode(USB_DISC_DEV, USB_DISC_PIN, GPIO_OUTPUT_PP);
/* setup the apb1 clock for USB */
pRCC->APB1ENR |= 0x00800000;
/* initialize the usb application */
- gpio_write_bit(USB_DISC_BANK, USB_DISC_PIN, 0); // presents us to the host
+ gpio_write_bit(USB_DISC_DEV, USB_DISC_PIN, 0); // presents us to the host
USB_Init(); // low level init routine provided by the ST library
}
@@ -115,7 +114,7 @@ void disableUSB (void) {
// These are just guesses about how to do this
// TODO: real disable function
usbDsbISR();
- gpio_write_bit(USB_DISC_BANK,USB_DISC_PIN,1);
+ gpio_write_bit(USB_DISC_DEV, USB_DISC_PIN, 1);
}
void usbSuspend(void) {
@@ -241,7 +240,7 @@ void usbDsbISR(void) {
}
/* overloaded ISR routine, this is the main usb ISR */
-void usb_lpIRQHandler(void) {
+void __irq_usb_lp_can_rx0(void) {
wIstr = _GetISTR();
/* go nuts with the preproc switches since this is an ISTR and must be FAST */
@@ -321,7 +320,7 @@ if (wIstr & ISTR_CTR & wInterrupt_Mask) {
}
void usbWaitReset(void) {
- delay(RESET_DELAY);
+ delay_us(RESET_DELAY);
systemHardReset();
}
@@ -344,23 +343,21 @@ void usbBlockingSendByte(char ch) {
countTx = 1;
while (countTx);
}
-uint32 usbSendBytes(uint8* sendBuf, uint32 len) {
- /* any checks on connection (via dtr/rts) done upstream in wirish or
- by user */
- /* last xmit hasnt finished, abort */
+uint32 usbSendBytes(const uint8* sendBuf, uint32 len) {
+ /* Last transmission hasn't finished, abort */
if (countTx) {
return 0;
}
// We can only put VCOM_TX_EPSIZE bytes in the buffer
- if(len > VCOM_TX_EPSIZE/2) {
- len = VCOM_TX_EPSIZE/2;
+ if (len > VCOM_TX_EPSIZE / 2) {
+ len = VCOM_TX_EPSIZE / 2;
}
// Try to load some bytes if we can
if (len) {
- UserToPMABufferCopy(sendBuf,VCOM_TX_ADDR, len);
+ UserToPMABufferCopy(sendBuf, VCOM_TX_ADDR, len);
_SetEPTxCount(VCOM_TX_ENDP, len);
countTx += len;
_SetEPTxValid(VCOM_TX_ENDP);
diff --git a/libmaple/usb/usb.h b/libmaple/usb/usb.h
index 0ed02e5..c724c54 100644
--- a/libmaple/usb/usb.h
+++ b/libmaple/usb/usb.h
@@ -69,12 +69,12 @@ void usbDsbISR(void);
void usbEnbISR(void);
/* overloaded ISR routine, this is the main usb ISR */
-void usb_lpIRQHandler(void);
+void __irq_usb_lp_can_rx0(void);
void usbWaitReset(void);
/* blocking functions for send/receive */
void usbBlockingSendByte(char ch);
-uint32 usbSendBytes(uint8* sendBuf,uint32 len);
+uint32 usbSendBytes(const uint8* sendBuf,uint32 len);
uint32 usbBytesAvailable(void);
uint32 usbReceiveBytes(uint8* recvBuf, uint32 len);
uint8 usbGetDTR(void);
diff --git a/libmaple/usb/usb_callbacks.c b/libmaple/usb/usb_callbacks.c
index ccb0fdd..8184537 100644
--- a/libmaple/usb/usb_callbacks.c
+++ b/libmaple/usb/usb_callbacks.c
@@ -129,7 +129,7 @@ u8* vcomGetSetLineCoding(uint16 length) {
return (uint8*)&line_coding;
}
-vcomSetLineState(void) {
+void vcomSetLineState(void) {
}
void usbInit(void) {
diff --git a/libmaple/usb/usb_config.h b/libmaple/usb/usb_config.h
index e5f3979..23b49ee 100644
--- a/libmaple/usb/usb_config.h
+++ b/libmaple/usb/usb_config.h
@@ -4,6 +4,68 @@
#define __USB_CONFIG_H
#include "usb_lib.h"
+#include "gpio.h"
+
+/******************************************************************************
+ ******************************************************************************
+ ***
+ *** HACK ALERT
+ ***
+ *** FIXME FIXME FIXME FIXME
+ ***
+ *** A bunch of board-specific #defines that are only used by the
+ *** USB routines got put into libmaple.h for what appear to be
+ *** historical reasons. I'm [mbolivar] putting them in here for
+ *** now, so that we can treat the usb/ directory as a black box,
+ *** freeing the rest of libmaple/ to be implemented as a
+ *** general-purpose STM32 library. All of this REALLY needs to get
+ *** moved into wirish when we get a chance to redo the USB stack.
+ ***
+ ******************************************************************************
+ *****************************************************************************/
+
+#define VCOM_ID_VENDOR 0x1EAF
+#define RESET_DELAY (100000)
+#define USB_CONFIG_MAX_POWER (100 >> 1)
+
+#if defined(BOARD_maple) || defined(BOARD_maple_RET6)
+
+ /* USB Identifier numbers */
+ #define VCOM_ID_PRODUCT 0x0004
+ #define USB_DISC_DEV GPIOC
+ #define USB_DISC_PIN 12
+
+#elif defined(BOARD_maple_mini)
+
+ #define VCOM_ID_PRODUCT 0x0004
+ #define USB_DISC_DEV GPIOB
+ #define USB_DISC_PIN 9
+
+#elif defined(BOARD_maple_native)
+
+ #define VCOM_ID_PRODUCT 0x0004
+ #define USB_DISC_DEV GPIOB
+ #define USB_DISC_PIN 8
+
+#else
+
+#error ("Sorry! the USB stack relies on LeafLabs board-specific " \
+ "configuration right now. If you want, you can pretend you're one " \
+ "of our boards; i.e., #define BOARD_maple, BOARD_maple_mini, or " \
+ "BOARD_maple_native according to what matches your MCU best. " \
+ "You should also take a look at libmaple/usb/descriptors.c; we make " \
+ "some assumptions there that you probably won't like.")
+
+#endif
+
+/******************************************************************************
+ ******************************************************************************
+ ***
+ *** END HACK
+ ***
+ ******************************************************************************
+ *****************************************************************************/
+
/* choose addresses to give endpoints the max 64 byte buffers */
#define USB_BTABLE_ADDRESS 0x00
@@ -33,14 +95,15 @@
#define NUM_ENDPTS 0x04
/* handle all usb interrupts */
-#define ISR_MSK ( CNTR_CTRM | \
- CNTR_WKUPM | \
- CNTR_SUSPM | \
- CNTR_ERRM | \
- CNTR_SOFM | \
- CNTR_ESOFM | \
- CNTR_RESETM )
+#define ISR_MSK (CNTR_CTRM | \
+ CNTR_WKUPM | \
+ CNTR_SUSPM | \
+ CNTR_ERRM | \
+ CNTR_SOFM | \
+ CNTR_ESOFM | \
+ CNTR_RESETM)
#define F_SUSPEND_ENABLED 1
+
#endif
diff --git a/libmaple/usb/usb_hardware.c b/libmaple/usb/usb_hardware.c
index 505dcf1..9a7d12c 100644
--- a/libmaple/usb/usb_hardware.c
+++ b/libmaple/usb/usb_hardware.c
@@ -33,47 +33,6 @@
#include "usb_hardware.h"
-void setPin(u32 bank, u8 pin) {
- u32 pinMask = 0x1 << (pin);
- SET_REG(GPIO_BSRR(bank),pinMask);
-}
-
-void resetPin(u32 bank, u8 pin) {
- u32 pinMask = 0x1 << (16+pin);
- SET_REG(GPIO_BSRR(bank),pinMask);
-}
-
-void systemReset(void) {
- SET_REG(RCC_CR, GET_REG(RCC_CR) | 0x00000001);
- SET_REG(RCC_CFGR, GET_REG(RCC_CFGR) & 0xF8FF0000);
- SET_REG(RCC_CR, GET_REG(RCC_CR) & 0xFEF6FFFF);
- SET_REG(RCC_CR, GET_REG(RCC_CR) & 0xFFFBFFFF);
- SET_REG(RCC_CFGR, GET_REG(RCC_CFGR) & 0xFF80FFFF);
-
- SET_REG(RCC_CIR, 0x00000000); // disable all RCC interrupts
-}
-
-void setupCLK (void) {
- /* enable HSE */
- SET_REG(RCC_CR,GET_REG(RCC_CR) | 0x00010001);
- /* for it to come on */
- while ((GET_REG(RCC_CR) & 0x00020000) == 0);
-
- /* Configure PLL */
- /* pll=72Mhz,APB1=36Mhz,AHB=72Mhz */
- SET_REG(RCC_CFGR,GET_REG(RCC_CFGR) | 0x001D0400);
- /* enable the pll */
- SET_REG(RCC_CR,GET_REG(RCC_CR) | 0x01000000);
- /* wait for it to come on */
- while ((GET_REG(RCC_CR) & 0x03000000) == 0);
-
- /* Set SYSCLK as PLL */
- SET_REG(RCC_CFGR,GET_REG(RCC_CFGR) | 0x00000002);
- /* wait for it to come on */
- while ((GET_REG(RCC_CFGR) & 0x00000008) == 0);
-}
-
-
void nvicInit(NVIC_InitTypeDef* NVIC_InitStruct) {
u32 tmppriority = 0x00;
u32 tmpreg = 0x00;
diff --git a/libmaple/usb/usb_hardware.h b/libmaple/usb/usb_hardware.h
index e4a26b4..cfead1a 100644
--- a/libmaple/usb/usb_hardware.h
+++ b/libmaple/usb/usb_hardware.h
@@ -22,34 +22,20 @@
* THE SOFTWARE.
* ****************************************************************************/
-#ifndef __HARDWARE_H
-#define __HARDWARE_H
-
+#include "rcc.h"
#include "usb_type.h"
+#ifndef _USB_HARDWARE_H_
+#define _USB_HARDWARE_H_
+
/* macro'd register and peripheral definitions */
#define EXC_RETURN 0xFFFFFFF9
#define DEFAULT_CPSR 0x61000000
-#define RCC ((u32)0x40021000)
#define FLASH ((u32)0x40022000)
-#define GPIOA ((u32)0x40010800)
-#define GPIOC ((u32)0x40011000)
#define USB_PACKET_BUFFER ((u32)0x40006000)
-#define RCC_CR RCC
-#define RCC_CFGR RCC + 0x04
-#define RCC_CIR RCC + 0x08
-#define RCC_AHBENR RCC + 0x14
-#define RCC_APB2ENR RCC + 0x18
-#define RCC_APB1ENR RCC + 0x16
-
-#define GPIO_CRL(port) port
-#define GPIO_CRH(port) port+0x04
-#define GPIO_ODR(port) port+0x0c
-#define GPIO_BSRR(port) port+0x10
-
#define SCS_BASE ((u32)0xE000E000)
#define NVIC_BASE (SCS_BASE + 0x0100)
#define SCB_BASE (SCS_BASE + 0x0D00)
@@ -74,9 +60,8 @@
#define __PSC(__TIMCLK, __PERIOD) (((__VAL(__TIMCLK, __PERIOD)+49999UL)/50000UL) - 1)
#define __ARR(__TIMCLK, __PERIOD) ((__VAL(__TIMCLK, __PERIOD)/(__PSC(__TIMCLK, __PERIOD)+1)) - 1)
-/* todo, wrap in do whiles for the natural ; */
-#define SET_REG(addr,val) *(vu32*)(addr)=val
-#define GET_REG(addr) *(vu32*)(addr)
+#define SET_REG(addr,val) do { *(vu32*)(addr)=val; } while (0)
+#define GET_REG(addr) do { *(vu32*)(addr); } while (0)
#if defined(__cplusplus)
extern "C" {
@@ -85,7 +70,7 @@ extern "C" {
/* todo: there must be some major misunderstanding in how we access regs. The direct access approach (GET_REG)
causes the usb init to fail upon trying to activate RCC_APB1 |= 0x00800000. However, using the struct approach
from ST, it works fine...temporarily switching to that approach */
-typedef struct
+typedef struct
{
vu32 CR;
vu32 CFGR;
@@ -98,7 +83,7 @@ typedef struct
vu32 BDCR;
vu32 CSR;
} RCC_RegStruct;
-#define pRCC ((RCC_RegStruct *) RCC)
+#define pRCC ((RCC_RegStruct *) RCC_BASE)
typedef struct {
vu32 ISER[2];
@@ -139,12 +124,7 @@ typedef struct {
} SCB_TypeDef;
-void setPin (u32 bank, u8 pin);
-void resetPin (u32 bank, u8 pin);
-
void systemHardReset(void);
-void systemReset (void);
-void setupCLK (void);
void nvicInit (NVIC_InitTypeDef*);
void nvicDisableInterrupts(void);
diff --git a/libmaple/usb/usb_lib/usb_mem.c b/libmaple/usb/usb_lib/usb_mem.c
index ee698c5..90ffc62 100644
--- a/libmaple/usb/usb_lib/usb_mem.c
+++ b/libmaple/usb/usb_lib/usb_mem.c
@@ -30,9 +30,9 @@
* - wPMABufAddr: address into PMA.
* - wNBytes: no. of bytes to be copied.
* Output : None.
-* Return : None .
+* Return : None .
*******************************************************************************/
-void UserToPMABufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes)
+void UserToPMABufferCopy(const u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes)
{
u32 n = (wNBytes + 1) >> 1; /* n = (wNBytes + 1) / 2 */
u32 i, temp1, temp2;
diff --git a/libmaple/usb/usb_lib/usb_mem.h b/libmaple/usb/usb_lib/usb_mem.h
index a3d7927..60ff9f1 100644
--- a/libmaple/usb/usb_lib/usb_mem.h
+++ b/libmaple/usb/usb_lib/usb_mem.h
@@ -26,7 +26,7 @@
extern "C" {
#endif
-void UserToPMABufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes);
+void UserToPMABufferCopy(const u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes);
void PMAToUserBufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes);
#if defined(__cplusplus)
diff --git a/libmaple/util.c b/libmaple/util.c
index 135f005..4a234ae 100644
--- a/libmaple/util.c
+++ b/libmaple/util.c
@@ -24,7 +24,7 @@
/**
* @brief Utility procedures for debugging, mostly an error LED fade
- * and messages dumped over a uart for failed asserts.
+ * and messages dumped over a UART for failed asserts.
*/
#include "libmaple.h"
@@ -32,60 +32,92 @@
#include "gpio.h"
#include "nvic.h"
#include "adc.h"
-#include "timers.h"
-
-/* Error assert + fade */
-void _fail(const char* file, int line, const char* exp) {
- int32 slope = 1;
- uint32 CC = 0x0000;
- uint32 TOP_CNT = 0x02FF;
- uint32 i = 0;
+#include "timer.h"
+
+/* Failed ASSERT()s send out a message using this USART config. */
+#ifndef ERROR_USART
+#define ERROR_USART USART2
+#define ERROR_USART_CLK_SPEED 72000000UL
+#define ERROR_USART_BAUD 9600
+#define ERROR_TX_PORT GPIOA
+#define ERROR_TX_PIN 2
+#endif
+
+/* If you define ERROR_LED_PORT and ERROR_LED_PIN, then a failed
+ * ASSERT() will also throb() an LED connected to that port and pin.
+ */
+#if defined(ERROR_LED_PORT) && defined(ERROR_LED_PIN)
+#define HAVE_ERROR_LED
+#endif
- /* Turn off interrupts */
+/**
+ * @brief Disables all peripheral interrupts except USB and fades the
+ * error LED.
+ */
+/* (Called from exc.S with global interrupts disabled.) */
+void __error(void) {
+ /* Turn off peripheral interrupts */
nvic_irq_disable_all();
- /* Turn off timers */
+ /* Turn off timers */
timer_disable_all();
/* Turn off ADC */
- adc_disable();
+ adc_disable_all();
- /* Turn off all usarts */
+ /* Turn off all USARTs */
usart_disable_all();
- /* Initialize the error usart */
- gpio_set_mode(ERROR_TX_PORT, ERROR_TX_PIN, GPIO_MODE_AF_OUTPUT_PP);
- usart_init(ERROR_USART_NUM, ERROR_USART_BAUD);
+ /* Turn the USB interrupt back on so the bootloader keeps on functioning */
+ nvic_irq_enable(NVIC_USB_HP_CAN_TX);
+ nvic_irq_enable(NVIC_USB_LP_CAN_RX0);
- /* Print failed assert message */
- usart_putstr(ERROR_USART_NUM, "ERROR: FAILED ASSERT(");
- usart_putstr(ERROR_USART_NUM, exp);
- usart_putstr(ERROR_USART_NUM, "): ");
- usart_putstr(ERROR_USART_NUM, file);
- usart_putstr(ERROR_USART_NUM, ": ");
- usart_putudec(ERROR_USART_NUM, line);
- usart_putc(ERROR_USART_NUM, '\n');
- usart_putc(ERROR_USART_NUM, '\r');
-
- /* Turn on the error LED */
- gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_MODE_OUTPUT_PP);
+ /* Reenable global interrupts */
+ nvic_globalirq_enable();
+ throb();
+}
- /* Turn the USB interrupt back on so the bootloader keeps on functioning */
- nvic_irq_enable(NVIC_INT_USBHP);
- nvic_irq_enable(NVIC_INT_USBLP);
+/**
+ * @brief Print an error message on a UART upon a failed assertion
+ * and throb the error LED, if there is one defined.
+ * @param file Source file of failed assertion
+ * @param line Source line of failed assertion
+ * @param exp String representation of failed assertion
+ * @sideeffect Turns of all peripheral interrupts except USB.
+ */
+void _fail(const char* file, int line, const char* exp) {
+ /* Initialize the error USART */
+ gpio_set_mode(ERROR_TX_PORT, ERROR_TX_PIN, GPIO_AF_OUTPUT_PP);
+ usart_init(ERROR_USART);
+ usart_set_baud_rate(ERROR_USART, ERROR_USART_CLK_SPEED, ERROR_USART_BAUD);
+
+ /* Print failed assert message */
+ usart_putstr(ERROR_USART, "ERROR: FAILED ASSERT(");
+ usart_putstr(ERROR_USART, exp);
+ usart_putstr(ERROR_USART, "): ");
+ usart_putstr(ERROR_USART, file);
+ usart_putstr(ERROR_USART, ": ");
+ usart_putudec(ERROR_USART, line);
+ usart_putc(ERROR_USART, '\n');
+ usart_putc(ERROR_USART, '\r');
/* Error fade */
- throb();
+ __error();
}
+/**
+ * @brief Fades the error LED on and off
+ * @sideeffect Sets output push-pull on ERROR_LED_PIN.
+ */
void throb(void) {
+#ifdef HAVE_ERROR_LED
int32 slope = 1;
uint32 CC = 0x0000;
uint32 TOP_CNT = 0x0200;
uint32 i = 0;
- gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_MODE_OUTPUT_PP);
- /* Error fade */
+ gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_OUTPUT_PP);
+ /* Error fade. */
while (1) {
if (CC == TOP_CNT) {
slope = -1;
@@ -105,5 +137,9 @@ void throb(void) {
}
i++;
}
+#else
+ /* No error LED is defined; do nothing. */
+ while (1)
+ ;
+#endif
}
-
diff --git a/libmaple/util.h b/libmaple/util.h
index 64782d9..f54f3fd 100644
--- a/libmaple/util.h
+++ b/libmaple/util.h
@@ -23,59 +23,48 @@
*****************************************************************************/
/**
- * @file util.h
- *
- * @brief Various macros and utility procedures.
+ * @file util.h
+ * @brief Miscellaneous utility macros and procedures.
*/
-/* Generally "useful" utility procedures */
+#include "libmaple_types.h"
+
#ifndef _UTIL_H_
#define _UTIL_H_
-#include "libmaple.h"
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/*
+ * Bit manipulation
+ */
#define BIT(shift) (1UL << (shift))
#define BIT_MASK_SHIFT(mask, shift) ((mask) << (shift))
-
-/* Return bits m to n of x */
+/** Bits m to n of x */
#define GET_BITS(x, m, n) ((((uint32)x) << (31 - (n))) >> ((31 - (n)) + (m)))
-
-/* Bit-banding macros */
-/* 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 REG_SET(reg, val) (*(volatile uint32*)(reg) = (val))
-#define REG_SET_BIT(reg, bit) (*(volatile uint32*)(reg) |= BIT(bit))
-#define REG_CLEAR_BIT(reg, bit) (*(volatile uint32*)(reg) &= ~BIT(bit))
-#define REG_SET_MASK(reg, mask) (*(volatile uint32*)(reg) |= (uint32)(mask))
-#define REG_CLEAR_MASK(reg, mask) (*(volatile uint32*)(reg) &= (uint32)~(mask))
-
-#define REG_GET(reg) (*(volatile uint32*)(reg))
-
-#define __set_bits(addr, mask) (*(volatile uint32*)(addr) |= (uint32)(mask))
-#define __clear_bits(addr, mask) (*(volatile uint32*)(addr) &= (uint32)~(mask))
-#define __get_bits(addr, mask) (*(volatile uint32*)(addr) & (uint32)(mask))
-
-#define __read(reg) (*(volatile uint32*)(reg))
-#define __write(reg, value) (*(volatile uint32*)(reg) = (value))
-
#define IS_POWER_OF_TWO(v) (v && !(v & (v - 1)))
-#ifdef __cplusplus
-extern "C"{
-#endif
+/*
+ * Failure routines
+ */
+void __error(void);
void _fail(const char*, int, const char*);
void throb(void);
-#ifdef __cplusplus
-} // extern "C"
-#endif
+/*
+ * Asserts and debug levels
+ */
-/* Asserts for sanity checks, redefine DEBUG_LEVEL in libmaple.h to
- * compile out these checks */
+#define DEBUG_NONE 0
+#define DEBUG_FAULT 1
+#define DEBUG_ALL 2
+
+#ifndef DEBUG_LEVEL
+#define DEBUG_LEVEL DEBUG_ALL
+#endif
#if DEBUG_LEVEL >= DEBUG_ALL
#define ASSERT(exp) \
@@ -83,7 +72,6 @@ void throb(void);
} else { \
_fail(__FILE__, __LINE__, #exp); \
}
-
#else
#define ASSERT(exp) (void)((0))
#endif
@@ -94,10 +82,12 @@ void throb(void);
} else { \
_fail(__FILE__, __LINE__, #exp); \
}
-
#else
#define ASSERT_FAULT(exp) (void)((0))
#endif
+#ifdef __cplusplus
+} // extern "C"
#endif
+#endif