diff options
Diffstat (limited to 'libmaple')
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 = ®s->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 |