diff options
author | Marti Bolivar <mbolivar@leaflabs.com> | 2011-03-22 16:59:29 -0400 |
---|---|---|
committer | Marti Bolivar <mbolivar@leaflabs.com> | 2011-03-24 07:25:14 -0400 |
commit | 61db54f52f32e63c895d775982fbcdcb67f2dde6 (patch) | |
tree | b0513c712b5888ab0a4e6613fdee3db606b61aaf | |
parent | 6bc8cb7c1181e8005019e4ce1f2bea956c44e044 (diff) | |
download | librambutan-61db54f52f32e63c895d775982fbcdcb67f2dde6.tar.gz librambutan-61db54f52f32e63c895d775982fbcdcb67f2dde6.zip |
Initial timer refactor.
Basic PWM works. Had some problems in testing that might be due to
USART bugs.
HardwareTimer has been removed from the build for now; I will
re-implement it in terms of the new libmaple API, but consider it
deprecated. Let's come up with something better.
Servo is implemented in terms of HardwareTimer, so it also has been
temporarily removed from the build.
pwmWrite() likely got a little bit less inefficient due to
indirection, but the PIN_MAPs shrank by a pointer per PinMapping.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | examples/test-session.cpp | 19 | ||||
-rw-r--r-- | examples/test-timers.cpp | 372 | ||||
-rw-r--r-- | libmaple/bitband.h | 5 | ||||
-rw-r--r-- | libmaple/delay.h | 2 | ||||
-rw-r--r-- | libmaple/nvic.h | 101 | ||||
-rw-r--r-- | libmaple/rules.mk | 2 | ||||
-rw-r--r-- | libmaple/spi.h | 3 | ||||
-rw-r--r-- | libmaple/timer.c | 449 | ||||
-rw-r--r-- | libmaple/timer.h | 1010 | ||||
-rw-r--r-- | libmaple/timers.c | 526 | ||||
-rw-r--r-- | libmaple/timers.h | 435 | ||||
-rw-r--r-- | libmaple/util.c | 2 | ||||
-rw-r--r-- | libraries/Servo/Servo.h | 1 | ||||
-rw-r--r-- | notes/portable.txt | 94 | ||||
-rw-r--r-- | notes/usb.txt | 4 | ||||
-rw-r--r-- | notes/vga.txt | 9 | ||||
-rw-r--r-- | wirish/boards.cpp | 646 | ||||
-rw-r--r-- | wirish/boards.h | 9 | ||||
-rw-r--r-- | wirish/comm/HardwareSPI.cpp | 4 | ||||
-rw-r--r-- | wirish/comm/HardwareSerial.cpp | 20 | ||||
-rw-r--r-- | wirish/comm/HardwareSerial.h | 12 | ||||
-rw-r--r-- | wirish/pwm.cpp | 22 | ||||
-rw-r--r-- | wirish/rules.mk | 1 | ||||
-rw-r--r-- | wirish/wirish.cpp | 122 | ||||
-rw-r--r-- | wirish/wirish.h | 3 | ||||
-rw-r--r-- | wirish/wirish_digital.cpp | 6 |
27 files changed, 2095 insertions, 1786 deletions
@@ -97,7 +97,7 @@ endif LIBMAPLE_MODULES := $(SRCROOT)/libmaple LIBMAPLE_MODULES += $(SRCROOT)/wirish # Official libraries: -LIBMAPLE_MODULES += $(SRCROOT)/libraries/Servo +# LIBMAPLE_MODULES += $(SRCROOT)/libraries/Servo LIBMAPLE_MODULES += $(SRCROOT)/libraries/LiquidCrystal LIBMAPLE_MODULES += $(SRCROOT)/libraries/Wire diff --git a/examples/test-session.cpp b/examples/test-session.cpp index a147e06..9b4bce4 100644 --- a/examples/test-session.cpp +++ b/examples/test-session.cpp @@ -15,7 +15,6 @@ //#define COMM Serial2 //#define COMM Serial3 - #define ESC ((uint8)27) int rate = 0; @@ -610,17 +609,15 @@ void cmd_servo_sweep(void) { COMM.println("(reset serial port)"); } +static uint16 init_all_timers_prescale = 0; + +static void set_prescale(timer_dev *dev) { + timer_set_prescaler(dev, init_all_timers_prescale); +} + void init_all_timers(uint16 prescale) { - timer_init(TIMER1, prescale); - timer_init(TIMER2, prescale); - timer_init(TIMER3, prescale); - timer_init(TIMER4, prescale); -#ifdef STM32_HIGH_DENSITY - timer_init(TIMER5, prescale); - // timer_init(TIMER6, prescale); - // timer_init(TIMER7, prescale); - timer_init(TIMER8, prescale); -#endif + init_all_timers_prescale = prescale; + timer_foreach(set_prescale); } // Force init to be called *first*, i.e. before static object allocation. diff --git a/examples/test-timers.cpp b/examples/test-timers.cpp index f3cfdcc..a4fbc8a 100644 --- a/examples/test-timers.cpp +++ b/examples/test-timers.cpp @@ -1,8 +1,7 @@ -// Program to test the wirish timers implementation +// Program to test the timer.h implementation's essential functionality. #include "wirish.h" - -#define LED_PIN BOARD_LED_PIN +#include "timer.h" void handler1(void); void handler2(void); @@ -27,208 +26,253 @@ uint16 val2 = 10000; uint16 val3 = 10000; uint16 val4 = 10000; -HardwareTimer Timers[] = {Timer1, Timer2, Timer3, Timer4}; +// FIXME high density timer test (especially basic timers + DAC) +timer_dev *timers[] = {TIMER1, TIMER2, TIMER3, TIMER4}; +voidFuncPtr handlers[] = {handler1, handler2, handler3, handler4}; + +void initTimer(timer_dev *dev); +void setTimerPeriod(timer_dev *dev, uint32 period_us); +void testSetTimerPeriod(uint32 period); +void testTimerChannels(timer_dev *dev); +int timerNumber(timer_dev *dev); -void setup() -{ - /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); +void setup() { + // Set up the LED to blink + pinMode(BOARD_LED_PIN, OUTPUT); // Setup the button as input pinMode(BOARD_BUTTON_PIN, INPUT); - // Wait for user to attach... - waitForButtonPress(0); - - // Send a message out SerialUSB - SerialUSB.println("Beginning timer test..."); - for(int t=0; t<4; t++) { - Timers[t].setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel2Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel3Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel4Mode(TIMER_OUTPUTCOMPARE); - } + // Send a message out Serial2 + Serial2.begin(115200); + Serial2.println("*** Initializing timers..."); + Serial2.println("foo"); + timer_foreach(initTimer); + Serial2.println("*** Done. Beginning timer test."); } void loop() { - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.println("Testing setCount/getCount"); - SerialUSB.print("Timer1.getCount() = "); SerialUSB.println(Timer1.getCount()); - SerialUSB.println("Timer1.setCount(1234)"); - Timer1.setCount(1234); - SerialUSB.print("Timer1.getCount() = "); SerialUSB.println(Timer1.getCount()); - // This tests whether the pause/resume functions work; when BUT is held - // down Timer4 is in the "pause" state and the timer doesn't increment, so - // the final counts should reflect the ratio of time that BUT was held down - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.println("Testing Pause/Resume; button roughly controls Timer4"); + Serial2.println("-----------------------------------------------------"); + + Serial2.println("Testing timer_get_count()/timer_set_count()"); + Serial2.print("TIMER1 count = "); + Serial2.println(timer_get_count(TIMER1)); + Serial2.println("timer_set_count(TIMER1, 1234)"); + timer_set_count(TIMER1, 1234); + Serial2.print("timer_get_count(TIMER1) = "); + Serial2.println(timer_get_count(TIMER1)); + + Serial2.println("-----------------------------------------------------"); + Serial2.println("Testing pause/resume; button roughly controls TIMER4"); + // when BUT is held down, TIMER4 is in the "pause" state and the + // timer doesn't increment, so the final counts should reflect the + // ratio of time that BUT was held down. count3 = 0; count4 = 0; - Timer3.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer3.pause(); - Timer4.pause(); - Timer3.setCount(0); - Timer4.setCount(0); - Timer3.setOverflow(30000); - Timer4.setOverflow(30000); - Timer3.setCompare1(1000); - Timer4.setCompare1(1000); - Timer3.attachCompare1Interrupt(handler3b); - Timer4.attachCompare1Interrupt(handler4b); - Timer3.resume(); - Timer4.resume(); - SerialUSB.println("~4 seconds..."); - for(int i = 0; i<4000; i++) { - if(isButtonPressed()) { - Timer4.pause(); + timer_set_mode(TIMER3, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_pause(TIMER3); + timer_pause(TIMER4); + timer_set_count(TIMER3, 0); + timer_set_count(TIMER4, 0); + timer_set_reload(TIMER3, 30000); + timer_set_reload(TIMER4, 30000); + timer_set_compare(TIMER3, 1, 1000); + timer_set_compare(TIMER4, 1, 1000); + timer_attach_interrupt(TIMER3, TIMER_CC1_INTERRUPT, handler3b); + timer_attach_interrupt(TIMER4, TIMER_CC1_INTERRUPT, handler4b); + timer_resume(TIMER3); + timer_resume(TIMER4); + + Serial2.println("Testing for ~4 seconds..."); + for(int i = 0; i < 4000; i++) { + if (isButtonPressed()) { + timer_pause(TIMER4); } else { - Timer4.resume(); + timer_resume(TIMER4); } delay(1); } - Timer3.setChannel1Mode(TIMER_DISABLED); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count3: "); SerialUSB.println(count3); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - - // These test the setPeriod auto-configure functionality - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.println("Testing setPeriod"); - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setCompare1(1); - Timer4.setPeriod(10); - Timer4.pause(); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 10ms, wait 2 seconds..."); - count4 = 0; - Timer4.resume(); - delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/10ms = 200000)"); - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setCompare1(1); - Timer4.pause(); - Timer4.setPeriod(30000); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 30000ms, wait 2 seconds..."); - count4 = 0; - Timer4.resume(); - delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/30000ms ~ 67)"); - - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setPeriod(300000); - Timer4.setCompare1(1); - Timer4.pause(); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 300000ms, wait 2 seconds..."); - count4 = 0; - Timer4.resume(); - delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/300000ms ~ 6.7)"); - - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setPrescaleFactor(33); - Timer4.setOverflow(65454); - Timer4.pause(); - Timer4.setCount(0); - Timer4.setCompare1(1); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 30000ms, wait 2 seconds..."); + + timer_set_mode(TIMER3, TIMER_CH1, TIMER_DISABLED); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_DISABLED); + + Serial2.print("TIMER3 count: "); + Serial2.println(timer_get_count(TIMER3)); + Serial2.print("TIMER4 count: "); + Serial2.println(timer_get_count(TIMER4)); + + Serial2.println("-----------------------------------------------------"); + Serial2.println("Testing setTimerPeriod()"); + testSetTimerPeriod(10); + testSetTimerPeriod(30000); + testSetTimerPeriod(300000); + testSetTimerPeriod(30000); + + Serial2.println("Sanity check (with hand-coded reload and prescaler for " + "72 MHz timers):"); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_set_prescaler(TIMER4, 33); + timer_set_reload(TIMER4, 65454); + timer_pause(TIMER4); + timer_set_count(TIMER4, 0); + timer_set_compare(TIMER4, TIMER_CH1, 1); + timer_attach_interrupt(TIMER4, TIMER_CC1_INTERRUPT, handler4b); + Serial2.println("Period 30000ms, wait 2 seconds..."); count4 = 0; - Timer4.resume(); + timer_resume(TIMER4); delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/30000ms ~ 67)"); - - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setCompare1(1); - Timer4.setPeriod(30000); - Timer4.pause(); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 30000ms, wait 2 seconds..."); + timer_pause(TIMER4); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_DISABLED); + Serial2.print("TIMER4 count: "); + Serial2.println(count4); + Serial2.println(" (Should be around 2sec/30000ms ~ 67)"); + + // Test all the individual timer channels + timer_foreach(testTimerChannels); +} + +void initTimer(timer_dev *dev) { + switch (dev->type) { + case TIMER_ADVANCED: + case TIMER_GENERAL: + Serial2.print("Initializing timer "); + Serial2.println(timerNumber(dev)); + for (int c = 1; c <= 4; c++) { + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + } + Serial2.println("Done."); + break; + case TIMER_BASIC: + break; + } +} + +void testSetTimerPeriod(uint32 period) { + timer_set_mode(TIMER4, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_set_compare(TIMER4, TIMER_CH1, 1); + setTimerPeriod(TIMER4, period); + timer_pause(TIMER4); + timer_set_count(TIMER4, 0); + timer_attach_interrupt(TIMER4, TIMER_CC1_INTERRUPT, handler4b); + Serial2.println("Period "); + Serial2.print(period); + Serial2.print(" ms. Waiting 2 seconds..."); count4 = 0; - Timer4.resume(); + timer_resume(TIMER4); delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/30000ms ~ 67)"); - - // This section is to touch every channel of every timer. The output - // ratios should reflect the ratios of the rate variables. Demonstrates - // that over time the actual timing rates get blown away by other system - // interrupts. - for(t=0; t<4; t++) { - toggleLED(); - delay(100); - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.print("Testing Timer "); SerialUSB.println(t+1); + timer_pause(TIMER4); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_DISABLED); + Serial2.print("TIMER4 count: "); + Serial2.println(timer_get_count(TIMER4)); + Serial2.print(" (Should be around 2 sec / "); + Serial2.print(period); + Serial2.print(" ms = "); + Serial2.print(double(2) / period * 1000); + Serial2.println(", modulo delays due to interrupts)"); +} + +int timerNumber(timer_dev *dev) { + switch (dev->clk_id) { + case RCC_TIMER1: + return 1; + case RCC_TIMER2: + return 2; + case RCC_TIMER3: + return 3; + case RCC_TIMER4: + return 4; +#ifdef STM32_HIGH_DENSITY + case RCC_TIMER5: + return 5; + case RCC_TIMER6: + return 6; + case RCC_TIMER7: + return 7; + case RCC_TIMER8: + return 8; +#endif + default: + ASSERT(0); + return 0; + } +} + +/* This function touches every channel of a given timer. The output + * ratios should reflect the ratios of the rate variables. It + * demonstrates that, over time, the actual timing rates get blown + * away by other system interrupts. */ +void testTimerChannels(timer_dev *dev) { + t = timerNumber(dev); + toggleLED(); + delay(100); + Serial2.println("-----------------------------------------------------"); + switch (dev->type) { + case TIMER_BASIC: + Serial2.print("NOT testing channels for basic timer "); + Serial2.println(t); + break; + case TIMER_ADVANCED: + case TIMER_GENERAL: + Serial2.print("Testing channels for timer "); + Serial2.println(t); + timer_pause(dev); count1 = count2 = count3 = count4 = 0; - Timers[t].setOverflow(0xFFFF); - Timers[t].setPrescaleFactor(1); - Timers[t].setCompare1(65535); - Timers[t].setCompare2(65535); - Timers[t].setCompare3(65535); - Timers[t].setCompare4(65535); - Timers[t].setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel2Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel3Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel4Mode(TIMER_OUTPUTCOMPARE); - Timers[t].attachCompare1Interrupt(handler1); - Timers[t].attachCompare2Interrupt(handler2); - Timers[t].attachCompare3Interrupt(handler3); - Timers[t].attachCompare4Interrupt(handler4); - Timers[t].resume(); + timer_set_reload(dev, 0xFFFF); + timer_set_prescaler(dev, 1); + for (int c = 1; c <= 4; c++) { + timer_set_compare(dev, c, 65535); + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + timer_attach_interrupt(dev, c, handlers[c - 1]); + } + timer_resume(dev); delay(3000); - Timers[t].setChannel1Mode(TIMER_DISABLED); - Timers[t].setChannel2Mode(TIMER_DISABLED); - Timers[t].setChannel3Mode(TIMER_DISABLED); - Timers[t].setChannel4Mode(TIMER_DISABLED); - SerialUSB.print("Count1: "); SerialUSB.println(count1); - SerialUSB.print("Count2: "); SerialUSB.println(count2); - SerialUSB.print("Count3: "); SerialUSB.println(count3); - SerialUSB.print("Count4: "); SerialUSB.println(count4); + for (int c = 1; c <= 4; c++) { + timer_set_mode(dev, c, TIMER_DISABLED); + } + Serial2.print("Channel 1 count: "); Serial2.println(count1); + Serial2.print("Channel 2 count: "); Serial2.println(count2); + Serial2.print("Channel 3 count: "); Serial2.println(count3); + Serial2.print("Channel 4 count: "); Serial2.println(count4); + break; + } +} + +// FIXME move this into the new wirish timer implementation +void setTimerPeriod(timer_dev *dev, uint32 period_us) { + if (!period_us) { + // FIXME handle this case + ASSERT(0); + return; } + uint32 cycles = period_us * CYCLES_PER_MICROSECOND; + uint16 pre = (uint16)((cycles >> 16) + 1); + timer_set_prescaler(dev, pre); + timer_set_reload(dev, cycles / pre - 1); } void handler1(void) { val1 += rate1; - Timers[t].setCompare1(val1); + timer_set_compare(timers[t], TIMER_CH1, val1); count1++; } void handler2(void) { val2 += rate2; - Timers[t].setCompare2(val2); + timer_set_compare(timers[t], TIMER_CH2, val2); count2++; } void handler3(void) { val3 += rate3; - Timers[t].setCompare3(val3); + timer_set_compare(timers[t], TIMER_CH3, val3); count3++; } void handler4(void) { val4 += rate4; - Timers[t].setCompare4(val4); + timer_set_compare(timers[t], TIMER_CH4, val4); count4++; } diff --git a/libmaple/bitband.h b/libmaple/bitband.h index 3e02702..870abe9 100644 --- a/libmaple/bitband.h +++ b/libmaple/bitband.h @@ -30,6 +30,9 @@ * @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 @@ -103,3 +106,5 @@ static inline __io uint32* __bb_addr(__io void *address, uint32 bb_ref) { return (__io uint32*)(bb_base + ((uint32)address - bb_ref) * 32 + bit * 4); } + +#endif /* _BITBAND_H_ */ diff --git a/libmaple/delay.h b/libmaple/delay.h index 10839c9..e4d85c5 100644 --- a/libmaple/delay.h +++ b/libmaple/delay.h @@ -1,5 +1,5 @@ /** - * @brief + * @brief Delay implementation */ #ifndef _DELAY_H_ diff --git a/libmaple/nvic.h b/libmaple/nvic.h index fe9990f..cbcd49c 100644 --- a/libmaple/nvic.h +++ b/libmaple/nvic.h @@ -53,9 +53,6 @@ extern "C"{ /* System control registers */ #define SCB_VTOR 0xE000ED08 // Vector table offset register -#define NVIC_VectTab_RAM ((u32)0x20000000) -#define NVIC_VectTab_FLASH ((u32)0x08000000) - #define NVIC_BASE 0xE000E100 #define NVIC ((nvic_reg_map*)NVIC_BASE) @@ -75,52 +72,58 @@ typedef struct nvic_reg_map { __io uint32 STIR; // Software Trigger Interrupt Registers } nvic_reg_map; -enum { - NVIC_NMI = -14, - NVIC_MEM_MANAGE = -12, - NVIC_BUS_FAULT = -11, - NVIC_USAGE_FAULT = -10, - NVIC_SVC = -5, - NVIC_DEBUG_MON = -4, - NVIC_PEND_SVC = -2, - NVIC_SYSTICK = -1, - 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, - - NVIC_I2C1_EV = 31, - NVIC_I2C1_ER = 32, - NVIC_I2C2_EV = 33, - NVIC_I2C2_ER = 34 -}; - +typedef enum nvic_irq_num { + NVIC_NMI = -14, + NVIC_MEM_MANAGE = -12, + NVIC_BUS_FAULT = -11, + NVIC_USAGE_FAULT = -10, + NVIC_SVC = -5, + NVIC_DEBUG_MON = -4, + NVIC_PEND_SVC = -2, + NVIC_SYSTICK = -1, + + NVIC_TIMER1_BRK = 24, + NVIC_TIMER1_UP = 25, + NVIC_TIMER1_TRG_COM = 26, + NVIC_TIMER1_CC = 27, + NVIC_TIMER2 = 28, + NVIC_TIMER3 = 29, + NVIC_TIMER4 = 30, + NVIC_TIMER5 = 50, + NVIC_TIMER6 = 54, + NVIC_TIMER7 = 55, + NVIC_TIMER8_BRK = 43, + NVIC_TIMER8_UP = 44, + NVIC_TIMER8_TRG_COM = 45, + NVIC_TIMER8_CC = 46, + + NVIC_USART1 = 37, + NVIC_USART2 = 38, + NVIC_USART3 = 39, + NVIC_UART4 = 52, + NVIC_UART5 = 53, + + 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, + + NVIC_I2C1_EV = 31, + NVIC_I2C1_ER = 32, + NVIC_I2C2_EV = 33, + NVIC_I2C2_ER = 34 +} nvic_irq_num; #define nvic_globalirq_enable() asm volatile("cpsie i") #define nvic_globalirq_disable() asm volatile("cpsid i") diff --git a/libmaple/rules.mk b/libmaple/rules.mk index 48b5ed4..a9d7e50 100644 --- a/libmaple/rules.mk +++ b/libmaple/rules.mk @@ -28,7 +28,7 @@ cSRCS_$(d) := adc.c \ spi.c \ syscalls.c \ systick.c \ - timers.c \ + timer.c \ usart.c \ util.c \ usb/descriptors.c \ diff --git a/libmaple/spi.h b/libmaple/spi.h index db8aa9c..5ebf52d 100644 --- a/libmaple/spi.h +++ b/libmaple/spi.h @@ -31,6 +31,9 @@ #ifndef _SPI_H_ #define _SPI_H_ +#include "libmaple_types.h" +#include "util.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/libmaple/timer.c b/libmaple/timer.c new file mode 100644 index 0000000..ad0ec6f --- /dev/null +++ b/libmaple/timer.c @@ -0,0 +1,449 @@ +/****************************************************************************** + * 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" + +#if defined(STM32_MEDIUM_DENSITY) +#define NR_TIMERS 4 +#elif defined(STM32_HIGH_DENSITY) +#define NR_TIMERS 8 +#endif + +/* 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(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 given function on all timers. + * @param fn Function to call on each timer. + */ +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(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(0); + break; + } +} diff --git a/libmaple/timer.h b/libmaple/timer.h new file mode 100644 index 0000000..025bcb0 --- /dev/null +++ b/libmaple/timer.h @@ -0,0 +1,1010 @@ +/****************************************************************************** + * 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 (basic) */ +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 timer_num 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 + * @brief dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @brief 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 << 8) - 1; + (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 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 c561d39..0000000 --- a/libmaple/timers.c +++ /dev/null @@ -1,526 +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 - }, -#ifdef STM32_HIGH_DENSITY - /* 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; - } -#ifdef STM32_HIGH_DENSITY - 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) { - timer_port *timer; -#ifdef STM32_HIGH_DENSITY - 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 __irq_tim1_cc(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 __irq_tim2(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 __irq_tim3(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 __irq_tim4(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 1f6afcd..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 */ -#ifdef STM32_HIGH_DENSITY - 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 ranges over timers, and n ranges - * 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/util.c b/libmaple/util.c index 09fbecd..6b4f216 100644 --- a/libmaple/util.c +++ b/libmaple/util.c @@ -32,7 +32,7 @@ #include "gpio.h" #include "nvic.h" #include "adc.h" -#include "timers.h" +#include "timer.h" /* Failed asserts send out a message on this USART. */ #ifndef ERROR_USART_NUM diff --git a/libraries/Servo/Servo.h b/libraries/Servo/Servo.h index 1c75618..583312e 100644 --- a/libraries/Servo/Servo.h +++ b/libraries/Servo/Servo.h @@ -28,6 +28,7 @@ #include <stdint.h> #include "wirish.h" /* hack for IDE compile */ +#include "HardwareTimer.h" /* Note on Arduino compatibility: diff --git a/notes/portable.txt b/notes/portable.txt index 69952d7..cc1f2ac 100644 --- a/notes/portable.txt +++ b/notes/portable.txt @@ -1,98 +1,28 @@ +Board portability is implemented in boards.h, libmaple.h, and stm32.h. -Disclaimer text: // High-density devices only (Maple Native) +At compile time, we currently expect one of STM32_MEDIUM_DENSITY or +STM32_HIGH_DENSITY to be defined. There's no support for low-density +chips. XL-density isn't in the near horizon; patches welcome. You'll +also need to define some BOARD_foo if you expect to use Wirish; this +comes along with some additional assumptions about your board's layout. +The code in usb/ is not very portable at all right now; expect this to +change in the future, but for now, we're focusing on rolling out a +more complete backend. -Board portability is implemented by adding a header file to ./libmaple with the -name of the BOARD target, and then editing libmaple.h to add this file as an -option. - -A pin maple file should be added to ./notes describing the pin numbering - -Files to check by hand: -# adc.c -# adc.h -# exc.c -# exti.c -# exti.h -# flash.c -# flash.h -# gpio.c -# gpio.h -# libmaple_types.h -# nvic.c -# nvic.h -# rcc.c -# rcc.h -# ring_buffer.h -# rules.mk -# spi.c -- spi.h -# syscalls.c -# systick.c -# systick.h -# timers.c -# timers.h -# usart.c -# usart.h -# util.c -# util.h -# libmaple.h -# usb/* - -wirish/: -# bits.h -# boards.h -# cxxabi-compat.cpp -# ext_interrupts.c -# ext_interrupts.h -# HardwareTimer.cpp -# HardwareTimer.h -# io.h -# main.cxx -# Print.cpp -# Print.h -# pwm.c -# pwm.h -# rules.mk -# time.c -# time.h -# usb_serial.cpp -# usb_serial.h -# wirish_analog.c -# wirish.c -# wirish_digital.c -# wirish.h -# wirish_math.cpp -# wirish_math.h -# wirish_shift.c -# WProgram.h -- comm/ - - - -ADC Notes: - only using ADC1? - untested - -EXTI Notes: - need to update huge table in comments? - untested +A file should be added to ./notes describing the pin numbering of any +new board you add. NVIC Notes: - I don't think NVIC_ISER3 and NVIC_ICER3 actually exist? Only CANBUS and USB OTG use interrupts above #63, but I updated the nvic code anyways RCC Notes: Added some clock stuff to all boards even though they aren't usable... blah. SPI Notes: - SPI3 is only in XL chips so didn't really handle that + SPI3 is only in XL chips, so we don't handle that. TIMER Notes: - High-density devices add an advanced timer (TIMER8) and another normal one (TIMER5). - TIMER6 and TIMER7 are much less useful. - There is some partial progress towards adding timer5/timer8 functionality, - but not much. This should probably all be rewritten. The wirish timer implementation should be refactored to use pin numbers. USART Notes: diff --git a/notes/usb.txt b/notes/usb.txt index 5e00354..9552b9f 100644 --- a/notes/usb.txt +++ b/notes/usb.txt @@ -1,3 +1,7 @@ +XXX +XXX This file may be out of date! +XXX + [NOTE: this is a long term proposal. The current implementation just does a 2ms TIMEOUT] diff --git a/notes/vga.txt b/notes/vga.txt index d75281a..43b6830 100644 --- a/notes/vga.txt +++ b/notes/vga.txt @@ -7,8 +7,7 @@ gpio_write_bit() is about 360ns (2.78MHz) Writing to GPIO?_BASE is about 60ns (16.6MHz -> 18MHz) -pwm write 0x0001 is about 30ns (33MHz) with prescaler as 1 (default) -pwm write 0x0001 is about 14ns (72MHz) with prescaler as 0 (!) +PWM write 0x0001 is about 14ns (72MHz) with prescaler as 0 (!) VGA Timing ------------------------------------------------------------------------------ @@ -34,9 +33,3 @@ Crude 640x480 directions: 11 lines front porch 2 lines Vsync (low) 31 lines back porch - -Currently, setting vs. clearing GPIO registers seems to take a different amount -of time? Or perhaps i'm not analyzing branching correctly. Regardless, if you -SET 100x times then UNSET on one line, then UNSET 100x then SET the next line, -the two changes in color will generally not line up. - diff --git a/wirish/boards.cpp b/wirish/boards.cpp index 66f008f..d99b019 100644 --- a/wirish/boards.cpp +++ b/wirish/boards.cpp @@ -1,96 +1,56 @@ #include "boards.h" -// think of the poor column numbers +// For concision #define ADCx ADC_INVALID -#define TIMERx TIMER_INVALID #if defined(BOARD_maple) PinMapping PIN_MAP[NR_GPIO_PINS] = { - /* D0/PA3 */ - {GPIOA, 3, 3, TIMER2_CH4_CCR, TIMER2, 4, AFIO_EXTI_PA}, - /* D1/PA2 */ - {GPIOA, 2, 2, TIMER2_CH3_CCR, TIMER2, 3, AFIO_EXTI_PA}, - /* D2/PA0 */ - {GPIOA, 0, 0, TIMER2_CH1_CCR, TIMER2, 1, AFIO_EXTI_PA}, - /* D3/PA1 */ - {GPIOA, 1, 1, TIMER2_CH2_CCR, TIMER2, 2, AFIO_EXTI_PA}, - /* D4/PB5 */ - {GPIOB, 5, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D5/PB6 */ - {GPIOB, 6, ADCx, TIMER4_CH1_CCR, TIMER4, 1, AFIO_EXTI_PB}, - /* D6/PA8 */ - {GPIOA, 8, ADCx, TIMER1_CH1_CCR, TIMER1, 1, AFIO_EXTI_PA}, - /* D7/PA9 */ - {GPIOA, 9, ADCx, TIMER1_CH2_CCR, TIMER1, 2, AFIO_EXTI_PA}, - /* D8/PA10 */ - {GPIOA, 10, ADCx, TIMER1_CH3_CCR, TIMER1, 3, AFIO_EXTI_PA}, - /* D9/PB7 */ - {GPIOB, 7, ADCx, TIMER4_CH2_CCR, TIMER4, 2, AFIO_EXTI_PB}, - /* D10/PA4 */ - {GPIOA, 4, 4, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D11/PA7 */ - {GPIOA, 7, 7, TIMER3_CH2_CCR, TIMER3, 2, AFIO_EXTI_PA}, - /* D12/PA6 */ - {GPIOA, 6, 6, TIMER3_CH1_CCR, TIMER3, 1, AFIO_EXTI_PA}, - /* D13/PA5 */ - {GPIOA, 5, 5, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D14/PB8 */ - {GPIOB, 8, ADCx, TIMER4_CH3_CCR, TIMER4, 3, AFIO_EXTI_PB}, + {GPIOA, 3, 3, TIMER2, 4, AFIO_EXTI_PA}, /* D0/PA3 */ + {GPIOA, 2, 2, TIMER2, 3, AFIO_EXTI_PA}, /* D1/PA2 */ + {GPIOA, 0, 0, TIMER2, 1, AFIO_EXTI_PA}, /* D2/PA0 */ + {GPIOA, 1, 1, TIMER2, 2, AFIO_EXTI_PA}, /* D3/PA1 */ + {GPIOB, 5, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D4/PB5 */ + {GPIOB, 6, ADCx, TIMER4, 1, AFIO_EXTI_PB}, /* D5/PB6 */ + {GPIOA, 8, ADCx, TIMER1, 1, AFIO_EXTI_PA}, /* D6/PA8 */ + {GPIOA, 9, ADCx, TIMER1, 2, AFIO_EXTI_PA}, /* D7/PA9 */ + {GPIOA, 10, ADCx, TIMER1, 3, AFIO_EXTI_PA}, /* D8/PA10 */ + {GPIOB, 7, ADCx, TIMER4, 2, AFIO_EXTI_PB}, /* D9/PB7 */ + {GPIOA, 4, 4, NULL, 0, AFIO_EXTI_PA}, /* D10/PA4 */ + {GPIOA, 7, 7, TIMER3, 2, AFIO_EXTI_PA}, /* D11/PA7 */ + {GPIOA, 6, 6, TIMER3, 1, AFIO_EXTI_PA}, /* D12/PA6 */ + {GPIOA, 5, 5, NULL, 0, AFIO_EXTI_PA}, /* D13/PA5 (LED) */ + {GPIOB, 8, ADCx, TIMER4, 3, AFIO_EXTI_PB}, /* D14/PB8 */ /* Little header */ - /* D15/PC0 */ - {GPIOC, 0, 10, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D16/PC1 */ - {GPIOC, 1, 11, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D17/PC2 */ - {GPIOC, 2, 12, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D18/PC3 */ - {GPIOC, 3, 13, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D19/PC4 */ - {GPIOC, 4, 14, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D20/PC5 */ - {GPIOC, 5, 15, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, + {GPIOC, 0, 10, NULL, 0, AFIO_EXTI_PC}, /* D15/PC0 */ + {GPIOC, 1, 11, NULL, 0, AFIO_EXTI_PC}, /* D16/PC1 */ + {GPIOC, 2, 12, NULL, 0, AFIO_EXTI_PC}, /* D17/PC2 */ + {GPIOC, 3, 13, NULL, 0, AFIO_EXTI_PC}, /* D18/PC3 */ + {GPIOC, 4, 14, NULL, 0, AFIO_EXTI_PC}, /* D19/PC4 */ + {GPIOC, 5, 15, NULL, 0, AFIO_EXTI_PC}, /* D20/PC5 */ /* External header */ - /* D21/PC13 */ - {GPIOC, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D22/PC14 */ - {GPIOC, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D23/PC15 */ - {GPIOC, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D24/PB9 */ - {GPIOB, 9, ADCx, TIMER4_CH4_CCR, TIMER4, 4, AFIO_EXTI_PB}, - /* D25/PD2 */ - {GPIOD, 2, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D26/PC10 */ - {GPIOC, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D27/PB0 */ - {GPIOB, 0, 8, TIMER3_CH3_CCR, TIMER3, 3, AFIO_EXTI_PB}, - /* D28/PB1 */ - {GPIOB, 1, 9, TIMER3_CH4_CCR, TIMER3, 4, AFIO_EXTI_PB}, - /* D29/PB10 */ - {GPIOB, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D30/PB11 */ - {GPIOB, 11, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D31/PB12 */ - {GPIOB, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D32/PB13 */ - {GPIOB, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D33/PB14 */ - {GPIOB, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D34/PB15 */ - {GPIOB, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D35/PC6 */ - {GPIOC, 6, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D36/PC7 */ - {GPIOC, 7, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D37/PC8 */ - {GPIOC, 8, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D38/PC9 (BUT) */ - {GPIOC, 9, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC} + {GPIOC, 13, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D21/PC13 */ + {GPIOC, 14, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D22/PC14 */ + {GPIOC, 15, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D23/PC15 */ + {GPIOB, 9, ADCx, TIMER4, 4, AFIO_EXTI_PB}, /* D24/PB9 */ + {GPIOD, 2, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D25/PD2 */ + {GPIOC, 10, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D26/PC10 */ + {GPIOB, 0, 8, TIMER3, 3, AFIO_EXTI_PB}, /* D27/PB0 */ + {GPIOB, 1, 9, TIMER3, 4, AFIO_EXTI_PB}, /* D28/PB1 */ + {GPIOB, 10, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D29/PB10 */ + {GPIOB, 11, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D30/PB11 */ + {GPIOB, 12, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D31/PB12 */ + {GPIOB, 13, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D32/PB13 */ + {GPIOB, 14, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D33/PB14 */ + {GPIOB, 15, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D34/PB15 */ + {GPIOC, 6, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D35/PC6 */ + {GPIOC, 7, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D36/PC7 */ + {GPIOC, 8, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D37/PC8 */ + {GPIOC, 9, ADCx, NULL, 0, AFIO_EXTI_PC} /* D38/PC9 (BUT) */ }; #elif defined(BOARD_maple_native) @@ -98,375 +58,207 @@ PinMapping PIN_MAP[NR_GPIO_PINS] = { PinMapping PIN_MAP[NR_GPIO_PINS] = { /* Top header */ - /* D0/PB10 */ - {GPIOB, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D1/PB2 */ - {GPIOB, 2, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D2/PB12 */ - {GPIOB, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D3/PB13 */ - {GPIOB, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D4/PB14 */ - {GPIOB, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D5/PB15 */ - {GPIOB, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D6/PC0 */ - {GPIOC, 0, 10, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D7/PC1 */ - {GPIOC, 1, 11, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D8/PC2 */ - {GPIOC, 2, 12, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D9/PC3 */ - {GPIOC, 3, 13, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D10/PC4 */ - {GPIOC, 4, 14, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D11/PC5 */ - {GPIOC, 5, 15, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D12/PC6 */ - {GPIOC, 6, ADCx, TIMER8_CH1_CCR, TIMER8, 1, AFIO_EXTI_PC}, - /* D13/PC7 */ - {GPIOC, 7, ADCx, TIMER8_CH2_CCR, TIMER8, 2, AFIO_EXTI_PC}, - /* D14/PC8 */ - {GPIOC, 8, ADCx, TIMER8_CH3_CCR, TIMER8, 3, AFIO_EXTI_PC}, - /* D15/PC9 */ - {GPIOC, 9, ADCx, TIMER8_CH4_CCR, TIMER8, 4, AFIO_EXTI_PC}, - /* D16/PC10 */ - {GPIOC, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D17/PC11 */ - {GPIOC, 11, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D18/PC12 */ - {GPIOC, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D19/PC13 */ - {GPIOC, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D20/PC14 */ - {GPIOC, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D21/PC15 */ - {GPIOC, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D22/PA8 */ - {GPIOA, 8, ADCx, TIMER1_CH1_CCR, TIMER1, 1, AFIO_EXTI_PA}, - /* D23/PA9 */ - {GPIOA, 9, ADCx, TIMER1_CH2_CCR, TIMER1, 2, AFIO_EXTI_PA}, - /* D24/PA10 */ - {GPIOA, 10, ADCx, TIMER1_CH3_CCR, TIMER1, 3, AFIO_EXTI_PA}, - /* D25/PB9 */ - {GPIOB, 9, ADCx, TIMER4_CH4_CCR, TIMER4, 4, AFIO_EXTI_PB}, + {GPIOB, 10, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D0/PB10 */ + {GPIOB, 2, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D1/PB2 */ + {GPIOB, 12, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D2/PB12 */ + {GPIOB, 13, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D3/PB13 */ + {GPIOB, 14, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D4/PB14 */ + {GPIOB, 15, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D5/PB15 */ + {GPIOC, 0, 10, NULL, 0, AFIO_EXTI_PC}, /* D6/PC0 */ + {GPIOC, 1, 11, NULL, 0, AFIO_EXTI_PC}, /* D7/PC1 */ + {GPIOC, 2, 12, NULL, 0, AFIO_EXTI_PC}, /* D8/PC2 */ + {GPIOC, 3, 13, NULL, 0, AFIO_EXTI_PC}, /* D9/PC3 */ + {GPIOC, 4, 14, NULL, 0, AFIO_EXTI_PC}, /* D10/PC4 */ + {GPIOC, 5, 15, NULL, 0, AFIO_EXTI_PC}, /* D11/PC5 */ + {GPIOC, 6, ADCx, TIMER8, 1, AFIO_EXTI_PC}, /* D12/PC6 */ + {GPIOC, 7, ADCx, TIMER8, 2, AFIO_EXTI_PC}, /* D13/PC7 */ + {GPIOC, 8, ADCx, TIMER8, 3, AFIO_EXTI_PC}, /* D14/PC8 */ + {GPIOC, 9, ADCx, TIMER8, 4, AFIO_EXTI_PC}, /* D15/PC9 */ + {GPIOC, 10, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D16/PC10 */ + {GPIOC, 11, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D17/PC11 */ + {GPIOC, 12, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D18/PC12 */ + {GPIOC, 13, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D19/PC13 */ + {GPIOC, 14, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D20/PC14 */ + {GPIOC, 15, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D21/PC15 */ + {GPIOA, 8, ADCx, TIMER1, 1, AFIO_EXTI_PA}, /* D22/PA8 */ + {GPIOA, 9, ADCx, TIMER1, 2, AFIO_EXTI_PA}, /* D23/PA9 */ + {GPIOA, 10, ADCx, TIMER1, 3, AFIO_EXTI_PA}, /* D24/PA10 */ + {GPIOB, 9, ADCx, TIMER4, 4, AFIO_EXTI_PB}, /* D25/PB9 */ /* Bottom header */ + /* FIXME (?) What about D48--D50 also being TIMER2_CH[234]? */ - /* D26/PD2 */ - {GPIOD, 2, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D27/PD3 */ - {GPIOD, 3, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D28/PD6 */ - {GPIOD, 6, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D29/PG11 */ - {GPIOG, 11, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D30/PG12 */ - {GPIOG, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D31/PG13 */ - {GPIOG, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D32/PG14 */ - {GPIOG, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D33/PG8 */ - {GPIOG, 8, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D34/PG7 */ - {GPIOG, 7, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D35/PG6 */ - {GPIOG, 6, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D36/PB5 */ - {GPIOB, 5, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D37/PB6 */ - {GPIOB, 6, ADCx, TIMER4_CH1_CCR, TIMER4, 1, AFIO_EXTI_PB}, - /* D38/PB7 */ - {GPIOB, 7, ADCx, TIMER4_CH2_CCR, TIMER4, 2, AFIO_EXTI_PB}, - /* D39/PF6 */ - {GPIOF, 6, 4, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D40/PF7 */ - {GPIOF, 7, 5, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D41/PF8 */ - {GPIOF, 8, 6, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D42/PF9 */ - {GPIOF, 9, 7, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D43/PF10 */ - {GPIOF, 10, 8, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D44/PF11 */ - {GPIOF, 11, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D45/PB1 */ - {GPIOB, 1, 9, TIMER3_CH4_CCR, TIMER3, 4, AFIO_EXTI_PB}, - /* D46/PB0 */ - {GPIOB, 0, 8, TIMER3_CH3_CCR, TIMER3, 3, AFIO_EXTI_PB}, - /* D47/PA0 */ - {GPIOA, 0, 0, TIMER5_CH1_CCR, TIMER5, 1, AFIO_EXTI_PA}, - /* D48/PA1 */ /* FIXME (?) - What about D48--D50 also being TIMER2_CH[234]? */ - {GPIOA, 1, 1, TIMER5_CH2_CCR, TIMER5, 2, AFIO_EXTI_PA}, - /* D49/PA2 */ - {GPIOA, 2, 2, TIMER5_CH3_CCR, TIMER5, 3, AFIO_EXTI_PA}, - /* D50/PA3 */ - {GPIOA, 3, 3, TIMER5_CH4_CCR, TIMER5, 4, AFIO_EXTI_PA}, - /* D51/PA4 */ - {GPIOA, 4, 4, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D52/PA5 */ - {GPIOA, 5, 5, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D53/PA6 */ - {GPIOA, 6, 6, TIMER3_CH1_CCR, TIMER3, 1, AFIO_EXTI_PA}, - /* D54/PA7 */ - {GPIOA, 7, 7, TIMER3_CH2_CCR, TIMER3, 2, AFIO_EXTI_PA}, + {GPIOD, 2, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D26/PD2 */ + {GPIOD, 3, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D27/PD3 */ + {GPIOD, 6, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D28/PD6 */ + {GPIOG, 11, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D29/PG11 */ + {GPIOG, 12, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D30/PG12 */ + {GPIOG, 13, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D31/PG13 */ + {GPIOG, 14, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D32/PG14 */ + {GPIOG, 8, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D33/PG8 */ + {GPIOG, 7, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D34/PG7 */ + {GPIOG, 6, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D35/PG6 */ + {GPIOB, 5, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D36/PB5 */ + {GPIOB, 6, ADCx, TIMER4, 1, AFIO_EXTI_PB}, /* D37/PB6 */ + {GPIOB, 7, ADCx, TIMER4, 2, AFIO_EXTI_PB}, /* D38/PB7 */ + {GPIOF, 6, 4, NULL, 0, AFIO_EXTI_PF}, /* D39/PF6 */ + {GPIOF, 7, 5, NULL, 0, AFIO_EXTI_PF}, /* D40/PF7 */ + {GPIOF, 8, 6, NULL, 0, AFIO_EXTI_PF}, /* D41/PF8 */ + {GPIOF, 9, 7, NULL, 0, AFIO_EXTI_PF}, /* D42/PF9 */ + {GPIOF, 10, 8, NULL, 0, AFIO_EXTI_PF}, /* D43/PF10 */ + {GPIOF, 11, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D44/PF11 */ + {GPIOB, 1, 9, TIMER3, 4, AFIO_EXTI_PB}, /* D45/PB1 */ + {GPIOB, 0, 8, TIMER3, 3, AFIO_EXTI_PB}, /* D46/PB0 */ + {GPIOA, 0, 0, TIMER5, 1, AFIO_EXTI_PA}, /* D47/PA0 */ + {GPIOA, 1, 1, TIMER5, 2, AFIO_EXTI_PA}, /* D48/PA1 */ + {GPIOA, 2, 2, TIMER5, 3, AFIO_EXTI_PA}, /* D49/PA2 */ + {GPIOA, 3, 3, TIMER5, 4, AFIO_EXTI_PA}, /* D50/PA3 */ + {GPIOA, 4, 4, NULL, 0, AFIO_EXTI_PA}, /* D51/PA4 */ + {GPIOA, 5, 5, NULL, 0, AFIO_EXTI_PA}, /* D52/PA5 */ + {GPIOA, 6, 6, TIMER3, 1, AFIO_EXTI_PA}, /* D53/PA6 */ + {GPIOA, 7, 7, TIMER3, 2, AFIO_EXTI_PA}, /* D54/PA7 */ /* Right (triple) header */ - /* D55/PF0 */ - {GPIOF, 0, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D56/PD11 */ - {GPIOD, 11, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D57/PD14 */ - {GPIOD, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D58/PF1 */ - {GPIOF, 1, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D59/PD12 */ - {GPIOD, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D60/PD15 */ - {GPIOD, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D61/PF2 */ - {GPIOF, 2, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D62/PD13 */ - {GPIOD, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D63/PD0 */ - {GPIOD, 0, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D64/PF3 */ - {GPIOF, 3, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D65/PE3 */ - {GPIOE, 3, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D66/PD1 */ - {GPIOD, 1, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D67/PF4 */ - {GPIOF, 4, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D68/PE4 */ - {GPIOE, 4, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D69/PE7 */ - {GPIOE, 7, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D70/PF5 */ - {GPIOF, 5, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D71/PE5 */ - {GPIOE, 5, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D72/PE8 */ - {GPIOE, 8, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D73/PF12 */ - {GPIOF, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D74/PE6 */ - {GPIOE, 6, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D75/PE9 */ - {GPIOE, 9, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D76/PF13 */ - {GPIOF, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D77/PE10 */ - {GPIOE, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D78/PF14 */ - {GPIOF, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D79/PG9 */ - {GPIOG, 9, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D80/PE11 */ - {GPIOE, 11, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D81/PF15 */ - {GPIOF, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PF}, - /* D82/PG10 */ - {GPIOG, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D83/PE12 */ - {GPIOE, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D84/PG0 */ - {GPIOG, 0, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D85/PD5 */ - {GPIOD, 5, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D86/PE13 */ - {GPIOE, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D87/PG1 */ - {GPIOG, 1, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D88/PD4 */ - {GPIOD, 4, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D89/PE14 */ - {GPIOE, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D90/PG2 */ - {GPIOG, 2, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D91/PE1 */ - {GPIOE, 1, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D92/PE15 */ - {GPIOE, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D93/PG3 */ - {GPIOG, 3, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D94/PE0 */ - {GPIOE, 0, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PE}, - /* D95/PD8 */ - {GPIOD, 8, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D96/PG4 */ - {GPIOG, 4, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D97/PD9 */ - {GPIOD, 9, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D98/PG5 */ - {GPIOG, 5, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PG}, - /* D99/PD10 */ - {GPIOD, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD} + {GPIOF, 0, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D55/PF0 */ + {GPIOD, 11, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D56/PD11 */ + {GPIOD, 14, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D57/PD14 */ + {GPIOF, 1, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D58/PF1 */ + {GPIOD, 12, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D59/PD12 */ + {GPIOD, 15, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D60/PD15 */ + {GPIOF, 2, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D61/PF2 */ + {GPIOD, 13, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D62/PD13 */ + {GPIOD, 0, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D63/PD0 */ + {GPIOF, 3, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D64/PF3 */ + {GPIOE, 3, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D65/PE3 */ + {GPIOD, 1, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D66/PD1 */ + {GPIOF, 4, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D67/PF4 */ + {GPIOE, 4, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D68/PE4 */ + {GPIOE, 7, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D69/PE7 */ + {GPIOF, 5, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D70/PF5 */ + {GPIOE, 5, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D71/PE5 */ + {GPIOE, 8, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D72/PE8 */ + {GPIOF, 12, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D73/PF12 */ + {GPIOE, 6, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D74/PE6 */ + {GPIOE, 9, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D75/PE9 */ + {GPIOF, 13, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D76/PF13 */ + {GPIOE, 10, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D77/PE10 */ + {GPIOF, 14, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D78/PF14 */ + {GPIOG, 9, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D79/PG9 */ + {GPIOE, 11, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D80/PE11 */ + {GPIOF, 15, ADCx, NULL, 0, AFIO_EXTI_PF}, /* D81/PF15 */ + {GPIOG, 10, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D82/PG10 */ + {GPIOE, 12, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D83/PE12 */ + {GPIOG, 0, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D84/PG0 */ + {GPIOD, 5, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D85/PD5 */ + {GPIOE, 13, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D86/PE13 */ + {GPIOG, 1, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D87/PG1 */ + {GPIOD, 4, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D88/PD4 */ + {GPIOE, 14, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D89/PE14 */ + {GPIOG, 2, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D90/PG2 */ + {GPIOE, 1, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D91/PE1 */ + {GPIOE, 15, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D92/PE15 */ + {GPIOG, 3, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D93/PG3 */ + {GPIOE, 0, ADCx, NULL, 0, AFIO_EXTI_PE}, /* D94/PE0 */ + {GPIOD, 8, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D95/PD8 */ + {GPIOG, 4, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D96/PG4 */ + {GPIOD, 9, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D97/PD9 */ + {GPIOG, 5, ADCx, NULL, 0, AFIO_EXTI_PG}, /* D98/PG5 */ + {GPIOD, 10, ADCx, NULL, 0, AFIO_EXTI_PD} /* D99/PD10 */ }; #elif defined(BOARD_maple_mini) PinMapping PIN_MAP[NR_GPIO_PINS] = { - /* D0/PB11 */ - {GPIOB, 11, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D1/PB10 */ - {GPIOB, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D2/PB2 */ - {GPIOB, 2, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D3/PB0 */ - {GPIOB, 0, 8, TIMER3_CH3_CCR, TIMER3, 3, AFIO_EXTI_PB}, - /* D4/PA7 */ - {GPIOA, 7, 7, TIMER3_CH2_CCR, TIMER3, 2, AFIO_EXTI_PA}, - /* D5/PA6 */ - {GPIOA, 6, 6, TIMER3_CH1_CCR, TIMER3, 1, AFIO_EXTI_PA}, - /* D6/PA5 */ - {GPIOA, 5, 5, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D7/PA4 */ - {GPIOA, 4, 4, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D8/PA3 */ - {GPIOA, 3, 3, TIMER2_CH4_CCR, TIMER2, 4, AFIO_EXTI_PA}, - /* D9/PA2 */ - {GPIOA, 2, 2, TIMER2_CH3_CCR, TIMER2, 3, AFIO_EXTI_PA}, - /* D10/PA1 */ - {GPIOA, 1, 1, TIMER2_CH2_CCR, TIMER2, 2, AFIO_EXTI_PA}, - /* D11/PA0 */ - {GPIOA, 0, 0, TIMER2_CH1_CCR, TIMER2, 1, AFIO_EXTI_PA}, - /* D12/PC15 */ - {GPIOC, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D13/PC14 */ - {GPIOC, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D14/PC13 */ - {GPIOC, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D15/PB7 */ - {GPIOB, 7, ADCx, TIMER4_CH2_CCR, TIMER4, 2, AFIO_EXTI_PB}, - /* D16/PB6 */ - {GPIOB, 6, ADCx, TIMER4_CH1_CCR, TIMER4, 1, AFIO_EXTI_PB}, - /* D17/PB5 */ - {GPIOB, 5, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D18/PB4 */ - {GPIOB, 4, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D19/PB3 */ - {GPIOB, 3, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D20/PA15 */ - {GPIOA, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D21/PA14 */ - {GPIOA, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D22/PA13 */ - {GPIOA, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D23/PA12 */ - {GPIOA, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D24/PA11 */ - {GPIOA, 11, ADCx, TIMER1_CH4_CCR, TIMER1, 4, AFIO_EXTI_PA}, - /* D25/PA10 */ - {GPIOA, 10, ADCx, TIMER1_CH3_CCR, TIMER1, 3, AFIO_EXTI_PA}, - /* D26/PA9 */ - {GPIOA, 9, ADCx, TIMER1_CH2_CCR, TIMER2, 2, AFIO_EXTI_PA}, - /* D27/PA8 */ - {GPIOA, 8, ADCx, TIMER1_CH1_CCR, TIMER1, 1, AFIO_EXTI_PA}, - /* D28/PB15 */ - {GPIOB, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D29/PB14 */ - {GPIOB, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D30/PB13 */ - {GPIOB, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D31/PB12 */ - {GPIOB, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D32/PB8 */ - {GPIOB, 8, ADCx, TIMER4_CH3_CCR, TIMER4, 3, AFIO_EXTI_PB}, - /* D33/PB1 */ - {GPIOB, 1, 9, TIMER3_CH4_CCR, TIMER3, 4, AFIO_EXTI_PB}, + /* Top header */ + + {GPIOB, 11, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D0/PB11 */ + {GPIOB, 10, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D1/PB10 */ + {GPIOB, 2, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D2/PB2 */ + {GPIOB, 0, 8, TIMER3, 3, AFIO_EXTI_PB}, /* D3/PB0 */ + {GPIOA, 7, 7, TIMER3, 2, AFIO_EXTI_PA}, /* D4/PA7 */ + {GPIOA, 6, 6, TIMER3, 1, AFIO_EXTI_PA}, /* D5/PA6 */ + {GPIOA, 5, 5, NULL, 0, AFIO_EXTI_PA}, /* D6/PA5 */ + {GPIOA, 4, 4, NULL, 0, AFIO_EXTI_PA}, /* D7/PA4 */ + {GPIOA, 3, 3, TIMER2, 4, AFIO_EXTI_PA}, /* D8/PA3 */ + {GPIOA, 2, 2, TIMER2, 3, AFIO_EXTI_PA}, /* D9/PA2 */ + {GPIOA, 1, 1, TIMER2, 2, AFIO_EXTI_PA}, /* D10/PA1 */ + {GPIOA, 0, 0, TIMER2, 1, AFIO_EXTI_PA}, /* D11/PA0 */ + {GPIOC, 15, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D12/PC15 */ + {GPIOC, 14, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D13/PC14 */ + {GPIOC, 13, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D14/PC13 */ + + /* Bottom header */ + + {GPIOB, 7, ADCx, TIMER4, 2, AFIO_EXTI_PB}, /* D15/PB7 */ + {GPIOB, 6, ADCx, TIMER4, 1, AFIO_EXTI_PB}, /* D16/PB6 */ + {GPIOB, 5, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D17/PB5 */ + {GPIOB, 4, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D18/PB4 */ + {GPIOB, 3, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D19/PB3 */ + {GPIOA, 15, ADCx, NULL, 0, AFIO_EXTI_PA}, /* D20/PA15 */ + {GPIOA, 14, ADCx, NULL, 0, AFIO_EXTI_PA}, /* D21/PA14 */ + {GPIOA, 13, ADCx, NULL, 0, AFIO_EXTI_PA}, /* D22/PA13 */ + {GPIOA, 12, ADCx, NULL, 0, AFIO_EXTI_PA}, /* D23/PA12 */ + {GPIOA, 11, ADCx, TIMER1, 4, AFIO_EXTI_PA}, /* D24/PA11 */ + {GPIOA, 10, ADCx, TIMER1, 3, AFIO_EXTI_PA}, /* D25/PA10 */ + {GPIOA, 9, ADCx, TIMER2, 2, AFIO_EXTI_PA}, /* D26/PA9 */ + {GPIOA, 8, ADCx, TIMER1, 1, AFIO_EXTI_PA}, /* D27/PA8 */ + {GPIOB, 15, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D28/PB15 */ + {GPIOB, 14, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D29/PB14 */ + {GPIOB, 13, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D30/PB13 */ + {GPIOB, 12, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D31/PB12 */ + {GPIOB, 8, ADCx, TIMER4, 3, AFIO_EXTI_PB}, /* D32/PB8 */ + {GPIOB, 1, 9, TIMER3, 4, AFIO_EXTI_PB}, /* D33/PB1 */ }; #elif defined(BOARD_maple_RET6) PinMapping PIN_MAP[NR_GPIO_PINS] = { - /* D0/PA3 */ - {GPIOA, 3, 3, TIMER2_CH4_CCR, TIMER2, 4, AFIO_EXTI_PA}, - /* D1/PA2 */ - {GPIOA, 2, 2, TIMER2_CH3_CCR, TIMER2, 3, AFIO_EXTI_PA}, - /* D2/PA0 */ - {GPIOA, 0, 0, TIMER2_CH1_CCR, TIMER2, 1, AFIO_EXTI_PA}, - /* D3/PA1 */ - {GPIOA, 1, 1, TIMER2_CH2_CCR, TIMER2, 2, AFIO_EXTI_PA}, - /* D4/PB5 */ - {GPIOB, 5, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D5/PB6 */ - {GPIOB, 6, ADCx, TIMER4_CH1_CCR, TIMER4, 1, AFIO_EXTI_PB}, - /* D6/PA8 */ - {GPIOA, 8, ADCx, TIMER1_CH1_CCR, TIMER1, 1, AFIO_EXTI_PA}, - /* D7/PA9 */ - {GPIOA, 9, ADCx, TIMER1_CH2_CCR, TIMER1, 2, AFIO_EXTI_PA}, - /* D8/PA10 */ - {GPIOA, 10, ADCx, TIMER1_CH3_CCR, TIMER1, 3, AFIO_EXTI_PA}, - /* D9/PB7 */ - {GPIOB, 7, ADCx, TIMER4_CH2_CCR, TIMER4, 2, AFIO_EXTI_PB}, - /* D10/PA4 */ - {GPIOA, 4, 4, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D11/PA7 */ - {GPIOA, 7, 7, TIMER3_CH2_CCR, TIMER3, 2, AFIO_EXTI_PA}, - /* D12/PA6 */ - {GPIOA, 6, 6, TIMER3_CH1_CCR, TIMER3, 1, AFIO_EXTI_PA}, - /* D13/PA5 */ - {GPIOA, 5, 5, 0, TIMERx, TIMERx, AFIO_EXTI_PA}, - /* D14/PB8 */ - {GPIOB, 8, ADCx, TIMER4_CH3_CCR, TIMER4, 3, AFIO_EXTI_PB}, + {GPIOA, 3, 3, TIMER2, 4, AFIO_EXTI_PA}, /* D0/PA3 */ + {GPIOA, 2, 2, TIMER2, 3, AFIO_EXTI_PA}, /* D1/PA2 */ + {GPIOA, 0, 0, TIMER2, 1, AFIO_EXTI_PA}, /* D2/PA0 */ + {GPIOA, 1, 1, TIMER2, 2, AFIO_EXTI_PA}, /* D3/PA1 */ + {GPIOB, 5, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D4/PB5 */ + {GPIOB, 6, ADCx, TIMER4, 1, AFIO_EXTI_PB}, /* D5/PB6 */ + {GPIOA, 8, ADCx, TIMER1, 1, AFIO_EXTI_PA}, /* D6/PA8 */ + {GPIOA, 9, ADCx, TIMER1, 2, AFIO_EXTI_PA}, /* D7/PA9 */ + {GPIOA, 10, ADCx, TIMER1, 3, AFIO_EXTI_PA}, /* D8/PA10 */ + {GPIOB, 7, ADCx, TIMER4, 2, AFIO_EXTI_PB}, /* D9/PB7 */ + {GPIOA, 4, 4, NULL, 0, AFIO_EXTI_PA}, /* D10/PA4 */ + {GPIOA, 7, 7, TIMER3, 2, AFIO_EXTI_PA}, /* D11/PA7 */ + {GPIOA, 6, 6, TIMER3, 1, AFIO_EXTI_PA}, /* D12/PA6 */ + {GPIOA, 5, 5, NULL, 0, AFIO_EXTI_PA}, /* D13/PA5 (LED) */ + {GPIOB, 8, ADCx, TIMER4, 3, AFIO_EXTI_PB}, /* D14/PB8 */ /* Little header */ - /* D15/PC0 */ - {GPIOC, 0, 10, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D16/PC1 */ - {GPIOC, 1, 11, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D17/PC2 */ - {GPIOC, 2, 12, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D18/PC3 */ - {GPIOC, 3, 13, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D19/PC4 */ - {GPIOC, 4, 14, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D20/PC5 */ - {GPIOC, 5, 15, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, + {GPIOC, 0, 10, NULL, 0, AFIO_EXTI_PC}, /* D15/PC0 */ + {GPIOC, 1, 11, NULL, 0, AFIO_EXTI_PC}, /* D16/PC1 */ + {GPIOC, 2, 12, NULL, 0, AFIO_EXTI_PC}, /* D17/PC2 */ + {GPIOC, 3, 13, NULL, 0, AFIO_EXTI_PC}, /* D18/PC3 */ + {GPIOC, 4, 14, NULL, 0, AFIO_EXTI_PC}, /* D19/PC4 */ + {GPIOC, 5, 15, NULL, 0, AFIO_EXTI_PC}, /* D20/PC5 */ /* External header */ - /* D21/PC13 */ - {GPIOC, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D22/PC14 */ - {GPIOC, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D23/PC15 */ - {GPIOC, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D24/PB9 */ - {GPIOB, 9, ADCx, TIMER4_CH4_CCR, TIMER4, 4, AFIO_EXTI_PB}, - /* D25/PD2 */ - {GPIOD, 2, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PD}, - /* D26/PC10 */ - {GPIOC, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PC}, - /* D27/PB0 */ - {GPIOB, 0, 8, TIMER3_CH3_CCR, TIMER3, 3, AFIO_EXTI_PB}, - /* D28/PB1 */ - {GPIOB, 1, 9, TIMER3_CH4_CCR, TIMER3, 4, AFIO_EXTI_PB}, - /* D29/PB10 */ - {GPIOB, 10, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D30/PB11 */ - {GPIOB, 11, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D31/PB12 */ - {GPIOB, 12, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D32/PB13 */ - {GPIOB, 13, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D33/PB14 */ - {GPIOB, 14, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D34/PB15 */ - {GPIOB, 15, ADCx, 0, TIMERx, TIMERx, AFIO_EXTI_PB}, - /* D35/PC6 */ - {GPIOC, 6, ADCx, TIMER8_CH1_CCR, TIMER8, 1, AFIO_EXTI_PC}, - /* D36/PC7 */ - {GPIOC, 7, ADCx, TIMER8_CH1_CCR, TIMER8, 2, AFIO_EXTI_PC}, - /* D37/PC8 */ - {GPIOC, 8, ADCx, TIMER8_CH1_CCR, TIMER8, 3, AFIO_EXTI_PC}, - /* D38/PC9 (BUT) */ - {GPIOC, 9, ADCx, TIMER8_CH1_CCR, TIMER8, 4, AFIO_EXTI_PC} + {GPIOC, 13, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D21/PC13 */ + {GPIOC, 14, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D22/PC14 */ + {GPIOC, 15, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D23/PC15 */ + {GPIOB, 9, ADCx, TIMER4, 4, AFIO_EXTI_PB}, /* D24/PB9 */ + {GPIOD, 2, ADCx, NULL, 0, AFIO_EXTI_PD}, /* D25/PD2 */ + {GPIOC, 10, ADCx, NULL, 0, AFIO_EXTI_PC}, /* D26/PC10 */ + {GPIOB, 0, 8, TIMER3, 3, AFIO_EXTI_PB}, /* D27/PB0 */ + {GPIOB, 1, 9, TIMER3, 4, AFIO_EXTI_PB}, /* D28/PB1 */ + {GPIOB, 10, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D29/PB10 */ + {GPIOB, 11, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D30/PB11 */ + {GPIOB, 12, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D31/PB12 */ + {GPIOB, 13, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D32/PB13 */ + {GPIOB, 14, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D33/PB14 */ + {GPIOB, 15, ADCx, NULL, 0, AFIO_EXTI_PB}, /* D34/PB15 */ + {GPIOC, 6, ADCx, TIMER8, 1, AFIO_EXTI_PC}, /* D35/PC6 */ + {GPIOC, 7, ADCx, TIMER8, 2, AFIO_EXTI_PC}, /* D36/PC7 */ + {GPIOC, 8, ADCx, TIMER8, 3, AFIO_EXTI_PC}, /* D37/PC8 */ + {GPIOC, 9, ADCx, TIMER8, 4, AFIO_EXTI_PC} /* D38/PC9 (BUT) */ }; #else diff --git a/wirish/boards.h b/wirish/boards.h index 94566fa..d186dc4 100644 --- a/wirish/boards.h +++ b/wirish/boards.h @@ -33,7 +33,8 @@ #include "libmaple.h" #include "gpio.h" -#include "timers.h" +#include "timer.h" +#include "native_sram.h" /* Set of all possible pin names; not all boards have all these (note that we use the Dx convention since all of the Maple's pins are @@ -56,9 +57,8 @@ typedef struct PinMapping { gpio_dev *gpio_device; uint32 pin; uint32 adc_channel; - TimerCCR timer_ccr; - timer_dev_num timer_num; - uint32 timer_chan; + timer_dev* timer_device; + uint8 timer_chan; afio_exti_port ext_port; } PinMapping; @@ -94,6 +94,7 @@ extern PinMapping PIN_MAP[]; #define NR_GPIO_PINS 100 #define BOARD_INIT do { \ + initNativeSRAM(); } while(0) #elif defined(BOARD_maple_mini) diff --git a/wirish/comm/HardwareSPI.cpp b/wirish/comm/HardwareSPI.cpp index 20090f5..aea7734 100644 --- a/wirish/comm/HardwareSPI.cpp +++ b/wirish/comm/HardwareSPI.cpp @@ -47,8 +47,10 @@ * TODO: Do the complementary PWM outputs mess up SPI2? * */ -#include "wirish.h" #include "spi.h" +#include "timer.h" + +#include "wirish.h" #include "HardwareSPI.h" static const uint32 prescaleFactors[MAX_SPI_FREQS] = { diff --git a/wirish/comm/HardwareSerial.cpp b/wirish/comm/HardwareSerial.cpp index 08252d8..97a5ec3 100644 --- a/wirish/comm/HardwareSerial.cpp +++ b/wirish/comm/HardwareSerial.cpp @@ -31,12 +31,10 @@ #include "wirish.h" #include "HardwareSerial.h" #include "usart.h" -#include "gpio.h" -#include "timers.h" -HardwareSerial Serial1(USART1, 4500000UL, GPIOA, 9,10, TIMER1, 2); -HardwareSerial Serial2(USART2, 2250000UL, GPIOA, 2, 3, TIMER2, 3); -HardwareSerial Serial3(USART3, 2250000UL, GPIOB, 10,11, TIMER_INVALID, 0); +HardwareSerial Serial1(USART1, 4500000UL, GPIOA, 9, 10, TIMER1, 2); +HardwareSerial Serial2(USART2, 2250000UL, GPIOA, 2, 3, TIMER2, 3); +HardwareSerial Serial3(USART3, 2250000UL, GPIOB, 10, 11, NULL, 0); // TODO: High density device ports HardwareSerial::HardwareSerial(uint8 usart_num, @@ -44,15 +42,15 @@ HardwareSerial::HardwareSerial(uint8 usart_num, gpio_dev *gpio_device, uint8 tx_pin, uint8 rx_pin, - timer_dev_num timer_num, - uint8 compare_num) { + timer_dev *timer_device, + uint8 channel_num) { this->usart_num = usart_num; this->max_baud = max_baud; this->gpio_device = gpio_device; this->tx_pin = tx_pin; this->rx_pin = rx_pin; - this->timer_num = timer_num; - this->compare_num = compare_num; + this->timer_device = timer_device; + this->channel_num = channel_num; } uint8 HardwareSerial::read(void) { @@ -75,9 +73,9 @@ void HardwareSerial::begin(uint32 baud) { gpio_set_mode(gpio_device, tx_pin, GPIO_AF_OUTPUT_PP); gpio_set_mode(gpio_device, rx_pin, GPIO_INPUT_FLOATING); - if (timer_num != TIMER_INVALID) { + if (timer_device != NULL) { /* turn off any pwm if there's a conflict on this usart */ - timer_set_mode(timer_num, compare_num, TIMER_DISABLED); + timer_set_mode(timer_device, channel_num, TIMER_DISABLED); } usart_init(usart_num, baud); diff --git a/wirish/comm/HardwareSerial.h b/wirish/comm/HardwareSerial.h index ef19a56..7852d51 100644 --- a/wirish/comm/HardwareSerial.h +++ b/wirish/comm/HardwareSerial.h @@ -31,7 +31,9 @@ #ifndef _HARDWARESERIAL_H_ #define _HARDWARESERIAL_H_ -#include "timers.h" +#include "libmaple_types.h" +#include "gpio.h" +#include "timer.h" #include "Print.h" @@ -49,16 +51,16 @@ class HardwareSerial : public Print { gpio_dev *gpio_device; uint8 tx_pin; uint8 rx_pin; - timer_dev_num timer_num; - uint8 compare_num; + timer_dev *timer_device; + uint8 channel_num; public: HardwareSerial(uint8 usart_num, uint32 max_baud, gpio_dev *gpio_device, uint8 tx_pin, uint8 rx_pin, - timer_dev_num timer_num, - uint8 compare_num); + timer_dev *timer_device, + uint8 channel_num); void begin(uint32 baud); void end(void); uint32 available(void); diff --git a/wirish/pwm.cpp b/wirish/pwm.cpp index 072e4cd..6b09cef 100644 --- a/wirish/pwm.cpp +++ b/wirish/pwm.cpp @@ -23,26 +23,20 @@ *****************************************************************************/ /** - * @brief Arduino-compatible PWM implementation. + * @brief Arduino-style PWM implementation. */ -#include "wirish.h" -#include "timers.h" -#include "io.h" +#include "libmaple_types.h" +#include "timer.h" + +#include "boards.h" #include "pwm.h" void pwmWrite(uint8 pin, uint16 duty_cycle) { - TimerCCR ccr; - - if (pin >= NR_GPIO_PINS) { - return; - } - - ccr = PIN_MAP[pin].timer_ccr; - - if (ccr == 0) { + timer_dev *dev = PIN_MAP[pin].timer_device; + if (pin >= NR_GPIO_PINS || dev == NULL || dev->type == TIMER_BASIC) { return; } - timer_pwm_write_ccr(ccr, duty_cycle); + timer_set_compare(dev, PIN_MAP[pin].timer_chan, duty_cycle); } diff --git a/wirish/rules.mk b/wirish/rules.mk index 3dd4b2d..d250cb9 100644 --- a/wirish/rules.mk +++ b/wirish/rules.mk @@ -18,7 +18,6 @@ cppSRCS_$(d) := wirish_math.cpp \ comm/HardwareSerial.cpp \ comm/HardwareSPI.cpp \ usb_serial.cpp \ - HardwareTimer.cpp \ cxxabi-compat.cpp \ wirish.cpp \ wirish_shift.cpp \ diff --git a/wirish/wirish.cpp b/wirish/wirish.cpp index 65d0262..6dcb1b5 100644 --- a/wirish/wirish.cpp +++ b/wirish/wirish.cpp @@ -23,61 +23,107 @@ *****************************************************************************/ /** - * @brief generic maple board bring up: + * @brief Generic Maple board initialization. * - * By default, we bring up all maple boards running on the stm32 to 72mhz, - * clocked off the PLL, driven by the 8MHz external crystal. - * - * AHB and APB2 are clocked at 72MHz - * APB1 is clocked at 36MHz + * By default, we bring up all Maple boards to 72MHz, clocked off the + * PLL, driven by the 8MHz external crystal. AHB and APB2 are clocked + * at 72MHz. APB1 is clocked at 36MHz. */ #include "wirish.h" + +#include "flash.h" +#include "rcc.h" +#include "nvic.h" #include "systick.h" #include "gpio.h" -#include "nvic.h" +#include "adc.h" +#include "timer.h" #include "usb.h" -#include "rcc.h" -#include "fsmc.h" -#include "dac.h" -#include "flash.h" -#include "native_sram.h" +static void setupFlash(void); +static void setupClocks(void); +static void setupADC(void); +static void setupTimers(void); + +/** + * Board-wide initialization function. Called before main(). + */ void init(void) { - /* make sure the flash is ready before spinning the high speed clock up */ + setupFlash(); + setupClocks(); + nvic_init(); + systick_init(SYSTICK_RELOAD_VAL); + gpio_init_all(); + afio_init(); + setupADC(); + setupTimers(); + setupUSB(); + + BOARD_INIT; +} + +static void setupFlash(void) { flash_enable_prefetch(); flash_set_latency(FLASH_WAIT_STATE_2); +} -#ifdef BOARD_maple_native - initNativeSRAM(); -#endif - - /* initialize clocks */ +/* + * Clock setup. Note that some of this only takes effect if we're + * running bare metal and the bootloader hasn't done it for us + * already. + * + * If you change this function, you MUST change the file-level Doxygen + * comment above. + */ +static void setupClocks() { rcc_clk_init(RCC_CLKSRC_PLL, RCC_PLLSRC_HSE, RCC_PLLMUL_9); rcc_set_prescaler(RCC_PRESCALER_AHB, RCC_AHB_SYSCLK_DIV_1); rcc_set_prescaler(RCC_PRESCALER_APB1, RCC_APB1_HCLK_DIV_2); rcc_set_prescaler(RCC_PRESCALER_APB2, RCC_APB2_HCLK_DIV_1); +} - nvic_init(); - systick_init(SYSTICK_RELOAD_VAL); - gpio_init_all(); - afio_init(); - - /* Initialize the ADC for slow conversions, to allow for high - impedance inputs. */ +/* TODO initialize more ADCs on high density boards. */ +static void setupADC() { adc_init(ADC1, 0); - adc_set_sample_rate(ADC1, ADC_SMPR_55_5); - - timer_init(TIMER1, 1); - timer_init(TIMER2, 1); - timer_init(TIMER3, 1); - timer_init(TIMER4, 1); -#ifdef STM32_HIGH_DENSITY - timer_init(TIMER5, 1); - timer_init(TIMER8, 1); -#endif - setupUSB(); + adc_set_sample_rate(ADC1, ADC_SMPR_55_5); // for high impedance inputs +} - /* include the board-specific init macro */ - BOARD_INIT; +static void timerDefaultConfig(timer_dev*); + +static void setupTimers() { + timer_foreach(timerDefaultConfig); +} + +static void timerDefaultConfig(timer_dev *dev) { + timer_adv_reg_map *regs = (dev->regs).adv; + const uint16 full_overflow = 0xFFFF; + const uint16 half_duty = 0x8FFF; + + timer_init(dev); + timer_pause(dev); + + regs->CR1 = TIMER_CR1_ARPE; + regs->PSC = 1; + regs->SR = 0; + regs->DIER = 0; + regs->EGR = TIMER_EGR_UG; + + switch (dev->type) { + case TIMER_ADVANCED: + regs->BDTR = TIMER_BDTR_MOE | TIMER_BDTR_LOCK_OFF; + // fall-through + case TIMER_GENERAL: + timer_set_reload(dev, full_overflow); + + for (int channel = 1; channel <= 4; channel++) { + timer_set_compare(dev, channel, half_duty); + timer_oc_set_mode(dev, channel, TIMER_OC_MODE_PWM_1, TIMER_OC_PE); + } + // fall-through + case TIMER_BASIC: + break; + } + + timer_resume(dev); } diff --git a/wirish/wirish.h b/wirish/wirish.h index 447b2b6..319df97 100644 --- a/wirish/wirish.h +++ b/wirish/wirish.h @@ -32,7 +32,6 @@ #define _WIRISH_H_ #include "libmaple.h" -#include "timers.h" #include "boards.h" #include "io.h" @@ -44,7 +43,6 @@ #include "HardwareSPI.h" #include "HardwareSerial.h" #include "usb_serial.h" -#include "HardwareTimer.h" /* Arduino wiring macros and bit defines */ #define HIGH 0x1 @@ -69,7 +67,6 @@ typedef uint8 boolean; typedef uint8 byte; void init(void); -void shiftOut(uint8 dataPin, uint8 clockPin, uint8 bitOrder, byte val); #endif diff --git a/wirish/wirish_digital.cpp b/wirish/wirish_digital.cpp index 0c0bd85..4b68861 100644 --- a/wirish/wirish_digital.cpp +++ b/wirish/wirish_digital.cpp @@ -72,15 +72,15 @@ void pinMode(uint8 pin, WiringPinMode mode) { gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].pin, outputMode); - if (PIN_MAP[pin].timer_num != TIMER_INVALID) { + if (PIN_MAP[pin].timer_device != NULL) { /* enable/disable timer channels if we're switching into or out of pwm */ if (pwm) { - timer_set_mode(PIN_MAP[pin].timer_num, + timer_set_mode(PIN_MAP[pin].timer_device, PIN_MAP[pin].timer_chan, TIMER_PWM); } else { - timer_set_mode(PIN_MAP[pin].timer_num, + timer_set_mode(PIN_MAP[pin].timer_device, PIN_MAP[pin].timer_chan, TIMER_DISABLED); } |