Repository navigation
Expand file tree
/
Copy pathTimer.h
More file actions
214 lines (188 loc) · 10.8 KB
/
Copy pathTimer.h
File metadata and controls
214 lines (188 loc) · 10.8 KB
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
/******************************************************************************************************************************************************
* COPYRIGHT
* ---------------------------------------------------------------------------------------------------------------------------------------------------
* \verbatim
* Copyright (c) Andreas Burnickl All rights reserved.
*
* \endverbatim
* ---------------------------------------------------------------------------------------------------------------------------------------------------
* FILE DESCRIPTION
* -------------------------------------------------------------------------------------------------------------------------------------------------*/
/** \file Timer.h
* \brief 16-bit Timer/Counter TC4/TC5 of the XMEGA E5 as periodic interrupt and PWM source
*
* \details One class for TCC4 and TCC5, picked by a traits struct. Everything that differs
* between the two kinds - the register struct, the number of compare channels and
* the pins those channels drive - lives in the traits, so the class never has to ask
* at run time which kind it is. A channel the timer does not have is a compile error.
*
* The timer counts dual-slope and interrupts at BOTTOM, so a period spans 2 * PER
* timer cycles and PWM is phase-correct.
*
******************************************************************************************************************************************************/
#ifndef _TIMER_H_
#define _TIMER_H_
/******************************************************************************************************************************************************
* I N C L U D E S
******************************************************************************************************************************************************/
#include "StandardTypes.h"
#include <avr/io.h>
/******************************************************************************************************************************************************
* G L O B A L C O N S T A N T M A C R O S
******************************************************************************************************************************************************/
/* Timer configuration parameter: which timers get an instance and an overflow ISR.
* TCC4 is off by default, because the xmegaForArduino core drives tone() and analogWrite() with it
* and tone() defines TCC4_OVF_vect itself; also its channels A and B sit on PC0/PC1, the TWI pins.
* TCD5 cannot be selected at all, see the note above TimerC5. */
#define TIMER_USE_TCC4 STD_OFF
#define TIMER_USE_TCC5 STD_ON
/******************************************************************************************************************************************************
* T I M E R T R A I T S
******************************************************************************************************************************************************/
/* TC4: four compare channels, OC4A..OC4D on PC0..PC3 (PC4..PC7 if PORTC.REMAP moves them, which
* this class does not do) */
struct TimerC4
{
using RegisterType = TC4_t;
static RegisterType& getTimer() { return TCC4; }
static PORT_t& getPort() { return PORTC; }
static constexpr uint8_t NumberOfChannels{4u};
static constexpr uint8_t FirstPin{0u};
};
/* TC5: two compare channels, OC5A/OC5B on PC4/PC5 - the SPI SS and SCK pins.
*
* There are no traits for TCD5 on purpose: the xmegaForArduino core counts millis() on it and
* defines TCD5_OVF_vect itself, so a second ISR would not link, and reprogramming the timer would
* stop the system clock. */
struct TimerC5
{
using RegisterType = TC5_t;
static RegisterType& getTimer() { return TCC5; }
static PORT_t& getPort() { return PORTC; }
static constexpr uint8_t NumberOfChannels{2u};
static constexpr uint8_t FirstPin{4u};
};
/******************************************************************************************************************************************************
* C L A S S T I M E R
******************************************************************************************************************************************************/
template <typename TimerTraits>
class Timer
{
/******************************************************************************************************************************************************
* P U B L I C D A T A T Y P E S A N D S T R U C T U R E S
******************************************************************************************************************************************************/
public:
/* Timer ISR callback function */
typedef void (*TimerIsrCallbackF_void)(void);
/* Type which describes the internal state of the TIMER */
typedef enum {
STATE_NONE,
STATE_INIT,
STATE_READY,
STATE_RUNNING,
STATE_STOPPED
} StateType;
/* Type which includes the values of the Clock Select Bit Group */
typedef enum {
TIMER_REG_CS_NO_CLOCK = TC45_CLKSEL_OFF_gc,
TIMER_REG_CS_NO_PRESCALER = TC45_CLKSEL_DIV1_gc,
TIMER_REG_CS_PRESCALE_2 = TC45_CLKSEL_DIV2_gc,
TIMER_REG_CS_PRESCALE_4 = TC45_CLKSEL_DIV4_gc,
TIMER_REG_CS_PRESCALE_8 = TC45_CLKSEL_DIV8_gc,
TIMER_REG_CS_PRESCALE_64 = TC45_CLKSEL_DIV64_gc,
TIMER_REG_CS_PRESCALE_256 = TC45_CLKSEL_DIV256_gc,
TIMER_REG_CS_PRESCALE_1024 = TC45_CLKSEL_DIV1024_gc
} TimerClockSelectType;
/* Type which defines the compare channels, each drives one PWM pin */
typedef enum {
TIMER_CHANNEL_A,
TIMER_CHANNEL_B,
TIMER_CHANNEL_C,
TIMER_CHANNEL_D
} TimerChannelType;
typedef enum {
TIMER_COUNT_DIRECTION_UP,
TIMER_COUNT_DIRECTION_DOWN
} TimerCountDirection;
/* PWM duty resolution, the same 10 bit TimerOne offers */
static constexpr uint16_t PwmDutyMax{1023u};
/******************************************************************************************************************************************************
* P R I V A T E D A T A A N D F U N C T I O N S
******************************************************************************************************************************************************/
private:
/* TC4 and TC5 share the bit layout of every register used here; the class relies on that
* rather than carrying a second set of masks in the traits */
static_assert(TC4_CLKSEL_gm == TC5_CLKSEL_gm, "TC4/TC5 clock select differ");
static_assert(TC4_WGMODE_gm == TC5_WGMODE_gm, "TC4/TC5 waveform mode differ");
static_assert(TC4_DIR_bm == TC5_DIR_bm, "TC4/TC5 direction bit differs");
static_assert(TC4_OVFINTLVL_gm == TC5_OVFINTLVL_gm, "TC4/TC5 overflow interrupt level differs");
static_assert(TC4_OVFIF_bm == TC5_OVFIF_bm, "TC4/TC5 overflow flag differs");
static_assert(TC4_CCAMODE_gm == TC5_CCAMODE_gm, "TC4/TC5 channel mode differs");
static constexpr uint8_t ChannelModeWidth{2u};
static Timer Instance;
StateType State;
TimerClockSelectType ClockSelectBitGroup;
TimerIsrCallbackF_void TimerOverflowCallback;
constexpr Timer() : State(STATE_NONE), ClockSelectBitGroup(TIMER_REG_CS_NO_PRESCALER), TimerOverflowCallback(nullptr) { }
// functions
static typename TimerTraits::RegisterType& getTimer() { return TimerTraits::getTimer(); }
static constexpr uint8_t toPinMask(TimerChannelType Channel) {
return static_cast<uint8_t>(1u << (TimerTraits::FirstPin + Channel));
}
static constexpr uint8_t toChannelModeMask(TimerChannelType Channel) {
return static_cast<uint8_t>(TC4_CCAMODE_gm << (Channel * ChannelModeWidth));
}
static constexpr uint8_t toChannelModeCompare(TimerChannelType Channel) {
return static_cast<uint8_t>(TC45_CCAMODE_COMP_gc << (Channel * ChannelModeWidth));
}
static constexpr uint8_t toPrescalerShift(TimerClockSelectType ClockSelect) {
switch(ClockSelect) {
case TIMER_REG_CS_PRESCALE_2: return 1u;
case TIMER_REG_CS_PRESCALE_4: return 2u;
case TIMER_REG_CS_PRESCALE_8: return 3u;
case TIMER_REG_CS_PRESCALE_64: return 6u;
case TIMER_REG_CS_PRESCALE_256: return 8u;
case TIMER_REG_CS_PRESCALE_1024: return 10u;
default: return 0u;
}
}
TimerCountDirection getCountingDirection();
void setClockSelect(TimerClockSelectType);
StdReturnType enablePwmChannel(TimerChannelType, uint16_t);
StdReturnType disablePwmChannel(TimerChannelType);
StdReturnType setPwmDutyChannel(TimerChannelType, uint16_t);
/******************************************************************************************************************************************************
* P U B L I C F U N C T I O N S
******************************************************************************************************************************************************/
public:
// methods
static Timer& getInstance() { return Instance; }
StdReturnType init(uint32_t = 1000u, TimerIsrCallbackF_void = nullptr);
StdReturnType setPeriod(uint32_t);
StdReturnType start();
void stop();
StdReturnType resume();
StdReturnType attachInterrupt(TimerIsrCallbackF_void);
void detachInterrupt();
StdReturnType read(uint32_t&);
void handleOverflowInterrupt();
/* The channel is a template argument so that channel C or D on a TC5 fails to compile
* instead of writing into reserved registers at run time */
template <TimerChannelType Channel> StdReturnType enablePwm(uint16_t Duty) {
static_assert(Channel < TimerTraits::NumberOfChannels, "this timer has no such compare channel");
return enablePwmChannel(Channel, Duty);
}
template <TimerChannelType Channel> StdReturnType disablePwm() {
static_assert(Channel < TimerTraits::NumberOfChannels, "this timer has no such compare channel");
return disablePwmChannel(Channel);
}
template <TimerChannelType Channel> StdReturnType setPwmDuty(uint16_t Duty) {
static_assert(Channel < TimerTraits::NumberOfChannels, "this timer has no such compare channel");
return setPwmDutyChannel(Channel, Duty);
}
};
template <typename TimerTraits> Timer<TimerTraits> Timer<TimerTraits>::Instance;
#endif
/******************************************************************************************************************************************************
* E N D O F F I L E
******************************************************************************************************************************************************/