diff options
-rw-r--r-- | libmaple/timer.c | 30 | ||||
-rw-r--r-- | notes/timers.txt | 152 |
2 files changed, 90 insertions, 92 deletions
diff --git a/libmaple/timer.c b/libmaple/timer.c index a4e4032..b22ffb4 100644 --- a/libmaple/timer.c +++ b/libmaple/timer.c @@ -32,12 +32,6 @@ #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; @@ -51,7 +45,7 @@ #define NR_BAS_HANDLERS 1 static timer_dev timer1 = { - .regs = {.adv = TIMER1_BASE}, + .regs = { .adv = TIMER1_BASE }, .clk_id = RCC_TIMER1, .type = TIMER_ADVANCED, .handlers = { [NR_ADV_HANDLERS - 1] = 0 }, @@ -59,7 +53,7 @@ static timer_dev timer1 = { timer_dev *TIMER1 = &timer1; static timer_dev timer2 = { - .regs = {.gen = TIMER2_BASE}, + .regs = { .gen = TIMER2_BASE }, .clk_id = RCC_TIMER2, .type = TIMER_GENERAL, .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, @@ -67,7 +61,7 @@ static timer_dev timer2 = { timer_dev *TIMER2 = &timer2; static timer_dev timer3 = { - .regs = {.gen = TIMER3_BASE}, + .regs = { .gen = TIMER3_BASE }, .clk_id = RCC_TIMER3, .type = TIMER_GENERAL, .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, @@ -75,7 +69,7 @@ static timer_dev timer3 = { timer_dev *TIMER3 = &timer3; static timer_dev timer4 = { - .regs = {.gen = TIMER4_BASE}, + .regs = { .gen = TIMER4_BASE }, .clk_id = RCC_TIMER4, .type = TIMER_GENERAL, .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, @@ -84,7 +78,7 @@ timer_dev *TIMER4 = &timer4; #ifdef STM32_HIGH_DENSITY static timer_dev timer5 = { - .regs = {.gen = TIMER5_BASE}, + .regs = { .gen = TIMER5_BASE }, .clk_id = RCC_TIMER5, .type = TIMER_GENERAL, .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, @@ -92,7 +86,7 @@ static timer_dev timer5 = { timer_dev *TIMER5 = &timer5; static timer_dev timer6 = { - .regs = {.bas = TIMER6_BASE}, + .regs = { .bas = TIMER6_BASE }, .clk_id = RCC_TIMER6, .type = TIMER_BASIC, .handlers = { [NR_BAS_HANDLERS - 1] = 0 }, @@ -100,7 +94,7 @@ static timer_dev timer6 = { timer_dev *TIMER6 = &timer6; static timer_dev timer7 = { - .regs = {.bas = TIMER7_BASE}, + .regs = { .bas = TIMER7_BASE }, .clk_id = RCC_TIMER7, .type = TIMER_BASIC, .handlers = { [NR_BAS_HANDLERS - 1] = 0 }, @@ -108,7 +102,7 @@ static timer_dev timer7 = { timer_dev *TIMER7 = &timer7; static timer_dev timer8 = { - .regs = {.adv = TIMER8_BASE}, + .regs = { .adv = TIMER8_BASE }, .clk_id = RCC_TIMER8, .type = TIMER_ADVANCED, .handlers = { [NR_ADV_HANDLERS - 1] = 0 }, @@ -168,7 +162,7 @@ void timer_disable(timer_dev *dev) { * @param mode New timer mode for channel */ void timer_set_mode(timer_dev *dev, uint8 channel, timer_mode mode) { - ASSERT(channel > 0 && channel <= 4); + ASSERT_FAULT(channel > 0 && channel <= 4); /* TODO decide about the basic timers */ ASSERT(dev->type != TIMER_BASIC); @@ -351,8 +345,8 @@ static inline void dispatch_cc_irqs(timer_dev *dev) { uint32 sr_clear = 0; uint32 b; - ASSERT(sr & (TIMER_SR_CC1IF | TIMER_SR_CC2IF | - TIMER_SR_CC3IF | TIMER_SR_CC4IF)); + ASSERT_FAULT(sr & (TIMER_SR_CC1IF | TIMER_SR_CC2IF | + TIMER_SR_CC3IF | TIMER_SR_CC4IF)); for (b = TIMER_SR_CC1IF_BIT; b <= TIMER_SR_CC4IF_BIT; b++) { uint32 mask = BIT(b); @@ -443,7 +437,7 @@ static void enable_nonmuxed_irq(timer_dev *dev) { break; #endif default: - ASSERT(0); + ASSERT_FAULT(0); break; } } diff --git a/notes/timers.txt b/notes/timers.txt index 3f5b9f4..647e92e 100644 --- a/notes/timers.txt +++ b/notes/timers.txt @@ -1,82 +1,87 @@ +Timers +====== -Each timer (1-4) has 4 capture/compare channels (1-4). These are directly used -by PWM but have a ton of other possible functionality. The STM32 implementation -is particularly complicated with, eg, the ability to chain together timers - -Timer1 is an "advanced timer" with many more features. I think if we use just -the "Capture and compare interrupt", and enable MOE during initialization -everything will be ok. There are seperate Break, Update, and Trigger interrupts -as well that we will ignore for now. - -Timer2,Ch 3+4 are D0 and D1, which conflict with Serial2. USART should work -fine as long as pins aren't in output mode? and timers should work fine if -Serial2 isn't in use? - -Caveats ------------------------------------------------------------------------------- -There are probably subtle bugs with the way register settings get read in; eg, -it is often required to have the counter fully overflow before new settings -come into effect? -Getting really good timing is still an art, not a science here... usually you -need to fiddle with an oscilloscope and the exact overflow/compare numbers to -get just the right time values. -Any other interrupts (SysTick, USB, Serial, etc) can blow away all the nice -timing stuff. - -Misc Notes ------------------------------------------------------------------------------- -Implementation with case/switch in the interrupt handlers doesn't work; a lot -of the time multiple interrupt flags are active at the same time (or become -active?) +Medium-density chips have timers 1 through 4. High- and XL-density +chips additionally have timers 5 through 8. XL-density chips +additionally have timers 9--14, which we don't support yet. + +Timer Capabilities +------------------ + +Each of timers 1--4 has 4 capture/compare (C/C) channels (also numbered +1--4). These are directly used by PWM, but may serve other purposes as +well (including handling user-specified periodic interrupts). The +STM32 implementation is particularly featureful, with, e.g., the +ability to chain together timers. + +Timers 1 and 8 are an advanced timers, with many more features. +Wirish just uses just their capture/compare interrupts and enables MOE +during initialization, essentially treating them as general purpose +timers (like timers 2--5). Advanced timers also have separate break, +update, and trigger interrupts that we only provide low-level +(i.e. libmaple proper) support for. + +Timers 6 and 7 are basic timers, without C/C channels. They are still +useful for interrupts (via NVIC_TIMER6, NVIC_TIMER7 IRQs, which can +fire upon an update event), but they're most useful for controlling +periodic DAC output. + +Known Issues and Other Caveats +------------------------------ + +There are some conflicts between timer C/C outputs and USART 1 and 2 +TX/RX. Wirish tries to handle this gracefully, but (as of 7 April +2011) not all the bugs are sorted yet. In particular, if you call +HardwareSerial::disable(), then try to use PWM, the USART TX pins +don't cooperate. + +Resetting the prescaler or reload value only takes effect at the next +update event. You can use timer_generate_update() to generate an +update event via software. + +Other interrupts (SysTick, USB, Serial, etc.) can interfere with +timing-critical applications. If your program requires precise +timing, you should probably at least disable USB and SysTick. Note +that this also disables the bootloader and stops millis()/micros() +from counting. + +Getting really good timing is a bit of an art. If things don't work +at first, you need to fiddle with an oscilloscope and the exact +overflow/compare numbers to get precise behavior. TODO ------------------------------------------------------------------------------- -- document carefully (eg, determine clock-wise and overflow-wise behavior for - each function) -- track down and handle all pin conflicts -- implement the update interrupt as a "5th channel" -- "pulse in" stuff, both c and c++ -- function to read out CCR registers -- allow comparison output to the pin (a la PWM) -- additional modes and configuration (up, down, up/down, etc) - -Possible Wirish implementation ------------------------------------------------------------------------------- -Inspired by Timer1 Library for arduino -http://arduino.cc/pipermail/developers_arduino.cc/2010-June/002845.html +---- + +- Document more carefully (e.g., determine clock-wise and + overflow-wise behavior for each function). - class HardwareTimer { - - public: - - void pause(); - void resume(); - void setPrescaleFactor(uint8 factor); - void setOverflow(uint16 val); // truncates to overflow - void setCount(uint16 val); // truncates to overflow - uint16 getCount(); - uint16 setPeriod(uint32 microseconds); // tries to set prescaler and overflow wisely; returns overflow - void setMode(uint8 mode); - void setCompare1(uint16 val); // truncates to overflow - void setCompare2(uint16 val); // truncates to overflow - void setCompare3(uint16 val); // truncates to overflow - void setCompare4(uint16 val); // truncates to overflow - void attachCompare1Interrupt(void (*f)(void)); - void attachCompare2Interrupt(void (*f)(void)); - void attachCompare3Interrupt(void (*f)(void)); - void attachCompare4Interrupt(void (*f)(void)); - void detachCompare1Interrupt(); - void detachCompare2Interrupt(); - void detachCompare3Interrupt(); - void detachCompare4Interrupt(); - }; - - HardwareTimer Timer1 = HardwareTimer(1); - HardwareTimer Timer2 = HardwareTimer(2); - HardwareTimer Timer3 = HardwareTimer(3); - HardwareTimer Timer4 = HardwareTimer(4); +- Track down and handle pin conflicts. + +- Input capture interface. DON'T WRITE pulseIn() IN TERMS OF THIS. + Do that as a simple, Arduino style implementation that just + busy-waits and uses micros(), to allow a pulseIn() on arbitrary + pins. Eventually, expose the more precise/harder to use timer-based + API via a convenience library. + +- Complementary outputs, with convenient break/dead time interface. + +- Additional modes (center-aligned PWM, one pulse mode, etc.) and + count configuration (down, up/down). + +Alternative Wirish Implementations +---------------------------------- + +The current Wirish API is big and clunky. Its inclusion by default +also threatens making everyone's sketches bigger unnecessarily. We +need to deprecate the parts of it that are bad for 0.0.10, and remove +them when 0.1.0 comes out. + +Current implementation was inspired by Timer1 Library for Arduino: + +http://arduino.cc/pipermail/developers_arduino.cc/2010-June/002845.html Here's one of the more standard libaries out there: + http://www.arduino.cc/playground/Code/Timer1 void initialize(long microseconds=1000000); @@ -89,4 +94,3 @@ http://www.arduino.cc/playground/Code/Timer1 void disablePwm(char pin); void attachInterrupt(void (*isr)(), long microseconds=-1); void detachInterrupt(); - |