From 1f98566c939c84a986ee8b60fde6aa601c3521da Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Tue, 26 Apr 2011 03:27:10 -0400 Subject: 0.0.10 Documentation checkpoint. The vast majority of the Maple-specific values have been pulled out of the higher-level overview pages and replaced with refs into documents under /docs/source/hardware/. Much of the work that's left to be done in this regard is labeled with versioned TODO and FIXME comments. Suggestions from StephenFromNYC and gbulmer were incorporated from this forum thread: http://forums.leaflabs.com/topic.php?id=703 --- docs/source/timers.rst | 206 +++++++++++++------------------------------------ 1 file changed, 55 insertions(+), 151 deletions(-) (limited to 'docs/source/timers.rst') diff --git a/docs/source/timers.rst b/docs/source/timers.rst index 56dd686..cb30081 100644 --- a/docs/source/timers.rst +++ b/docs/source/timers.rst @@ -5,6 +5,8 @@ Timers ====== +.. FIXME [0.0.10] links to systick.h in a few places on this page + There are four general purpose timers in the Maple microcontroller that can be configured to generate periodic or delayed events with minimal work done by the microcontroller. For example, the :ref:`PWM @@ -43,164 +45,66 @@ event" interrupt is generated. You can configure the Maple to notify you when this takes place, by registering an interrupt handler, which is a function that will be called when the update event occurs. -libmaple Reference +Function Reference ------------------ -The libmaple API for interacting with timers is documented at the -:ref:`HardwareTimer reference `. +* :ref:`HardwareTimer ` Caveats ------- +Working with timers and interrupts can be tricky; they are a somewhat +"advanced" topic. The following subsections explain some common +problems associated with using timers and timer interrupts. + +In general: start simple, test with :ref:`ASSERT() `, +and don't try to do too much in your interrupt handlers! Make sure +that what you're trying to do in a handler isn't going to block other +interrupts from firing, if those other interrupts are important for +your program. + .. _timers-pwm-conflicts: -**PWM Conflicts:** Because PWM functionality on a given pin depends on +PWM Conflicts +^^^^^^^^^^^^^ + +Because PWM functionality on a given pin depends on the configuration of the timer and channel, you must chose your channels carefully if you want to use both timer interrupts and PWM in -the same program. Refer to the following table to match up timer -channels and Maple header pin numbers: - -.. _timers-pin-channel-map: - -.. csv-table:: - :header: Timer, Ch. 1 pin, Ch. 2 pin, Ch. 3 pin, Ch. 4 pin - - ``Timer1``, 6, 7, 8, -- - ``Timer2``, 2, 3, 1, 0 - ``Timer3``, 12, 11, 27, 28 - ``Timer4``, 5, 9, 14, 24 - -**Overhead:** there is some overhead associated with function and -interrupt calls (loading and unloading the stack, preparing state, -etc.) and this overhead can fudge your timing. Imperfect code -branching also means that, e.g., channel 1 interrupts may get called a -couple clock cycles sooner than a channel 4 interrupt, all other -configuration being the same. - -.. compound:: - - **Jitter:** other interrupts (USB, Serial, SysTick, or other - timers) can and will get called before or during the timer - interrupt routines, causing pseudorandom delays and other - frustrations. - - Disabling the USB port (by calling ``SerialUSB.end()``, or just - running off a battery) helps a lot, but then you lose the - auto-reset and communications functionality. This will require - that you put your Maple into :ref:`perpetual bootloader mode - ` before uploading a new - program to it (or somehow causing your program to re-enable serial - over USB using :ref:`SerialUSB.begin() `). - - Disabling SysTick with ``systick_disable()`` helps as well. - However, calling this function will break the ``micros()`` and - ``millis()`` functions. - -**General:** working with timers and interrupts can be tricky and hard -to debug; they are a somewhat "advanced" topic. Start simple, test -with :ref:`ASSERT() `, and don't try to do too much -in your interrupt handlers! Make sure that what you're trying to do in -a handler isn't going to block other interrupts from firing (e.g. USB, -Serial, SysTick) if those other interrupts are important for your -program. - -SysTick Peripheral ------------------- - -The SysTick peripheral allows another, simple way to perform periodic -or delayed events. This separate timer does not conflict with any -other peripherals, but the associated 1kHz interrupt can jitter the -general purpose timer interrupts; this is clearly seen when running -VGA code, where the timing jitters are transformed into visual jags in -the image. The SysTick peripheral can be disabled by calling -``systick_disable()``, and re-enabled using ``systick_resume()``. - -Code Examples -------------- - -LED blink -^^^^^^^^^ - -:: - - #define LED_RATE 500000 // in microseconds; should give 0.5Hz toggles - - void handler_led(void); - - void setup() - { - // Set up the LED to blink - pinMode(BOARD_LED_PIN, OUTPUT); - - // Setup Timer - Timer2.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer2.setPeriod(LED_RATE); // in microseconds - Timer2.setCompare1(1); // overflow might be small - Timer2.attachCompare1Interrupt(handler_led); - } - - void loop() { - // Nothing! It's all in the interrupts - } - - void handler_led(void) { - toggleLED(); - } - -Racing Counters -^^^^^^^^^^^^^^^ - -:: - - void handler_count1(void); - void handler_count2(void); - - int count1 = 0; - int count2 = 0; - - void setup() - { - // Set up BUT for input - pinMode(BOARD_BUTTON_PIN, INPUT_PULLUP); - - // Setup Counting Timers - 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); // somewhere in the middle - Timer4.setCompare1(1000); - Timer3.attachCompare1Interrupt(handler1); - Timer4.attachCompare1Interrupt(handler2); - Timer3.resume(); - Timer4.resume(); - } - - void loop() { - // Display the running counts - SerialUSB.print("Count 1: "); - SerialUSB.print(count1); - SerialUSB.print("\t\tCount 2: "); - SerialUSB.println(count2); - - // Run... while BUT is held, pause Count2 - for(int i = 0; i<1000; i++) { - if(digitalRead(BOARD_BUTTON_PIN)) { - Timer4.pause(); - } else { - Timer4.resume(); - } - delay(1); - } - } - - void handler1(void) { - count1++; - } - void handler2(void) { - count2++; - } +the same program. Refer to your board's :ref:`Timer Pin Map +` to match up timer channels and pin numbers. + +Overhead +^^^^^^^^ + +There is some overhead associated with function and interrupt calls +(loading and unloading the stack, preparing state, etc.) and this +overhead can fudge your timing. Imperfect code branching also means +that, e.g., channel 1 interrupts may get called a couple clock cycles +sooner than a channel 4 interrupt, all other configuration being the +same. + +Jitter +^^^^^^ + +Other interrupts can and will get called before or during the timer +interrupt routines, causing pseudorandom delays and other +frustrations. + +Disabling the :ref:`USB ` port (by calling ``SerialUSB.end()``, +or just running off a battery) helps a lot, but then you lose the +auto-reset and communications functionality. This will require that +you put your Maple into :ref:`perpetual bootloader mode +` before uploading a new program +to it (or somehow causing your program to re-enable serial over USB +using :ref:`SerialUSB.begin() `). + +The SysTick peripheral another way to perform periodic or delayed +events. Its separate timer does not conflict with any other +peripherals, but the associated 1 kHz interrupt can jitter the general +purpose timer interrupts. The SysTick peripheral can be disabled by +calling :ref:`systick_disable() `, and +re-enabled using :ref:`systick_resume() `. +However, be aware that calling ``systick_disable()`` will stop the +values coming from :ref:`lang-micros` and :ref:`lang-millis` from +increasing. -- cgit v1.2.3 From 6765e0eaf8da078195373e297acc9dff6dde7b9e Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Thu, 5 May 2011 17:18:25 -0400 Subject: Putting updated HardwareTimer back into the build. HardwareTimer was removed from the build when the timer refactor was done; this redoes it in terms of the new timer.h interface. A variety of conflicting or badly designed bits were deprecated or removed. I'm still not satisfied with this interface, as it's going to make life difficult moving forward to high-density chips, where the addition of basic timers means that the capture/compare methods won't apply in some cases. However, we need to get 0.0.10 out the door, so it'll have to do for now. The docs are up to date, and contain a warning that the Wirish API isn't stable and a recommendation to use libmaple proper. --- docs/source/lang/api/analogwrite.rst | 24 +- docs/source/lang/api/hardwaretimer.rst | 579 +++++++++++++-------------------- docs/source/timers.rst | 10 + wirish/HardwareTimer.cpp | 217 ++++-------- wirish/HardwareTimer.h | 430 ++++++++++-------------- wirish/rules.mk | 1 + wirish/wirish.h | 1 + 7 files changed, 500 insertions(+), 762 deletions(-) (limited to 'docs/source/timers.rst') diff --git a/docs/source/lang/api/analogwrite.rst b/docs/source/lang/api/analogwrite.rst index e789305..dd2192a 100644 --- a/docs/source/lang/api/analogwrite.rst +++ b/docs/source/lang/api/analogwrite.rst @@ -52,7 +52,7 @@ you much more precise control over the duty cycle of your PWM output. If you're porting code from the Arduino and want a quick-and-dirty fix, one solution is to :ref:`map ` the argument to -analogWrite into the right range:: +analogWrite() into the right range:: // Arduino code: analogWrite(pin, duty); @@ -71,8 +71,8 @@ that Timer's overflow to 255. Subsequent calls to analogWrite() should work as on the Arduino (with the same loss of precision). Note, however, that that affects the overflow for the **entire timer**, so other code relying on that timer (such as any -:ref:`interrupts ` the timer controls) will -likely need to be modified as well. +:ref:`interrupts ` the timer controls) +will likely need to be modified as well. Difference 2: You must use pinMode() to set up PWM ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -141,23 +141,27 @@ the steps are: 1. Figure out which :ref:`timer ` controls PWM output on your pin (\ :ref:`your board's Timer Pin Map - ` is your friend here). Let's say it's ``Timern``\ - , where ``n`` is some number 1, 2, 3, or 4. + ` is your friend here). -2. Call ``Timern.setPeriod(2041)``\ . This will set the timer's - period to approximately 2041 microseconds, which is a frequency of - approximately 490 Hz. +2. Let's say it's timer ``n``, where ``n`` is some number. You'll + then need to put "``HardwareTimer timer(n);``" with your variables, + as described in the :ref:`HardwareTimer + ` reference. + +3. In your :ref:`lang-setup`, put "``timer.setPeriod(2041);``". This + will set the timer's period to approximately 2041 microseconds, + which is a frequency of approximately 490 Hz. Be aware that this will change the period for the **entire timer**\ , and will affect anything else in your program that depends on that timer. The important examples are :ref:`timer interrupts -` and :ref:`PWM +` and :ref:`PWM `\ . See Also -------- -- :ref:`Maple PWM tutorial ` +- :ref:`pwm` .. rubric:: Footnotes diff --git a/docs/source/lang/api/hardwaretimer.rst b/docs/source/lang/api/hardwaretimer.rst index 526beb6..09245f0 100644 --- a/docs/source/lang/api/hardwaretimer.rst +++ b/docs/source/lang/api/hardwaretimer.rst @@ -5,456 +5,341 @@ HardwareTimer ============= -This class defines the public API for interfacing with the STM32's -built-in timer peripherals. More information on these peripherals -(including code examples) is available in the :ref:`timers reference -`. +This page describes how to control the built-in timers. It does not +describe how the timers work on your board. For more information on +that, the :ref:`timers reference `. -.. FIXME [0.0.10] Updated HardwareTimer documentation, with deprecation +.. warning:: The timer interface is still taking shape, and is + expected to change significantly between releases. Because of + that, the functionality described in this page shouldn't be + considered stable. -.. warning:: This class has been deprecated. It is not available in - the current build. + If you want a timer API that will be consistent between releases of + the Maple IDE, your best bet for now is to use the low-level + support in :ref:`libmaple-timer`. -Library Documentation ---------------------- +.. contents:: Contents + :local: -HardwareTimer Class Reference -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. _lang-hardwaretimer-getting-started: -To interact with a particular timer, call one of the methods -documented below on one of the predefined ``HardwareTimer`` instances. -For example, to set the prescale factor on timer 1 to 5, call -``Timer1.setPrescaleFactor(5)``. +Getting Started +--------------- -.. TODO add tutorial-style examples +You'll first need to define a ``HardwareTimer`` variable, which you'll +use to control the timer. Do this by putting the line +"``HardwareTimer timer(number);``" with your variables, where +``number`` is the timer's number. -.. cpp:class:: HardwareTimer +Here's an example (we'll fill in :ref:`setup() ` and +:ref:`loop() ` later):: - Class for interacting with a timer. There are four predefined - instances available on the Maple: ``Timer1``, ``Timer2``, - ``Timer3``, and ``Timer4``. + // Use timer 1 + HardwareTimer timer(1); -.. _lang-hardwaretimer-attachinterrupt: + void setup() { + // Your setup code + } -.. cpp:function:: void HardwareTimer::attachInterrupt(int channel, voidFuncPtr handler) + void loop() { + // ... + } - Attach an interrupt handler to the given ``channel``. This - interrupt handler will be called when the timer's counter reaches - the given channel :ref:`compare ` - value. +Configuring the Prescaler and Overflow +-------------------------------------- - ``handler`` should be a function which takes no arguments and has - :ref:`void ` value; i.e. it should have signature :: +After defining your ``timer`` variable, you'll probably want to +configure how fast your timer's counter changes (using the prescaler) +and when it gets reset to zero (using the overflow value). You can do +that with the ``setPrescaleFactor()`` and ``setOverflow()`` functions. - void handler(void); - - You can later detach the interrupt using :ref:`detachInterrupt() - `. - - .. note:: The function (often called an *interrupt service - routine*, or ISR) should attempt to return as quickly as - possible. :ref:`Blinking the LED `, some - logic, :ref:`PWM ` updates, and :ref:`Serial - ` writes are fine; writing to - :ref:`SerialUSB ` or :ref:`waiting - ` for user input can take a long - time and prevent other interrupts from firing on time. - - Tip: if you have a :ref:`delay() ` in your - ISR, you're probably doing it wrong. - -.. cpp:function:: void HardwareTimer::attachCompare1Interrupt(voidFuncPtr handler) - - Equivalent to :ref:`attachInterrupt - `\ ``(1, handler)``. - -.. cpp:function:: void HardwareTimer::attachCompare2Interrupt(voidFuncPtr handler) - - Equivalent to :ref:`attachInterrupt - `\ ``(2, handler)``. - -.. cpp:function:: void HardwareTimer::attachCompare3Interrupt(voidFuncPtr handler) - - Equivalent to :ref:`attachInterrupt - `\ ``(3, handler)``. - -.. cpp:function:: void HardwareTimer::attachCompare4Interrupt(voidFuncPtr handler) - - Equivalent to :ref:`attachInterrupt - `\ ``(4, handler)``. - -.. _lang-hardwaretimer-setchannelmode: - -.. cpp:function:: void HardwareTimer::setChannelMode(int channel, TimerMode mode) - - Set the given channel of this timer to the given :ref:`mode - `. The parameter ``channel`` is one of - 1, 2, 3, and 4, and corresponds to the compare channel you would - like to set. Refer to your board's :ref:`master pin map - ` to match up timer channels and pin numbers. - -.. cpp:function:: void HardwareTimer::setChannel1Mode(TimerMode mode) - - Equivalent to :ref:`setChannelMode `\ - ``(1, mode)``. - -.. cpp:function:: void HardwareTimer::setChannel2Mode(TimerMode mode) - - Equivalent to :ref:`setChannelMode `\ - ``(2, mode)``. - -.. cpp:function:: void HardwareTimer::setChannel3Mode(TimerMode mode) - - Equivalent to :ref:`setChannelMode `\ - ``(3, mode)``. - -.. cpp:function:: void HardwareTimer::setChannel4Mode(TimerMode mode) - - Equivalent to :ref:`setChannelMode `\ - ``(4, mode)``. - -.. _lang-hardwaretimer-getcompare: - -.. cpp:function:: uint16 HardwareTimer::getCompare(int channel) - - Gets the compare value for the given ``channel``, from 1 to 4. See - :ref:`setCompare() `. - -.. cpp:function:: uint16 HardwareTimer::getCompare1() - - Equivalent to :ref:`getCompare `\ - ``(1, mode)``. - -.. cpp:function:: uint16 HardwareTimer::getCompare2() - - Equivalent to :ref:`getCompare `\ - ``(2, mode)``. - -.. cpp:function:: uint16 HardwareTimer::getCompare3() - - Equivalent to :ref:`getCompare `\ - ``(3, mode)``. - -.. cpp:function:: uint16 HardwareTimer::getCompare4() +.. _lang-hardwaretimer-setprescalefactor: - Equivalent to :ref:`getCompare `\ - ``(4, mode)``. +.. doxygenfunction:: HardwareTimer::setPrescaleFactor + :no-link: -.. _lang-hardwaretimer-setcompare: +.. _lang-hardwaretimer-setoverflow: -.. cpp:function:: void HardwareTimer::setCompare(int channel, uint16 compare) +.. doxygenfunction:: HardwareTimer::setOverflow + :no-link: - Sets the compare value for the given ``channel`` to ``compare``. - If ``compare`` is greater than this timer's overflow value, it will - be truncated to the overflow value. The default compare value is - 65,535 (the largest unsigned 16-bit integer value). +For example:: - When the counter reaches this value the interrupt for this channel - will fire if the given ``channel`` :ref:`mode - ` is ``TIMER_OUTPUTCOMPARE`` and - an interrupt is :ref:`attached - `. + // Use timer 1 + HardwareTimer timer(1); - By default, this only changes the relative offsets between events - on a single timer ("phase"); they don't control the frequency with - which they occur. However, a common trick is to increment the - compare value manually in the interrupt handler so that the event - will fire again after the increment period. There can be a - different increment value for each channel, so this trick allows - events to be programmed at 4 different rates on a single - timer. Note that function call overheads mean that the smallest - increment rate is at least a few microseconds. + void setup() { + timer.setPrescaleFactor(5); + timer.setOverflow(255); + } -.. cpp:function:: void HardwareTimer::setCompare1(uint16 compare) + void loop() { + // ... + } - Equivalent to :ref:`setCompare `\ - ``(1, compare)``. +You may also find the ``setPeriod()`` function useful: -.. cpp:function:: void HardwareTimer::setCompare2(uint16 compare) +.. _lang-hardwaretimer-setperiod: - Equivalent to :ref:`setCompare `\ - ``(2, compare)``. +.. doxygenfunction:: HardwareTimer::setPeriod + :no-link: -.. cpp:function:: void HardwareTimer::setCompare3(uint16 compare) +For example:: - Equivalent to :ref:`setCompare `\ - ``(3, compare)``. + // Use timer 1 + HardwareTimer timer(1); -.. cpp:function:: void HardwareTimer::setCompare4(uint16 compare) + void setup() { + // Have the timer repeat every 20 milliseconds + int microseconds_per_millisecond = 1000; + timer.setPeriod(20 * microseconds_per_millisecond); + } - Equivalent to :ref:`setCompare `\ - ``(4, compare)``. + void loop() { + // ... + } -.. cpp:function:: uint16 HardwareTimer::getCount() +.. _lang-hardwaretimer-interrupts: - Gets the current timer count. Due to function call overhead, the - return value will be increasingly accurate with smaller prescale - values. Also see :ref:`setCount() `. +Using Timer Interrupts +---------------------- -.. _lang-hardwaretimer-setcount: +.. TODO [0.2.0] Improve the interrupts section, here or in timers.rst -.. cpp:function:: void HardwareTimer::setCount(uint16 val) +In order to use timer interrupts, we recommend the following sequence: - Set the timer's current count to ``val``. +* Pause the timer. +* Configure the prescaler and overflow. +* Pick a timer channel to handle the interrupt and set the channel's + :ref:`mode ` to ``TIMER_OUTPUT_COMPARE``. +* Set the channel compare value appropriately (this controls what counter value, + from 0 to overflow - 1). If you just want to make the interrupt fire once + every time the timer overflows, and you don't care what the timer count is, + the channel compare value can just be 1. +* Attach an interrupt handler to the channel. +* Refresh the timer. +* Resume the timer. - Note that there is some function call overhead associated with - calling this method, so using it is not a robust way to get - multiple timers to share a count value. +Here are two complete examples. - If ``val`` exceeds the timer's :ref:`overflow value - `, it is truncated to the overflow - value. +**LED blink**: This example blinks the built-in LED without doing +anything in ``loop()``. :: + #define LED_RATE 500000 // in microseconds; should give 0.5Hz toggles -.. _lang-hardwaretimer-detachinterrupt: - -.. cpp:function:: void HardwareTimer::detachInterrupt(int channel) + // We'll use timer 2 + HardwareTimer timer(2); - Remove the interrupt handler attached to the given ``channel``, if - any. The handler will no longer be called by this timer. + void setup() { + // Set up the LED to blink + pinMode(BOARD_LED_PIN, OUTPUT); -.. cpp:function:: void HardwareTimer::detachCompare1Interrupt() + // Pause the timer while we're configuring it + timer.pause(); - Equivalent to :ref:`detachInterrupt - `\ ``(1)``. + // Set up period + timer.setPeriod(LED_RATE); // in microseconds -.. cpp:function:: void HardwareTimer::detachCompare2Interrupt() + // Set up an interrupt on channel 1 + timer.setChannel1Mode(TIMER_OUTPUT_COMPARE); + timer.setCompare(TIMER_CH1, 1); // Interrupt 1 count after each update + timer.attachCompare1Interrupt(handler_led); - Equivalent to :ref:`detachInterrupt - `\ ``(2)``. + // Refresh the timer's count, prescale, and overflow + timer.refresh(); -.. cpp:function:: void HardwareTimer::detachCompare3Interrupt() + // Start the timer counting + timer.resume(); + } - Equivalent to :ref:`detachInterrupt - `\ ``(3)``. + void loop() { + // Nothing! It's all in the handler_led() interrupt: + } -.. cpp:function:: void HardwareTimer::detachCompare4Interrupt() + void handler_led(void) { + toggleLED(); + } - Equivalent to :ref:`detachInterrupt - `\ ``(4)``. +**Racing Counters**: This example shows how to use multiple timers at +the same time. :: -.. _lang-hardwaretimer-generateupdate: + int count3 = 0; + int count4 = 0; -.. cpp:function:: void HardwareTimer::generateUpdate() + // We'll use timers 3 and 4 + HardwareTimer timer3(3); + HardwareTimer timer4(4); - Re-initializes the counter (to 0 in upcounting mode, which is the - default), and generates an update of the prescale and overflow - registers. + void setup() { + // Set up the button for input + pinMode(BOARD_BUTTON_PIN, INPUT_PULLUP); -.. _lang-hardwaretimer-getoverflow: + // Set up timers to add 1 to their counts each time + // their interrupts fire. + timer3.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer4.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer3.pause(); + timer4.pause(); + timer3.setCount(0); + timer4.setCount(0); + timer3.setOverflow(30000); + timer4.setOverflow(30000); + timer3.setCompare(TIMER_CH1, 1000); // somewhere in the middle + timer4.setCompare(TIMER_CH1, 1000); + timer3.attachCompare1Interrupt(handler3); + timer4.attachCompare1Interrupt(handler4); + timer3.refresh(); + timer4.refresh(); + timer3.resume(); + timer4.resume(); + } -.. cpp:function:: uint16 HardwareTimer::getOverflow() + void loop() { + // Display the running counts + SerialUSB.print("Count 3: "); + SerialUSB.print(count3); + SerialUSB.print("\t\tCount 4: "); + SerialUSB.println(count4); + + // While the button is held down, pause timer 4 + for (int i = 0; i < 1000; i++) { + if (digitalRead(BOARD_BUTTON_PIN)) { + timer4.pause(); + } else { + timer4.resume(); + } + delay(1); + } + } - Gets the timer's overflow value. See :ref:`setOverflow() - `. + void handler3(void) { + count3++; + } -.. _lang-hardwaretimer-setoverflow: + void handler4(void) { + count4++; + } -.. cpp:function:: void HardwareTimer::setOverflow(uint16 val) +``HardwareTimer`` Class Reference +--------------------------------- - Sets the timer overflow (or "reload") value to ``val``. +This section gives a full listing of the capabilities of a +``HardwareTimer``. - When the timer's counter reaches this, value it resets to - zero. Its default value is 65535 (the largest unsigned 16-bit - integer); setting the overflow to anything lower will cause - interrupts to be called more frequently (see :ref:`setPeriod() - ` function for a shortcut). +.. doxygenclass:: HardwareTimer + :members: HardwareTimer, pause, resume, getPrescaleFactor, setPrescaleFactor, getOverflow, setOverflow, getCount, setCount, setPeriod, setMode, getCompare, setCompare, attachInterrupt, detachInterrupt, refresh - After the next :ref:`timer update - `, this number will be the - maximum value for the timer's channel compare values. +.. _lang-hardwaretimer-timermode: -.. _lang-hardwaretimer-pause: +.. doxygenenum:: timer_mode -.. cpp:function:: void HardwareTimer::pause() +Deprecated Functionality +------------------------ - Stop the timer's counter, without affecting its configuration. +The following functionality exists for now, but it has been +deprecated, and will be removed in a future Maple IDE release. You +shouldn't use it in new programs, and you should change any of your +programs which do use them to use the up-to-date features described +above. - The timer will no longer count or fire interrupts after this - function is called, until it is resumed. This function is useful - during timer setup periods, in order to prevent interrupts from - firing before the timer is fully configured. +The ``TimerMode`` type from previous releases has been renamed +``timer_mode``. The mode ``TIMER_OUTPUTCOMPARE`` is still present, +but will be removed in a future release. Use ``TIMER_OUTPUT_COMPARE`` +instead. - Note that there is some function call overhead associated with this - method, so using it in concert with :ref:`resume() - ` is not a robust way to align multiple - timers to the same count value. +.. cpp:function:: void HardwareTimer::attachCompare1Interrupt(voidFuncPtr handler) -.. _lang-hardwaretimer-setperiod: + Use ``attachInterrupt(1, handler)`` instead. -.. cpp:function:: uint16 HardwareTimer::setPeriod(uint32 microseconds) +.. cpp:function:: void HardwareTimer::attachCompare2Interrupt(voidFuncPtr handler) - Configure the :ref:`prescaler - ` and :ref:`overflow - ` values to generate a timer reload - with a period as close to the given number of ``microseconds`` as - possible. + Use ``attachInterrupt(2, handler)`` instead. - The return value is the new overflow value, which may be used to - set channel compare values. However, if a clock that fires an - interrupt every given number of microseconds is all that is - desired, and the relative "phases" are unimportant, channel compare - values may all be set to 1. +.. cpp:function:: void HardwareTimer::attachCompare3Interrupt(voidFuncPtr handler) -.. _lang-hardwaretimer-getprescalefactor: + Use ``attachInterrupt(3, handler)`` instead. -.. cpp:function:: uint16 HardwareTimer::getPrescaleFactor() +.. cpp:function:: void HardwareTimer::attachCompare4Interrupt(voidFuncPtr handler) - Returns the timer's prescale factor. See - :ref:`setPrescaleFactor() `. + Use ``attachInterrupt(4, handler)`` instead. -.. _lang-hardwaretimer-setprescalefactor: +.. _lang-hardwaretimer-setchannelmode: -.. cpp:function:: void HardwareTimer::setPrescaleFactor(uint16 factor) +.. cpp:function:: void HardwareTimer::setChannelMode(int channel, timer_mode mode) - Set the timer's prescale factor to ``factor``. + Use ``setMode(channel, mode)`` instead. - The prescaler acts as a clock divider to slow down the rate at - which the counter increments. +.. cpp:function:: void HardwareTimer::setChannel1Mode(timer_mode mode) - For example, the system clock rate is 72MHz, so the counter will - reach 65535 in (13.89 nanoseconds) × (65535 counts) = (910.22 - microseconds), or about a thousand times a second. If the - prescaler equals 1098, then the clock rate is effectively 72MHz / - 1098 = 65.56KHz, and the counter will reach 65536 in (15.25 - microseconds) × (65536 counts) = (0.999 seconds), or about once - per second. + Use ``setMode(1, mode)`` instead. - The :ref:`setPeriod() ` method may - also be used as a convenient alternative. +.. cpp:function:: void HardwareTimer::setChannel2Mode(timer_mode mode) -.. _lang-hardwaretimer-resume: + Use ``setMode(2, mode)`` instead. -.. cpp:function:: void HardwareTimer::resume() +.. cpp:function:: void HardwareTimer::setChannel3Mode(timer_mode mode) - Resume a paused timer, without affecting its configuration. + Use ``setMode(3, mode)`` instead. - The timer will resume counting and firing interrupts as - appropriate. +.. cpp:function:: void HardwareTimer::setChannel4Mode(timer_mode mode) - Note that there is some function call overhead associated with - using this method, so using it in concert with :ref:`pause() - ` is not a robust way to align multiple - timers to the same count value. + Use ``setMode(4, mode)`` instead. -.. cpp:function:: timer_dev_num HardwareTimer::getTimerNum() +.. cpp:function:: uint16 HardwareTimer::getCompare1() - Returns the :ref:`timer device number - ` associated with the timer. For - example, ``Timer1.getTimerNum()`` would return ``TIMER1``. + Use ``getCompare(1, mode)`` instead. - In most cases, you should not need to use this function. If you do - use it, be careful; the constant ``TIMER1`` is *not equal* to the - number 1; similarly, ``TIMER2`` is *not* the number 2, etc. Be - sure to refer to the timer device number by name. +.. cpp:function:: uint16 HardwareTimer::getCompare2() -.. _lang-hardwaretimer-modes: + Use ``getCompare(2, mode)`` instead. -Timer Modes -^^^^^^^^^^^ -.. doxygenenum:: TimerMode +.. cpp:function:: uint16 HardwareTimer::getCompare3() -.. _lang-hardwaretimer-timer-dev-num: + Use ``getCompare(3, mode)`` instead. -Timer Device Numbers -^^^^^^^^^^^^^^^^^^^^ +.. cpp:function:: uint16 HardwareTimer::getCompare4() -These provide a lower-level interface for interacting with timers. -They are mostly useful in context with the :ref:`getTimer() -` function. **Be careful** when using -these not to confuse e.g. ``TIMER1`` with the number 1; they are -different. + Use ``getCompare(4, mode)`` instead. -.. doxygenenum:: timer_dev_num +.. cpp:function:: void HardwareTimer::setCompare1(uint16 compare) -.. _lang-hardwaretimer-convenience: + Use ``setCompare(1, compare)`` instead. -.. _lang-hardwaretimer-gettimer: +.. cpp:function:: void HardwareTimer::setCompare2(uint16 compare) -Other Functions -^^^^^^^^^^^^^^^ -.. doxygenfunction:: getTimer + Use ``setCompare(2, compare)`` instead. -Examples -^^^^^^^^ +.. cpp:function:: void HardwareTimer::setCompare3(uint16 compare) -**LED blink**:: + Use ``setCompare(3, compare)`` instead. - #define LED_RATE 500000 // in microseconds; should give 0.5Hz toggles +.. cpp:function:: void HardwareTimer::setCompare4(uint16 compare) - void handler_led(void); + Use ``setCompare(4, compare)`` instead. - void setup() - { - // Set up the LED to blink - pinMode(BOARD_LED_PIN, OUTPUT); +.. cpp:function:: void HardwareTimer::detachCompare1Interrupt() - // Setup Timer - Timer2.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer2.setPeriod(LED_RATE); // in microseconds - Timer2.setCompare1(1); // overflow might be small - Timer2.attachCompare1Interrupt(handler_led); - } + Use ``detachInterrupt(1)`` instead. - void loop() { - // Nothing! It's all in the interrupts - } +.. cpp:function:: void HardwareTimer::detachCompare2Interrupt() - void handler_led(void) { - toggleLED(); - } + Use ``detachInterrupt(2)`` instead. -**Racing Counters**:: +.. cpp:function:: void HardwareTimer::detachCompare3Interrupt() - void handler_count1(void); - void handler_count2(void); + Use ``detachInterrupt(3)`` instead. - int count1 = 0; - int count2 = 0; +.. cpp:function:: void HardwareTimer::detachCompare4Interrupt() - void setup() - { - // Set up BUT for input - pinMode(BOARD_BUTTON_PIN, INPUT_PULLUP); + Use ``detachInterrupt(4)`` instead. - // Setup Counting Timers - 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); // somewhere in the middle - Timer4.setCompare1(1000); - Timer3.attachCompare1Interrupt(handler1); - Timer4.attachCompare1Interrupt(handler2); - Timer3.resume(); - Timer4.resume(); - } +.. cpp:function:: void HardwareTimer::generateUpdate() - void loop() { - // Display the running counts - SerialUSB.print("Count 1: "); - SerialUSB.print(count1); - SerialUSB.print("\t\tCount 2: "); - SerialUSB.println(count2); - - // Run... while BUT is held, pause Count2 - for(int i = 0; i<1000; i++) { - if(digitalRead(BOARD_BUTTON_PIN)) { - Timer4.pause(); - } else { - Timer4.resume(); - } - delay(1); - } - } + Use ``refresh()`` instead. - void handler1(void) { - count1++; - } - void handler2(void) { - count2++; - } +In previous releases, to interact with a particular timers, you would +use one of the predefined ``HardwareTimer`` instances ``Timer1``, +``Timer2``, ``Timer3``, and ``Timer4``. These are still available for +now, but they are also deprecated, and will be removed in a future +release. As detailed in :ref:`lang-hardwaretimer-getting-started`, +you should define your own ``HardwareTimer`` variables. diff --git a/docs/source/timers.rst b/docs/source/timers.rst index cb30081..9163e69 100644 --- a/docs/source/timers.rst +++ b/docs/source/timers.rst @@ -45,6 +45,16 @@ event" interrupt is generated. You can configure the Maple to notify you when this takes place, by registering an interrupt handler, which is a function that will be called when the update event occurs. +By default, different compare values only change the relative offsets +between events on a single timer ("phase"). They don't control the +frequency with which they occur. However, a common trick is to +increment the compare value manually in the interrupt handler so that +the event will fire again after the increment period. There can be a +different increment value for each channel, so this trick allows +events to be programmed at 4 different rates on a single timer. Note +that function call overheads mean that the smallest increment rate is +at least a few microseconds. + Function Reference ------------------ diff --git a/wirish/HardwareTimer.cpp b/wirish/HardwareTimer.cpp index 04d1c76..d0e32c3 100644 --- a/wirish/HardwareTimer.cpp +++ b/wirish/HardwareTimer.cpp @@ -22,204 +22,125 @@ * THE SOFTWARE. *****************************************************************************/ -/* - * wirish timer class to manage the four 16-bit timer peripherals - */ - -#include "wirish.h" #include "HardwareTimer.h" +#include "boards.h" // for CYCLES_PER_MICROSECOND +#include "wirish_math.h" -HardwareTimer::HardwareTimer(timer_dev_num timerNum) { - ASSERT(timerNum != TIMER_INVALID); +// TODO [0.1.0] Remove deprecated pieces - this->timerNum = timerNum; -} +#ifdef STM32_MEDIUM_DENSITY +#define NR_TIMERS 4 +#elif defined(STM32_HIGH_DENSITY) +#define NR_TIMERS 8 +#else +#error "Unsupported density" +#endif -void HardwareTimer::resume(void) { - timer_resume(this->timerNum); +#define MAX_RELOAD ((1 << 16) - 1) + +HardwareTimer::HardwareTimer(uint8 timerNum) { + if (timerNum > NR_TIMERS) { + ASSERT(0); + } + timer_dev *devs[] = { + TIMER1, + TIMER2, + TIMER3, + TIMER4, +#ifdef STM32_HIGH_DENSITY + TIMER5, + TIMER6, + TIMER7, + TIMER8, +#endif + }; + this->dev = devs[timerNum - 1]; } void HardwareTimer::pause(void) { - timer_pause(this->timerNum); + timer_pause(this->dev); } -uint16 HardwareTimer::getPrescaleFactor(void) { - return timer_get_prescaler(this->timerNum) + 1; +void HardwareTimer::resume(void) { + timer_resume(this->dev); +} + +uint32 HardwareTimer::getPrescaleFactor(void) { + return timer_get_prescaler(this->dev) + 1; } -void HardwareTimer::setPrescaleFactor(uint16 factor) { - // The prescaler register is zero-indexed - timer_set_prescaler(this->timerNum, factor-1); +void HardwareTimer::setPrescaleFactor(uint32 factor) { + timer_set_prescaler(this->dev, (uint16)(factor - 1)); } uint16 HardwareTimer::getOverflow() { - return timer_get_reload(this->timerNum); + return timer_get_reload(this->dev); } void HardwareTimer::setOverflow(uint16 val) { - timer_set_reload(this->timerNum, val); + timer_set_reload(this->dev, val); } uint16 HardwareTimer::getCount(void) { - return timer_get_count(this->timerNum); + return timer_get_count(this->dev); } void HardwareTimer::setCount(uint16 val) { uint16 ovf = this->getOverflow(); - timer_set_count(this->timerNum, min(val, ovf)); + timer_set_count(this->dev, min(val, ovf)); } +// FIXME [0.0.10 beta] test! uint16 HardwareTimer::setPeriod(uint32 microseconds) { // Not the best way to handle this edge case? - if(!microseconds) { - setPrescaleFactor(1); - setOverflow(1); + if (!microseconds) { + this->setPrescaleFactor(1); + this->setOverflow(1); return this->getOverflow(); } - uint32 cycles = microseconds * CYCLES_PER_MICROSECOND; - - // With a prescale factor of 1, there are CYCLES_PER_MICROSECOND - // counts/ms - uint16 ps = (uint16)((cycles >> 16) + 1); - setPrescaleFactor(ps); - - // Finally, this overflow will always be less than 65536 - setOverflow((cycles/ps) - 1); - - return this->getOverflow(); -} -void HardwareTimer::setChannelMode(int channel, TimerMode mode) { - timer_set_mode(this->timerNum, channel, mode); + uint32 period_cyc = microseconds * CYCLES_PER_MICROSECOND; + uint16 prescaler = (uint16)(period_cyc / MAX_RELOAD); + uint16 overflow = (uint16)round(period_cyc / prescaler); + this->setPrescaleFactor(prescaler); + this->setOverflow(overflow); + return overflow; } -void HardwareTimer::setChannel1Mode(TimerMode mode) { - this->setChannelMode(1, mode); -} - -void HardwareTimer::setChannel2Mode(TimerMode mode) { - this->setChannelMode(2, mode); -} - -void HardwareTimer::setChannel3Mode(TimerMode mode) { - this->setChannelMode(3, mode); -} - -void HardwareTimer::setChannel4Mode(TimerMode mode) { - this->setChannelMode(4, mode); +void HardwareTimer::setMode(int channel, timer_mode mode) { + timer_set_mode(this->dev, (uint8)channel, (timer_mode)mode); } uint16 HardwareTimer::getCompare(int channel) { - return timer_get_compare_value(this->timerNum, channel); -} - -uint16 HardwareTimer::getCompare1() { - return this->getCompare(1); -} - -uint16 HardwareTimer::getCompare2() { - return this->getCompare(2); -} - -uint16 HardwareTimer::getCompare3() { - return this->getCompare(3); -} - -uint16 HardwareTimer::getCompare4() { - return this->getCompare(4); + return timer_get_compare(this->dev, (uint8)channel); } void HardwareTimer::setCompare(int channel, uint16 val) { uint16 ovf = this->getOverflow(); - timer_set_compare_value(this->timerNum, channel, min(val, ovf)); -} - -void HardwareTimer::setCompare1(uint16 val) { - this->setCompare(1, val); -} - -void HardwareTimer::setCompare2(uint16 val) { - this->setCompare(2, val); -} - -void HardwareTimer::setCompare3(uint16 val) { - this->setCompare(3, val); -} - -void HardwareTimer::setCompare4(uint16 val) { - this->setCompare(4, val); + timer_set_compare(this->dev, (uint8)channel, min(val, ovf)); } void HardwareTimer::attachInterrupt(int channel, voidFuncPtr handler) { - timer_attach_interrupt(this->timerNum, channel, handler); -} - -void HardwareTimer::attachCompare1Interrupt(voidFuncPtr handler) { - this->attachInterrupt(1, handler); -} - -void HardwareTimer::attachCompare2Interrupt(voidFuncPtr handler) { - this->attachInterrupt(2, handler); -} - -void HardwareTimer::attachCompare3Interrupt(voidFuncPtr handler) { - this->attachInterrupt(3, handler); -} - -void HardwareTimer::attachCompare4Interrupt(voidFuncPtr handler) { - this->attachInterrupt(4, handler); + timer_attach_interrupt(this->dev, (uint8)channel, handler); } void HardwareTimer::detachInterrupt(int channel) { - timer_detach_interrupt(this->timerNum, channel); -} - -void HardwareTimer::detachCompare1Interrupt(void) { - this->detachInterrupt(1); -} - -void HardwareTimer::detachCompare2Interrupt(void) { - this->detachInterrupt(2); -} - -void HardwareTimer::detachCompare3Interrupt(void) { - this->detachInterrupt(3); + timer_detach_interrupt(this->dev, (uint8)channel); } -void HardwareTimer::detachCompare4Interrupt(void) { - this->detachInterrupt(4); +void HardwareTimer::refresh(void) { + timer_generate_update(this->dev); } -void HardwareTimer::generateUpdate(void) { - timer_generate_update(this->timerNum); -} +/* -- Deprecated predefined instances -------------------------------------- */ -HardwareTimer Timer1(TIMER1); -HardwareTimer Timer2(TIMER2); -HardwareTimer Timer3(TIMER3); -HardwareTimer Timer4(TIMER4); +HardwareTimer Timer1(1); +HardwareTimer Timer2(2); +HardwareTimer Timer3(3); +HardwareTimer Timer4(4); #ifdef STM32_HIGH_DENSITY -HardwareTimer Timer5(TIMER5); // High-density devices only -HardwareTimer Timer8(TIMER8); // High-density devices only +HardwareTimer Timer5(5); +HardwareTimer Timer6(6); +HardwareTimer Timer7(7); +HardwareTimer Timer8(8); #endif - -HardwareTimer* getTimer(timer_dev_num timerNum) { - switch (timerNum) { - case TIMER1: - return &Timer1; - case TIMER2: - return &Timer2; - case TIMER3: - return &Timer3; - case TIMER4: - return &Timer4; -#ifdef STM32_HIGH_DENSITY - case TIMER5: - return &Timer5; - case TIMER8: - return &Timer8; -#endif - default: - return 0; - } -} diff --git a/wirish/HardwareTimer.h b/wirish/HardwareTimer.h index 4030adc..fd8ca9a 100644 --- a/wirish/HardwareTimer.h +++ b/wirish/HardwareTimer.h @@ -23,66 +23,45 @@ *****************************************************************************/ /** - * @brief wirish timer class to manage the four 16-bit timer peripherals + * @brief Wirish timer class. */ #ifndef _HARDWARETIMER_H_ #define _HARDWARETIMER_H_ -#include "timers.h" +// TODO [0.1.0] Remove deprecated pieces, pick a better API + +#include "timer.h" + +/** Timer mode. */ +typedef timer_mode TimerMode; + +/** @brief Deprecated; use TIMER_OUTPUT_COMPARE instead. */ +#define TIMER_OUTPUTCOMPARE TIMER_OUTPUT_COMPARE /** - * Interface to one of the 16-bit timer peripherals. - * - * User code should not instantiate this class directly; instead, use - * one of the predefined Timer instances (Timer1, Timer2, etc.). - * - * HardwareTimer instances can be configured to generate periodic or - * delayed events with minimal work done by the microcontroller. Each - * timer maintains a single 16-bit count that can be configured with a - * prescaler and overflow value. - * - * By default, a timer's counter is incremented once per clock cycle. - * The prescaler acts as a divider of the 72MHz Maple system clock; - * without prescaling, the timer's count would reach 65535 (2**16-1) - * and roll over over 1000 times per second. - * - * The overflow value is the maximum value the counter will reach. It - * defaults to 65535; smaller values will cause the counter to reset - * more frequently. + * @brief Interface to one of the 16-bit timer peripherals. */ class HardwareTimer { - private: - timer_dev_num timerNum; - - public: - HardwareTimer(timer_dev_num timer_num); +private: + timer_dev *dev; +public: /** - * Return this timer's device number. For example, - * Timer1.getTimerNum() == TIMER1 + * @brief Construct a new HardwareTimer instance. + * @param timerNum number of the timer to control. */ - timer_dev_num getTimerNum() { return timerNum; } + HardwareTimer(uint8 timerNum); /** - * Stop the counter, without affecting its configuration. - * - * The timer will no longer count or fire interrupts after this - * function is called, until it is resumed. This function is - * useful during timer setup periods, in order to prevent - * interrupts from firing before the timer is fully configured. - * - * Note that there is some function call overhead associated with - * this method, so using it in concert with - * HardwareTimer::resume() is not a robust way to align multiple - * timers to the same count value. + * @brief Stop the counter, without affecting its configuration. * * @see HardwareTimer::resume() */ void pause(void); /** - * Resume a paused timer, without affecting its configuration. + * @brief Resume a paused timer, without affecting its configuration. * * The timer will resume counting and firing interrupts as * appropriate. @@ -97,66 +76,51 @@ class HardwareTimer { void resume(void); /** - * Returns the timer's prescale factor. + * @brief Get the timer's prescale factor. + * @return Timer prescaler, from 1 to 65,536. * @see HardwareTimer::setPrescaleFactor() */ - uint16 getPrescaleFactor(); + uint32 getPrescaleFactor(); /** - * Set the timer's prescale factor. + * @brief Set the timer's prescale factor. * - * The prescaler acts as a clock divider to slow down the rate at - * which the counter increments. + * The new value won't take effect until the next time the counter + * overflows. You can force the counter to reset using + * HardwareTimer::refresh(). * - * For example, the system clock rate is 72MHz, so the counter - * will reach 65535 in (13.89 nanoseconds) * (65535 counts) = - * (910.22 microseconds), or about a thousand times a second. If - * the prescaler equals 1098, then the clock rate is effectively - * 65.56KHz, and the counter will reach 65536 in (15.25 - * microseconds) * (65536 counts) = (0.999 seconds), or about once - * per second. - * - * The HardwareTimer::setPeriod() method may also be used as a - * convenient alternative. - * - * @param factor The new prescale value to set. - * @see HardwareTimer::setPeriod() + * @param factor The new prescale value to set, from 1 to 65,536. + * @see HardwareTimer::refresh() */ - void setPrescaleFactor(uint16 factor); + void setPrescaleFactor(uint32 factor); /** - * Gets the timer overflow value. + * @brief Get the timer overflow value. * @see HardwareTimer::setOverflow() */ uint16 getOverflow(); /** - * Sets the timer overflow (or "reload") value. + * @brief Set the timer overflow (or "reload") value. * - * When the timer's counter reaches this, value it resets to - * zero. Its default value is 65535 (the largest unsigned 16-bit - * integer); setting the overflow to anything lower will cause - * interrupts to be called more frequently (see the setPeriod() - * function below for a shortcut). This number sets the maximum - * value for the channel compare values. + * The new value won't take effect until the next time the counter + * overflows. You can force the counter to reset using + * HardwareTimer::refresh(). * * @param val The new overflow value to set + * @see HardwareTimer::refresh() */ void setOverflow(uint16 val); /** - * Retrieve the current timer count. + * @brief Get the current timer count. * * @return The timer's current count value */ uint16 getCount(void); /** - * Set the current timer count. - * - * Note that there is some function call overhead associated with - * calling this method, so using it is not a robust way to get - * multiple timers to share a count value. + * @brief Set the current timer count. * * @param val The new count value to set. If this value exceeds * the timer's overflow value, it is truncated to the @@ -165,143 +129,50 @@ class HardwareTimer { void setCount(uint16 val); /** - * Configure the prescaler and overflow values to generate a timer + * @brief Set the timer's period in microseconds. + * + * Configures the prescaler and overflow values to generate a timer * reload with a period as close to the given number of * microseconds as possible. * - * The return value is the overflow, which may be used to set - * channel compare values. However, if a clock that fires an - * interrupt every given number of microseconds is all that is - * desired, and the relative "phases" are unimportant, channel - * compare values may all be set to 1. - * - * @param microseconds the desired period of the timer. - * @return the overflow value (and thus, the largest value that can be - * set as a compare). + * @param microseconds The desired period of the timer. This must be + * greater than zero. + * @return The new overflow value. */ uint16 setPeriod(uint32 microseconds); /** - * Set the given channel of this timer to the given mode. - * + * @brief Configure a timer channel's mode. * @param channel Timer channel, from 1 to 4 * @param mode Mode to set */ - void setChannelMode(int channel, TimerMode mode); - - /** - * Set channel 1 of this timer to the given mode. - * - * Note: Timer1.setChannel1Mode(TIMER_PWM) may not work as - * expected; if you want PWM functionality on a channel make sure - * you don't set it to something else! - * - * @see TimerMode - */ - void setChannel1Mode(TimerMode mode); - - /** - * Set channel 2 of this timer to the given mode. - * @see TimerMode - */ - void setChannel2Mode(TimerMode mode); - - /** - * Set channel 3 of this timer to the given mode. - * @see TimerMode - */ - void setChannel3Mode(TimerMode mode); - - /** - * Set channel 4 of this timer to the given mode. - * @see TimerMode - */ - void setChannel4Mode(TimerMode mode); + void setMode(int channel, timer_mode mode); /** - * Gets the compare value for the given channel. + * @brief Get the compare value for the given channel. * @see HardwareTimer::setCompare() */ uint16 getCompare(int channel); - /** Equivalent to getCompare(1) */ - uint16 getCompare1(); - - /** Equivalent to getCompare(2) */ - uint16 getCompare2(); - - /** Equivalent to getCompare(3) */ - uint16 getCompare3(); - - /** Equivalent to getCompare(4) */ - uint16 getCompare4(); - /** - * Sets the compare value for the given channel. - * - * When the counter reaches this value the interrupt for this - * channel will fire if the channel mode is TIMER_OUTPUTCOMPARE - * and an interrupt is attached. - * - * By default, this only changes the relative offsets between - * events on a single timer ("phase"); they don't control the - * frequency with which they occur. However, a common trick is to - * increment the compare value manually in the interrupt handler - * so that the event will fire again after the increment - * period. There can be a different increment value for each - * channel, so this trick allows events to be programmed at 4 - * different rates on a single timer. Note that function call - * overheads mean that the smallest increment rate is at least a - * few microseconds. + * @brief Set the compare value for the given channel. * * @param channel the channel whose compare to set, from 1 to 4. * @param compare The compare value to set. If greater than this * timer's overflow value, it will be truncated to * the overflow value. * - * @see TimerMode - * @see HardwareTimer::setChannelMode() + * @see timer_mode + * @see HardwareTimer::setMode() * @see HardwareTimer::attachInterrupt() */ void setCompare(int channel, uint16 compare); /** - * Equivalent to setCompare(1, compare). - */ - void setCompare1(uint16 compare); - - /** - * Equivalent to setCompare(2, compare). - */ - void setCompare2(uint16 compare); - - /** - * Equivalent to setCompare(3, compare). - */ - void setCompare3(uint16 compare); - - /** - * Equivalent to setCompare(4, compare). - */ - void setCompare4(uint16 compare); - - /** - * Attach an interrupt handler to the given channel. This - * interrupt handler will be called when the timer's counter - * reaches the given channel compare value. - * - * The argument should be a function which takes no arguments and - * has no return value; i.e. it should have signature + * @brief Attach an interrupt handler to the given channel. * - * void (*handler)(void); - * - * Note: The function (often called an interrupt service routine, - * or ISR) should attempt to return as quickly as possible. - * Blinking the LED, some logic, PWM updates, and Serial writes - * are fine; writing to SerialUSB or waiting for user input can - * take a long time and other compare interrupts won't fire. Tip: - * if you have a delay() in your interrupt routine, you're probably - * doing it wrong. + * This interrupt handler will be called when the timer's counter + * reaches the given channel compare value. * * @param channel the channel to attach the ISR to, from 1 to 4. * @param handler The ISR to attach to the given channel. @@ -310,32 +181,10 @@ class HardwareTimer { void attachInterrupt(int channel, voidFuncPtr handler); /** - * Equivalent to attachCompareInterrupt(1, handler). - * @see HardwareTimer::attachCompareInterrupt() - */ - void attachCompare1Interrupt(voidFuncPtr handler); - - /** - * Equivalent to attachCompareInterrupt(2, handler). - * @see HardwareTimer::attachCompareInterrupt() - */ - void attachCompare2Interrupt(voidFuncPtr handler); - - /** - * Equivalent to attachCompareInterrupt(3, handler). - * @see HardwareTimer::attachCompareInterrupt() - */ - void attachCompare3Interrupt(voidFuncPtr handler); - - /** - * Equivalent to attachCompareInterrupt(4, handler). - * @see HardwareTimer::attachCompareInterrupt() - */ - void attachCompare4Interrupt(voidFuncPtr handler); - - /** - * Remove the interrupt handler attached to the given channel, if - * any. The handler will no longer be called by this timer. + * @brief Remove the interrupt handler attached to the given + * channel, if any. + * + * The handler will no longer be called by this timer. * * @param channel the channel whose interrupt to detach, from 1 to 4. * @see HardwareTimer::attachInterrupt() @@ -343,71 +192,138 @@ class HardwareTimer { void detachInterrupt(int channel); /** - * Equivalent to detachInterrupt(1). - * @see HardwareTimer::detachInterrupt() + * @brief Reset the counter, and update the prescaler and overflow + * values. + * + * This will reset the counter to 0 in upcounting mode (the + * default). It will also update the timer's prescaler and + * overflow, if you have set them up to be changed using + * HardwareTimer::setPrescaleFactor() or + * HardwareTimer::setOverflow(). + * + * @see HardwareTimer::setPrescaleFactor() + * @see HardwareTimer::setOverflow() */ - void detachCompare1Interrupt(void); + void refresh(void); - /** - * Equivalent to detachInterrupt(2). - * @see HardwareTimer::detachInterrupt() - */ - void detachCompare2Interrupt(void); + /* -- Deprecated methods ----------------------------------------------- */ - /** - * Equivalent to detachInterrupt(3). - * @see HardwareTimer::detachInterrupt() - */ - void detachCompare3Interrupt(void); + /** @brief Deprecated; use setMode(channel, mode) instead. */ + void setChannelMode(int channel, timer_mode mode) { + setMode(channel, mode); + } - /** - * Equivalent to detachInterrupt(4). - * @see HardwareTimer::detachInterrupt() - */ - void detachCompare4Interrupt(void); + /** @brief Deprecated; use setMode(TIMER_CH1, mode) instead. */ + void setChannel1Mode(timer_mode mode) { setMode(TIMER_CH1, mode); } - /** - * Re-initializes the counter (to 0 in upcounting mode, which is - * the default), and generates an update of the prescale and - * overflow registers. - */ - void generateUpdate(void); + /** @brief Deprecated; use setMode(TIMER_CH2, mode) instead. */ + void setChannel2Mode(timer_mode mode) { setMode(TIMER_CH2, mode); } + + /** @brief Deprecated; use setMode(TIMER_CH3, mode) instead. */ + void setChannel3Mode(timer_mode mode) { setMode(TIMER_CH3, mode); } + + /** @brief Deprecated; use setMode(TIMER_CH4, mode) instead. */ + void setChannel4Mode(timer_mode mode) { setMode(TIMER_CH4, mode); } + + /** @brief Deprecated; use return getCompare(TIMER_CH1) instead. */ + uint16 getCompare1() { return getCompare(TIMER_CH1); } + + /** @brief Deprecated; use return getCompare(TIMER_CH2) instead. */ + uint16 getCompare2() { return getCompare(TIMER_CH2); } + + /** @brief Deprecated; use return getCompare(TIMER_CH3) instead. */ + uint16 getCompare3() { return getCompare(TIMER_CH3); } + + /** @brief Deprecated; use return getCompare(TIMER_CH4) instead. */ + uint16 getCompare4() { return getCompare(TIMER_CH4); } + + /** @brief Deprecated; use setCompare(TIMER_CH1, compare) instead. */ + void setCompare1(uint16 compare) { setCompare(TIMER_CH1, compare); } + + /** @brief Deprecated; use setCompare(TIMER_CH2, compare) instead. */ + void setCompare2(uint16 compare) { setCompare(TIMER_CH2, compare); } + + /** @brief Deprecated; use setCompare(TIMER_CH3, compare) instead. */ + void setCompare3(uint16 compare) { setCompare(TIMER_CH3, compare); } + + /** @brief Deprecated; use setCompare(TIMER_CH4, compare) instead. */ + void setCompare4(uint16 compare) { setCompare(TIMER_CH4, compare); } + + /** @brief Deprecated; use attachInterrupt(TIMER_CH1, handler) instead. */ + void attachCompare1Interrupt(voidFuncPtr handler) { + attachInterrupt(TIMER_CH1, handler); + } + + /** @brief Deprecated; use attachInterrupt(TIMER_CH2, handler) instead. */ + void attachCompare2Interrupt(voidFuncPtr handler) { + attachInterrupt(TIMER_CH2, handler); + } + + /** @brief Deprecated; use attachInterrupt(TIMER_CH3, handler) instead. */ + void attachCompare3Interrupt(voidFuncPtr handler) { + attachInterrupt(TIMER_CH3, handler); + } + + /** @brief Deprecated; use attachInterrupt(TIMER_CH4, handler) instead. */ + void attachCompare4Interrupt(voidFuncPtr handler) { + attachInterrupt(TIMER_CH4, handler); + } + + /** @brief Deprecated; use detachInterrupt(TIMER_CH1) instead. */ + void detachCompare1Interrupt(void) { detachInterrupt(TIMER_CH1); } + + /** @brief Deprecated; use detachInterrupt(TIMER_CH2) instead. */ + void detachCompare2Interrupt(void) { detachInterrupt(TIMER_CH2); } + + /** @brief Deprecated; use detachInterrupt(TIMER_CH3) instead. */ + void detachCompare3Interrupt(void) { detachInterrupt(TIMER_CH3); } + + /** @brief Deprecated; use detachInterrupt(TIMER_CH4) instead. */ + void detachCompare4Interrupt(void) { detachInterrupt(TIMER_CH4); } + + /** @brief Deprecated; use refresh() instead. */ + void generateUpdate(void) { refresh(); } }; -/** Pre-instantiated timer for use by user code. */ +/* -- The rest of this file is deprecated. --------------------------------- */ + +/** + * @brief Deprecated. + * + * Pre-instantiated timer. + */ extern HardwareTimer Timer1; -/** Pre-instantiated timer for use by user code. */ +/** + * @brief Deprecated. + * + * Pre-instantiated timer. + */ extern HardwareTimer Timer2; -/** Pre-instantiated timer for use by user code. */ +/** + * @brief Deprecated. + * + * Pre-instantiated timer. + */ extern HardwareTimer Timer3; -/** Pre-instantiated timer for use by user code. */ +/** + * @brief Deprecated. + * + * Pre-instantiated timer. + */ extern HardwareTimer Timer4; #ifdef STM32_HIGH_DENSITY -/** Pre-instantiated timer for use by user code, on devices with - more than four timers (this does not include the Maple). */ -extern HardwareTimer Timer5; -/** Pre-instantiated timer for use by user code, on devices with - more than four timers (this does not include the Maple). */ -extern HardwareTimer Timer8; -#endif - /** - * Get one of the pre-instantiated HardwareTimer instances, given a - * timer device number. - * - * Be careful not to pass an actual number to this function. For - * example, getTimer(1) will not return Timer1. Use a real - * timer_dev_num, e.g. TIMER1, TIMER2, etc. + * @brief Deprecated. * - * @param timerNum the timer device number, e.g. TIMER1. - * - * @return Pointer to the HardwareTimer instance corresponding to the - * given timer device number. If timerNum is TIMER_INVALID, returns a - * null pointer. + * Pre-instantiated timer. + */ +extern HardwareTimer Timer5; +/** + * @brief Deprecated. * - * @see timer_dev_num + * Pre-instantiated timer. */ -HardwareTimer* getTimer(timer_dev_num timerNum); - +extern HardwareTimer Timer8; #endif +#endif diff --git a/wirish/rules.mk b/wirish/rules.mk index c3608e3..6999288 100644 --- a/wirish/rules.mk +++ b/wirish/rules.mk @@ -23,6 +23,7 @@ cppSRCS_$(d) := wirish_math.cpp \ boards/maple_RET6.cpp \ comm/HardwareSerial.cpp \ comm/HardwareSPI.cpp \ + HardwareTimer.cpp \ usb_serial.cpp \ cxxabi-compat.cpp \ wirish_shift.cpp \ diff --git a/wirish/wirish.h b/wirish/wirish.h index d30ad20..82a9897 100644 --- a/wirish/wirish.h +++ b/wirish/wirish.h @@ -44,6 +44,7 @@ #include "wirish_time.h" #include "HardwareSPI.h" #include "HardwareSerial.h" +#include "HardwareTimer.h" #include "usb_serial.h" /* Arduino wiring macros and bit defines */ -- cgit v1.2.3