aboutsummaryrefslogtreecommitdiffstats
path: root/source/timers.rst
blob: ce33f7fb225121c5f4e31b751ccb67fb3ca5dddd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
.. highlight:: cpp

.. _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 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.

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.

Library Documentation
---------------------

See the :ref:`HardwareTimer <lang-hardwaretimer>` reference for more
information on controlling the built-in timers.

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:`lang-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 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 your board's timer pin map to match up timer channels and pin
numbers:

.. TODO [0.0.12] Native links

* :ref:`Maple <maple-timer-map>`
* :ref:`Maple RET6 Edition <maple-ret6-timer-map>`
* :ref:`Maple Mini <maple-mini-timer-map>`

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 <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
<troubleshooting-perpetual-bootloader>` before uploading a new program
to it (or somehow causing your program to re-enable serial over USB
using :ref:`SerialUSB.begin() <lang-serialusb-begin>`).

The :ref:`SysTick <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()
<libmaple-systick-disable>`, and re-enabled using
:ref:`systick_enable() <libmaple-systick-enable>`.  However, be aware
that calling ``systick_disable()`` will stop the values coming from
:ref:`lang-micros` and :ref:`lang-millis` from increasing.