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.rst253
1 files changed, 72 insertions, 181 deletions
diff --git a/docs/source/timers.rst b/docs/source/timers.rst
index e0e57cb..56dd686 100644
--- a/docs/source/timers.rst
+++ b/docs/source/timers.rst
@@ -2,42 +2,73 @@
.. _timers:
-========
- Timers
-========
+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
-------
+.. _timers-pwm-conflicts:
+
**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,
@@ -50,7 +81,7 @@ configuration being the same.
**Jitter:** other interrupts (USB, Serial, SysTick, or other
timers) can and will get called before or during the timer
- interrupt routines, causing pseudo-random delays and other
+ interrupt routines, causing pseudorandom delays and other
frustrations.
Disabling the USB port (by calling ``SerialUSB.end()``, or just
@@ -58,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
@@ -72,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
------------------
@@ -110,133 +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.
-
-``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);
+ // Set up the LED to blink
+ pinMode(BOARD_LED_PIN, OUTPUT);
// Setup Timer
Timer2.setChannel1Mode(TIMER_OUTPUTCOMPARE);
@@ -246,20 +140,17 @@ LED blink
}
void loop() {
- // Nothing! It's all in the interrupts
+ // Nothing! It's all in the interrupts
}
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);
@@ -270,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);
@@ -282,7 +173,7 @@ Racing Counters
Timer3.setOverflow(30000);
Timer4.setOverflow(30000);
Timer3.setCompare1(1000); // somewhere in the middle
- Timer4.setCompare1(1000);
+ Timer4.setCompare1(1000);
Timer3.attachCompare1Interrupt(handler1);
Timer4.attachCompare1Interrupt(handler2);
Timer3.resume();
@@ -291,14 +182,14 @@ Racing Counters
void loop() {
// Display the running counts
- SerialUSB.print("Count 1: ");
+ SerialUSB.print("Count 1: ");
SerialUSB.print(count1);
- SerialUSB.print("\t\tCount 2: ");
+ SerialUSB.print("\t\tCount 2: ");
SerialUSB.println(count2);
// 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();
@@ -309,7 +200,7 @@ Racing Counters
void handler1(void) {
count1++;
- }
+ }
void handler2(void) {
count2++;
- }
+ }