FastArduino v1.10
C++ library to build fast but small Arduino/AVR projects
Loading...
Searching...
No Matches
pulse_timer.h
Go to the documentation of this file.
1// Copyright 2016-2023 Jean-Francois Poilpret
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
16
21#ifndef PULSE_TIMER_HH
22#define PULSE_TIMER_HH
23
24#include "boards/board_traits.h"
25#include <avr/interrupt.h>
26#include <stddef.h>
27#include "interrupts.h"
28#include "utilities.h"
29#include "timer.h"
30#include "gpio.h"
31#include "types_traits.h"
32
33// Macros to register ISR for PWM on PulseTimer8
34//==============================================
51#define REGISTER_PULSE_TIMER8_AB_ISR(TIMER_NUM, PRESCALER, PIN_A, PIN_B) \
52 ISR(CAT3(TIMER, TIMER_NUM, _OVF_vect)) \
53 { \
54 timer::isr_handler_pulse::pulse_timer_overflow<TIMER_NUM, PRESCALER, PIN_A, 0, PIN_B, 1>(); \
55 } \
56 ISR(CAT3(TIMER, TIMER_NUM, _COMPA_vect)) \
57 { \
58 timer::isr_handler_pulse::pulse_timer_compare<TIMER_NUM, 0, PIN_A>(); \
59 } \
60 ISR(CAT3(TIMER, TIMER_NUM, _COMPB_vect)) \
61 { \
62 timer::isr_handler_pulse::pulse_timer_compare<TIMER_NUM, 1, PIN_B>(); \
63 }
64
79#define REGISTER_PULSE_TIMER8_A_ISR(TIMER_NUM, PRESCALER, PIN_A) \
80 ISR(CAT3(TIMER, TIMER_NUM, _OVF_vect)) \
81 { \
82 timer::isr_handler_pulse::pulse_timer_overflow<TIMER_NUM, PRESCALER, PIN_A, 0>(); \
83 } \
84 ISR(CAT3(TIMER, TIMER_NUM, _COMPA_vect)) \
85 { \
86 timer::isr_handler_pulse::pulse_timer_compare<TIMER_NUM, 0, PIN_A>(); \
87 } \
88 EMPTY_INTERRUPT(CAT3(TIMER, TIMER_NUM, _COMPB_vect))
89
104#define REGISTER_PULSE_TIMER8_B_ISR(TIMER_NUM, PRESCALER, PIN_B) \
105 ISR(CAT3(TIMER, TIMER_NUM, _OVF_vect)) \
106 { \
107 timer::isr_handler_pulse::pulse_timer_overflow<TIMER_NUM, PRESCALER, PIN_B, 1>(); \
108 } \
109 ISR(CAT3(TIMER, TIMER_NUM, _COMPB_vect)) \
110 { \
111 timer::isr_handler_pulse::pulse_timer_compare<TIMER_NUM, 1, PIN_B>(); \
112 } \
113 EMPTY_INTERRUPT(CAT3(TIMER, TIMER_NUM, _COMPA_vect))
114
115namespace timer
116{
118 template<board::Timer NTIMER_, typename Calculator<NTIMER_>::PRESCALER PRESCALER_>
119 class PulseTimer16 : public Timer<NTIMER_>
120 {
121 public:
122 static constexpr const board::Timer NTIMER = NTIMER_;
123
124 private:
125 using PARENT = Timer<NTIMER>;
126 using TRAIT = typename PARENT::TRAIT;
127 static_assert(TRAIT::IS_16BITS, "TIMER must be a 16 bits timer");
128
129 public:
130 using CALCULATOR = Calculator<NTIMER>;
131 using TPRESCALER = typename CALCULATOR::PRESCALER;
132 static constexpr const TPRESCALER PRESCALER = PRESCALER_;
133
134 explicit PulseTimer16(uint16_t pulse_frequency) : Timer<NTIMER>{TCCRA_MASK(), TCCRB_MASK()}
135 {
136 TRAIT::ICR = CALCULATOR::PWM_ICR_counter(PRESCALER, pulse_frequency);
137 }
138
139 private:
140 static constexpr uint8_t TCCRA_MASK()
141 {
142 // If 16 bits, use ICR1 FastPWM
143 return TRAIT::F_PWM_ICR_TCCRA;
144 }
145 static constexpr uint8_t TCCRB_MASK()
146 {
147 // If 16 bits, use ICR1 FastPWM and prescaler forced to best fit all pulse frequency
148 return TRAIT::F_PWM_ICR_TCCRB | TRAIT::TCCRB_prescaler(PRESCALER);
149 }
150 };
151
152 template<board::Timer NTIMER_, typename Calculator<NTIMER_>::PRESCALER PRESCALER_>
153 class PulseTimer8 : public Timer<NTIMER_>
154 {
155 public:
156 static constexpr const board::Timer NTIMER = NTIMER_;
157
158 private:
159 using PARENT = Timer<NTIMER>;
160 using TRAIT = typename PARENT::TRAIT;
161 static_assert(!TRAIT::IS_16BITS, "TIMER must be an 8 bits timer");
162
163 public:
164 using CALCULATOR = Calculator<NTIMER>;
165 using TPRESCALER = typename CALCULATOR::PRESCALER;
166 static constexpr const TPRESCALER PRESCALER = PRESCALER_;
167
168 explicit PulseTimer8(uint16_t pulse_frequency)
169 : Timer<NTIMER>{TCCRA_MASK(), TCCRB_MASK(), TIMSK_int_mask()}, MAX{OVERFLOW_COUNTER(pulse_frequency)}
170 {
171 // If 8 bits timer, then we need ISR on Overflow and Compare A/B
173 }
174
175 private:
176 bool overflow()
177 {
178 ++count_;
179 if (count_ == MAX) count_ = 0;
180 return (count_ == 0);
181 }
182
183 static constexpr uint8_t TCCRA_MASK()
184 {
185 // If 8 bits, use CTC/TOV ISR
186 return 0;
187 }
188 static constexpr uint8_t TCCRB_MASK()
189 {
190 // If 8 bits, use CTC/TOV ISR with prescaler forced best fit max pulse width
191 return TRAIT::TCCRB_prescaler(PRESCALER);
192 }
193 static constexpr uint8_t TIMSK_int_mask()
194 {
195 return TRAIT::TIMSK_int_mask(uint8_t(TimerInterrupt::OVERFLOW | TimerInterrupt::OUTPUT_COMPARE_A
196 | TimerInterrupt::OUTPUT_COMPARE_B));
197 }
198 static constexpr uint8_t OVERFLOW_COUNTER(uint16_t pulse_frequency)
199 {
200 return F_CPU / TRAIT::MAX_COUNTER / bits::BV16(uint8_t(PRESCALER)) / pulse_frequency;
201 }
202
203 const uint8_t MAX;
204 uint8_t count_;
205
206 friend struct isr_handler_pulse;
207 };
209
243 template<board::Timer NTIMER_, typename timer::Calculator<NTIMER_>::PRESCALER PRESCALER_,
244 typename T = typename board_traits::Timer_trait<NTIMER_>::TYPE>
245 class PulseTimer : public timer::Timer<NTIMER_>
246 {
247 static_assert(types_traits::is_uint8_or_uint16<T>(), "T must be either uint8_t or uint16_t");
248
249 public:
255 explicit PulseTimer(UNUSED uint16_t pulse_frequency) : timer::Timer<NTIMER_>{0, 0} {}
256 };
257
259 template<board::Timer NTIMER_, typename timer::Calculator<NTIMER_>::PRESCALER PRESCALER_>
260 class PulseTimer<NTIMER_, PRESCALER_, uint8_t> : public timer::PulseTimer8<NTIMER_, PRESCALER_>
261 {
262 public:
263 explicit PulseTimer(uint16_t pulse_frequency) : timer::PulseTimer8<NTIMER_, PRESCALER_>{pulse_frequency} {}
264 };
265
266 template<board::Timer NTIMER_, typename timer::Calculator<NTIMER_>::PRESCALER PRESCALER_>
267 class PulseTimer<NTIMER_, PRESCALER_, uint16_t> : public timer::PulseTimer16<NTIMER_, PRESCALER_>
268 {
269 public:
270 explicit PulseTimer(uint16_t pulse_frequency) : timer::PulseTimer16<NTIMER_, PRESCALER_>{pulse_frequency} {}
271 };
273
275 // All PCI-related methods called by pre-defined ISR are defined here
276 struct isr_handler_pulse
277 {
278 template<uint8_t TIMER_NUM_, board::PWMPin PIN_, uint8_t COM_NUM_>
279 static constexpr board::Timer pulse_timer_check()
280 {
281 constexpr board::Timer NTIMER = isr_handler::check_timer<TIMER_NUM_>();
282 using TRAIT = board_traits::Timer_trait<NTIMER>;
283 static_assert(!TRAIT::IS_16BITS, "TIMER_NUM must be an 8 bits Timer");
284 using PINT = board_traits::Timer_COM_trait<NTIMER, COM_NUM_>;
285 static_assert(PIN_ == PINT::PIN_OCR, "PIN must be connected to TIMER_NUM OCxA/OCxB");
286 return NTIMER;
287 }
288
289 template<uint8_t TIMER_NUM_, typename timer::Calculator<(board::Timer) TIMER_NUM_>::PRESCALER PRESCALER_,
290 board::PWMPin PIN_, uint8_t COM_NUM_>
291 static void pulse_timer_overflow()
292 {
293 static constexpr board::Timer NTIMER = pulse_timer_check<TIMER_NUM_, PIN_, COM_NUM_>();
294 using PT = timer::PulseTimer8<NTIMER, PRESCALER_>;
295 if (interrupt::HandlerHolder<PT>::handler()->overflow())
296 {
297 using PWMTRAIT = board_traits::PWMPin_trait<PIN_>;
299 }
300 }
301
302 template<uint8_t TIMER_NUM_, typename timer::Calculator<(board::Timer) TIMER_NUM_>::PRESCALER PRESCALER_,
303 board::PWMPin PINA_, uint8_t COMA_NUM_, board::PWMPin PINB_, uint8_t COMB_NUM_>
304 static void pulse_timer_overflow()
305 {
306 static constexpr board::Timer NTIMER = pulse_timer_check<TIMER_NUM_, PINA_, COMA_NUM_>();
307 pulse_timer_check<TIMER_NUM_, PINB_, COMB_NUM_>();
308 using PT = timer::PulseTimer8<NTIMER, PRESCALER_>;
309 if (interrupt::HandlerHolder<PT>::handler()->overflow())
310 {
311 using PINTA = board_traits::Timer_COM_trait<NTIMER, COMA_NUM_>;
312 using PINTB = board_traits::Timer_COM_trait<NTIMER, COMB_NUM_>;
313 if (PINTA::OCR != 0)
314 {
315 using PWMTRAIT = board_traits::PWMPin_trait<PINA_>;
317 }
318 if (PINTB::OCR != 0)
319 {
320 using PWMTRAIT = board_traits::PWMPin_trait<PINB_>;
322 }
323 }
324 }
325
326 template<uint8_t TIMER_NUM_, uint8_t COM_NUM_, board::PWMPin PIN_> static void pulse_timer_compare()
327 {
328 static constexpr board::Timer NTIMER = pulse_timer_check<TIMER_NUM_, PIN_, COM_NUM_>();
329 using PINT = board_traits::Timer_COM_trait<NTIMER, COM_NUM_>;
330 static_assert(PIN_ == PINT::PIN_OCR, "PIN must be connected to TIMER_NUM OCxA/OCxB");
331 using PWMTRAIT = board_traits::PWMPin_trait<PIN_>;
333 }
334 };
336}
337
338#endif /* PULSE_TIMER_HH */
static void set()
Set pin level to HIGH (i.e.
Definition: gpio.h:596
static void clear()
Set pin level to LOW (i.e.
Definition: gpio.h:605
Special kind of timer::Timer, specialized in emitting pulses with accurate width, according to a slow...
Definition: pulse_timer.h:246
PulseTimer(UNUSED uint16_t pulse_frequency)
Create a PulseTimer with the provided pulse_frequency.
Definition: pulse_timer.h:255
General API to handle an AVR timer.
Definition: timer.h:705
#define UNUSED
Specific GCC attribute to declare an argument or variable unused, so that the compiler does not emit ...
Definition: defines.h:45
General Purpose (digital) Input Output API.
General API for handling AVR interrupt vectors.
static constexpr uint16_t BV16(uint8_t bit)
Create a uint16_t bitmask for the given bit number.
Definition: bits.h:148
Timer
Defines all timers available for target MCU.
Definition: empty.h:112
void register_handler(Handler &handler)
Register a class instance containing methods that shall be called back by an ISR.
Definition: interrupts.h:185
Defines all API to manipulate AVR Timers.
Definition: pulse_timer.h:116
Defines a set of calculation methods for the given NTIMER_ The behavior of these methods is specific ...
Definition: timer.h:302
Timer API.
Useful traits for common types.
General utilities API that have broad application in programs.