aboutsummaryrefslogtreecommitdiffstats
path: root/docs/source/timers.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/source/timers.rst')
-rw-r--r--docs/source/timers.rst231
1 files changed, 59 insertions, 172 deletions
diff --git a/docs/source/timers.rst b/docs/source/timers.rst
index 948805b..56dd686 100644
--- a/docs/source/timers.rst
+++ b/docs/source/timers.rst
@@ -8,25 +8,46 @@ Timers
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
-<pwm>` channels, once enabled, generate regular square-wave signals on
-specific output pins that will continue even if the microcontroller is
-busy crunching numbers or handling communications interrupts. By
-attaching interrupt handlers to these channels (instead of just
-changing the voltage on an external pin), more complex events like
-printing to a serial port, updating a variable, or emitting a whale
-mating call can be scheduled. You can even modify the configuration of
-the timer itself at a regular interval; the possibilities are endless!
-
-The four timers each have four separate compare channels. Each timer
-is a single 16-bit counter that can be configured with both a
-prescaler and an overflow value. The prescaler acts as a divider of
-the 72MHz system clock; without prescaling the counter would get up to
-65536 (2 to the 16th power) and roll over more than a thousand times a
-second; even with the full prescaler it rolls over about once a
-minute. The overflow value is the maximum value the counter will go up
-to. It defaults to the full 65535; smaller values will cause the
-counter to reset to zero more frequently.
+<pwm>` channels can generate regular square-wave signals on specific
+output pins without consuming extra clock cycles. By attaching
+interrupt handlers to these channels (instead of just changing the
+voltage on an external pin), more complex events can be scheduled.
+
+.. contents:: Contents
+ :local:
+
+Introduction
+------------
+
+.. _timers-prescale:
+
+The four timers each have four separate compare channels. Each channel
+has an associated 16-bit counter that can be configured with a 16-bit
+prescaler and a 16-bit overflow value. The prescaler determines how
+fast the counter changes, while the overflow value determines when it
+gets reset.
+
+The prescaler acts as a divider of the 72MHz system clock. That is,
+with a prescaler of 1, the channel's counter increments with a
+frequency of 72MHz, rolling over (passing the maximum 16-bit unsigned
+integer value of 65,535) more than a thousand times a second. With a
+prescaler of 7200, it has a frequency of (72/7200) MHz = 10 KHz,
+rolling over approximately every 6.55 seconds.
+
+The overflow value is the maximum value the counter will go up to. It
+defaults to the full 65,535; smaller values will cause the counter to
+reset to zero more frequently.
+
+Whenever a channel's counter reaches its overflow value, an "update
+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
+------------------
+The libmaple API for interacting with timers is documented at the
+:ref:`HardwareTimer reference <lang-hardwaretimer>`.
Caveats
-------
@@ -36,9 +57,18 @@ Caveats
**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 full :ref:`pin mapping table
-<pin-mapping-mega-table>` to match up timer channels and Maple header
-pin numbers.
+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,
@@ -59,7 +89,8 @@ configuration being the same.
auto-reset and communications functionality. This will require
that you put your Maple into :ref:`perpetual bootloader mode
<troubleshooting-perpetual-bootloader>` before uploading a new
- program to it.
+ program to it (or somehow causing your program to re-enable serial
+ over USB using :ref:`SerialUSB.begin() <lang-serialusb-begin>`).
Disabling SysTick with ``systick_disable()`` helps as well.
However, calling this function will break the ``micros()`` and
@@ -73,33 +104,6 @@ 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.
-.. _timers-modes:
-
-General Timer Modes
--------------------
-
-``TIMER_DISABLED``
-
- Exactly what it sounds like: 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. See the
- :ref:`PWM docs <pwm>` for more information on this 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.
-
-``TIMER_OUTPUTCOMPARE``
-
- In this mode, the timer counts from 0 to the overflow value
- repeatedly; every time the counter value reaches one of the
- channel compare values, the corresponding interrupt is fired.
-
SysTick Peripheral
------------------
@@ -111,136 +115,22 @@ 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()``.
-Function Reference
-------------------
-
-For all of these functions, ``Timer1`` can be replaced with
-``Timer2``, ``Timer3``, or ``Timer4``; the channel numbers also range
-from 1 to 4.
-
-``Timer1.pause()``/\ ``Timer1.resume()``
-
- These functions start and stop the counter without affecting the
- rest of the configuration. These functions can be used during the
- setup period to prevent interrupts from firing before they are
- completely configured. Note that there is some function call
- overhead with these functions, so they are not a perfect way to
- align multiple timers to the same count value.
-
-``Timer1.setOverflow(val)``
-
- Sets the overflow (or "reload") value for the whole timer; when
- the counter reaches this value it resets to zero. Defaults to
- 65535 (the largest unsigned 16bit integer); setting it 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.
-
-``Timer1.setPrescaleFactor(val)``
-
- The prescaler acts as a clock divider to slow down the rate at
- which the counter increments. 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 you set the prescaler to 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 just about
- once a second. Use the :ref:`setPeriod <timers-set-period>`
- function below if you are allergic to math!
-
-.. _timers-set-period:
-
-``Timer1.setPeriod(val)``
-
- This tricky trick will configure the prescaler and overflow values
- to generate a timer reload with a period as close to val
- microseconds as possible. It returns the chosen overflow value,
- which you can then use to set the channel compare values
- appropriately: if you just want the interrupts to fire when the
- clock rolls over and you don't care about the relative "phase",
- you can always set the channel compare values to 1.
-
- Remember: a microsecond is 1/1,000,000th of a second, or 1/1,000
- of a millisecond. The prescaler itself is 16bit, so the longest
- period that can be configured is 1/(72MHz) * (2^32) = (59.65
- seconds) or about a minute. You can get around this by creating an
- interrupt that increments a 32-bit variable, by using the
- ``millis()`` function, or by interfacing with an external
- real-time-clock chip.
-
-``Timer1.setCount(val)``/\ ``Timer1.getCount()``
-
- These functions let you mess with the counter's brains
- directly. You can probably make it not work if you try! The timer
- is 16bit, so ``val`` and the return value of ``getCount`` are
- ``uint16``.
-
-``Timer1.setChannel1Mode(MODE)``
-
- This sets the given channel (here 1) of the given timer (here 1)
- to the given mode. See the :ref:`list above <timers-modes>` for
- possible values; for interrupts you want ``TIMER_OUTPUTCOMPARE``.
-
-``Timer1.setCompare1(val)``
-
- Sets the compare value for the given channel; when the counter
- reaches this value the interrupt for this channel will fire if the
- channel is in output compare mode 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 overhead means that the smallest increment
- rate is a couple microseconds.
-
-.. _timers-attachinterrupt:
-.. _timers-detachinterrupt:
-
-``Timer1.attachCompare1Interrupt(function)``/\ ``Timer1.detachCompare1Interrupt()``
-
- This is how to attach or disable an interrupt handlers to timer
- channels; this what will get called when the counter reaches the
- compare value set with ``setCompareN(val)``. ``function``
- (sometimes referred to as an ISR: "interrupt service routine")
- should be of a type that does not accept or return any values
- (C/C++ programmers: ``void (function*)(void)``). They are just
- like any other function in your sketch/program and must be
- initialized at the top of the file and defined below.
-
- ``function`` should try to do what it has to do as fast 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 ISR, you're probably doing it
- wrong.
-
- Stay vigilant here... function pointers are serious business, and
- once you go down that path you'll find yourself in a `forest of
- parentheses <http://mitpress.mit.edu/sicp/>`_ before you know it.
-
Code Examples
-------------
LED blink
^^^^^^^^^
-\ ::
+::
- #define LED_PIN 13
#define LED_RATE 500000 // in microseconds; should give 0.5Hz toggles
void handler_led(void);
- int toggle = 0;
-
void setup()
{
// Set up the LED to blink
- pinMode(LED_PIN, OUTPUT);
+ pinMode(BOARD_LED_PIN, OUTPUT);
// Setup Timer
Timer2.setChannel1Mode(TIMER_OUTPUTCOMPARE);
@@ -254,16 +144,13 @@ LED blink
}
void handler_led(void) {
- toggle ^= 1;
- digitalWrite(LED_PIN, toggle);
+ toggleLED();
}
Racing Counters
^^^^^^^^^^^^^^^
-\ ::
-
- #define BUTTON_PIN 38
+::
void handler_count1(void);
void handler_count2(void);
@@ -274,7 +161,7 @@ Racing Counters
void setup()
{
// Set up BUT for input
- pinMode(BUTTON_PIN, INPUT_PULLUP);
+ pinMode(BOARD_BUTTON_PIN, INPUT_PULLUP);
// Setup Counting Timers
Timer3.setChannel1Mode(TIMER_OUTPUTCOMPARE);
@@ -302,7 +189,7 @@ Racing Counters
// Run... while BUT is held, pause Count2
for(int i = 0; i<1000; i++) {
- if(digitalRead(BUTTON_PIN)) {
+ if(digitalRead(BOARD_BUTTON_PIN)) {
Timer4.pause();
} else {
Timer4.resume();