aboutsummaryrefslogtreecommitdiffstats
path: root/wirish/HardwareTimer.h
blob: 4030adc718290b7cd099f309843ec6543bf64f1c (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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/******************************************************************************
 * The MIT License
 *
 * Copyright (c) 2010 Bryan Newbold.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *****************************************************************************/

/**
 *  @brief wirish timer class to manage the four 16-bit timer peripherals
 */

#ifndef _HARDWARETIMER_H_
#define _HARDWARETIMER_H_

#include "timers.h"

/**
 * Interface to one of the 16-bit timer peripherals.
 *
 * User code should not instantiate this class directly; instead, use
 * one of the predefined Timer<n> 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.
 */
class HardwareTimer {
 private:
    timer_dev_num timerNum;

 public:
    HardwareTimer(timer_dev_num timer_num);

    /**
     * Return this timer's device number. For example,
     *  Timer1.getTimerNum() == TIMER1
     */
    timer_dev_num getTimerNum() { return 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.
     *
     * @see HardwareTimer::resume()
     */
    void pause(void);

    /**
     * Resume a paused timer, without affecting its configuration.
     *
     * The timer will resume counting and firing interrupts as
     * appropriate.
     *
     * Note that there is some function call overhead associated with
     * using this method, so using it in concert with
     * HardwareTimer::pause() is not a robust way to align multiple
     * timers to the same count value.
     *
     * @see HardwareTimer::pause()
     */
    void resume(void);

    /**
     * Returns the timer's prescale factor.
     * @see HardwareTimer::setPrescaleFactor()
     */
    uint16 getPrescaleFactor();

    /**
     * Set the timer's prescale factor.
     *
     * 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
     * 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()
     */
    void setPrescaleFactor(uint16 factor);

    /**
     * Gets the timer overflow value.
     * @see HardwareTimer::setOverflow()
     */
    uint16 getOverflow();

    /**
     * Sets 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.
     *
     * @param val The new overflow value to set
     */
    void setOverflow(uint16 val);

    /**
     * Retrieve 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.
     *
     * @param val The new count value to set.  If this value exceeds
     *            the timer's overflow value, it is truncated to the
     *            overflow value.
     */
    void setCount(uint16 val);

    /**
     * Configure 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).
     */
    uint16 setPeriod(uint32 microseconds);

    /**
     * Set the given channel of this timer to the given 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);

    /**
     * Gets 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.
     *
     * @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 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
     *
     *     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.
     *
     * @param channel the channel to attach the ISR to, from 1 to 4.
     * @param handler The ISR to attach to the given channel.
     * @see voidFuncPtr
     */
    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.
     *
     * @param channel the channel whose interrupt to detach, from 1 to 4.
     * @see HardwareTimer::attachInterrupt()
     */
    void detachInterrupt(int channel);

    /**
     * Equivalent to detachInterrupt(1).
     * @see HardwareTimer::detachInterrupt()
     */
    void detachCompare1Interrupt(void);

    /**
     * Equivalent to detachInterrupt(2).
     * @see HardwareTimer::detachInterrupt()
     */
    void detachCompare2Interrupt(void);

    /**
     * Equivalent to detachInterrupt(3).
     * @see HardwareTimer::detachInterrupt()
     */
    void detachCompare3Interrupt(void);

    /**
     * Equivalent to detachInterrupt(4).
     * @see HardwareTimer::detachInterrupt()
     */
    void detachCompare4Interrupt(void);

    /**
     * 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);
};

/** Pre-instantiated timer for use by user code. */
extern HardwareTimer Timer1;
/** Pre-instantiated timer for use by user code. */
extern HardwareTimer Timer2;
/** Pre-instantiated timer for use by user code. */
extern HardwareTimer Timer3;
/** Pre-instantiated timer for use by user code. */
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.
 *
 * @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.
 *
 * @see timer_dev_num
 */
HardwareTimer* getTimer(timer_dev_num timerNum);

#endif