FastArduino  v1.7
C++ library to build fast but small Arduino/AVR projects
pulse_timer.h
Go to the documentation of this file.
1 // Copyright 2016-2021 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 
115 namespace 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  PulseTimer16(const PulseTimer16<NTIMER_, PRESCALER_>&) = delete;
131  PulseTimer16<NTIMER_, PRESCALER_>& operator=(const PulseTimer16<NTIMER_, PRESCALER_>&) = delete;
132 
133  using CALCULATOR = Calculator<NTIMER>;
134  using TPRESCALER = typename CALCULATOR::PRESCALER;
135  static constexpr const TPRESCALER PRESCALER = PRESCALER_;
136 
137  explicit PulseTimer16(uint16_t pulse_frequency) : Timer<NTIMER>{TCCRA_MASK(), TCCRB_MASK()}
138  {
139  TRAIT::ICR = CALCULATOR::PWM_ICR_counter(PRESCALER, pulse_frequency);
140  }
141 
142  private:
143  static constexpr uint8_t TCCRA_MASK()
144  {
145  // If 16 bits, use ICR1 FastPWM
146  return TRAIT::F_PWM_ICR_TCCRA;
147  }
148  static constexpr uint8_t TCCRB_MASK()
149  {
150  // If 16 bits, use ICR1 FastPWM and prescaler forced to best fit all pulse frequency
151  return TRAIT::F_PWM_ICR_TCCRB | TRAIT::TCCRB_prescaler(PRESCALER);
152  }
153  };
154 
155  template<board::Timer NTIMER_, typename Calculator<NTIMER_>::PRESCALER PRESCALER_>
156  class PulseTimer8 : public Timer<NTIMER_>
157  {
158  public:
159  static constexpr const board::Timer NTIMER = NTIMER_;
160 
161  private:
162  using PARENT = Timer<NTIMER>;
163  using TRAIT = typename PARENT::TRAIT;
164  static_assert(!TRAIT::IS_16BITS, "TIMER must be an 8 bits timer");
165 
166  public:
167  PulseTimer8(const PulseTimer8<NTIMER_, PRESCALER_>&) = delete;
168  PulseTimer8<NTIMER_, PRESCALER_>& operator=(const PulseTimer8<NTIMER_, PRESCALER_>&) = delete;
169 
170  using CALCULATOR = Calculator<NTIMER>;
171  using TPRESCALER = typename CALCULATOR::PRESCALER;
172  static constexpr const TPRESCALER PRESCALER = PRESCALER_;
173 
174  explicit PulseTimer8(uint16_t pulse_frequency)
175  : Timer<NTIMER>{TCCRA_MASK(), TCCRB_MASK(), TIMSK_int_mask()}, MAX{OVERFLOW_COUNTER(pulse_frequency)}
176  {
177  // If 8 bits timer, then we need ISR on Overflow and Compare A/B
179  }
180 
181  private:
182  bool overflow()
183  {
184  ++count_;
185  if (count_ == MAX) count_ = 0;
186  return (count_ == 0);
187  }
188 
189  static constexpr uint8_t TCCRA_MASK()
190  {
191  // If 8 bits, use CTC/TOV ISR
192  return 0;
193  }
194  static constexpr uint8_t TCCRB_MASK()
195  {
196  // If 8 bits, use CTC/TOV ISR with prescaler forced best fit max pulse width
197  return TRAIT::TCCRB_prescaler(PRESCALER);
198  }
199  static constexpr uint8_t TIMSK_int_mask()
200  {
201  return TRAIT::TIMSK_int_mask(uint8_t(TimerInterrupt::OVERFLOW | TimerInterrupt::OUTPUT_COMPARE_A
202  | TimerInterrupt::OUTPUT_COMPARE_B));
203  }
204  static constexpr uint8_t OVERFLOW_COUNTER(uint16_t pulse_frequency)
205  {
206  return F_CPU / TRAIT::MAX_COUNTER / bits::BV16(uint8_t(PRESCALER)) / pulse_frequency;
207  }
208 
209  const uint8_t MAX;
210  uint8_t count_;
211 
212  friend struct isr_handler_pulse;
213  };
215 
249  template<board::Timer NTIMER_, typename timer::Calculator<NTIMER_>::PRESCALER PRESCALER_,
250  typename T = typename board_traits::Timer_trait<NTIMER_>::TYPE>
251  class PulseTimer : public timer::Timer<NTIMER_>
252  {
253  static_assert(types_traits::is_uint8_or_uint16<T>(), "T must be either uint8_t or uint16_t");
254 
255  public:
261  explicit PulseTimer(UNUSED uint16_t pulse_frequency) : timer::Timer<NTIMER_>{0, 0} {}
262  };
263 
265  template<board::Timer NTIMER_, typename timer::Calculator<NTIMER_>::PRESCALER PRESCALER_>
266  class PulseTimer<NTIMER_, PRESCALER_, uint8_t> : public timer::PulseTimer8<NTIMER_, PRESCALER_>
267  {
268  public:
269  explicit PulseTimer(uint16_t pulse_frequency) : timer::PulseTimer8<NTIMER_, PRESCALER_>{pulse_frequency} {}
270  };
271 
272  template<board::Timer NTIMER_, typename timer::Calculator<NTIMER_>::PRESCALER PRESCALER_>
273  class PulseTimer<NTIMER_, PRESCALER_, uint16_t> : public timer::PulseTimer16<NTIMER_, PRESCALER_>
274  {
275  public:
276  explicit PulseTimer(uint16_t pulse_frequency) : timer::PulseTimer16<NTIMER_, PRESCALER_>{pulse_frequency} {}
277  };
279 
281  // All PCI-related methods called by pre-defined ISR are defined here
282  struct isr_handler_pulse
283  {
284  template<uint8_t TIMER_NUM_, board::PWMPin PIN_, uint8_t COM_NUM_>
285  static constexpr board::Timer pulse_timer_check()
286  {
287  constexpr board::Timer NTIMER = isr_handler::check_timer<TIMER_NUM_>();
288  using TRAIT = board_traits::Timer_trait<NTIMER>;
289  static_assert(!TRAIT::IS_16BITS, "TIMER_NUM must be an 8 bits Timer");
290  using PINT = board_traits::Timer_COM_trait<NTIMER, COM_NUM_>;
291  static_assert(PIN_ == PINT::PIN_OCR, "PIN must be connected to TIMER_NUM OCxA/OCxB");
292  return NTIMER;
293  }
294 
295  template<uint8_t TIMER_NUM_, typename timer::Calculator<(board::Timer) TIMER_NUM_>::PRESCALER PRESCALER_,
296  board::PWMPin PIN_, uint8_t COM_NUM_>
297  static void pulse_timer_overflow()
298  {
299  static constexpr board::Timer NTIMER = pulse_timer_check<TIMER_NUM_, PIN_, COM_NUM_>();
300  using PT = timer::PulseTimer8<NTIMER, PRESCALER_>;
301  if (interrupt::HandlerHolder<PT>::handler()->overflow())
302  {
303  using PWMTRAIT = board_traits::PWMPin_trait<PIN_>;
305  }
306  }
307 
308  template<uint8_t TIMER_NUM_, typename timer::Calculator<(board::Timer) TIMER_NUM_>::PRESCALER PRESCALER_,
309  board::PWMPin PINA_, uint8_t COMA_NUM_, board::PWMPin PINB_, uint8_t COMB_NUM_>
310  static void pulse_timer_overflow()
311  {
312  static constexpr board::Timer NTIMER = pulse_timer_check<TIMER_NUM_, PINA_, COMA_NUM_>();
313  pulse_timer_check<TIMER_NUM_, PINB_, COMB_NUM_>();
314  using PT = timer::PulseTimer8<NTIMER, PRESCALER_>;
315  if (interrupt::HandlerHolder<PT>::handler()->overflow())
316  {
317  using PINTA = board_traits::Timer_COM_trait<NTIMER, COMA_NUM_>;
318  using PINTB = board_traits::Timer_COM_trait<NTIMER, COMB_NUM_>;
319  if (PINTA::OCR != 0)
320  {
321  using PWMTRAIT = board_traits::PWMPin_trait<PINA_>;
323  }
324  if (PINTB::OCR != 0)
325  {
326  using PWMTRAIT = board_traits::PWMPin_trait<PINB_>;
328  }
329  }
330  }
331 
332  template<uint8_t TIMER_NUM_, uint8_t COM_NUM_, board::PWMPin PIN_> static void pulse_timer_compare()
333  {
334  static constexpr board::Timer NTIMER = pulse_timer_check<TIMER_NUM_, PIN_, COM_NUM_>();
335  using PINT = board_traits::Timer_COM_trait<NTIMER, COM_NUM_>;
336  static_assert(PIN_ == PINT::PIN_OCR, "PIN must be connected to TIMER_NUM OCxA/OCxB");
337  using PWMTRAIT = board_traits::PWMPin_trait<PIN_>;
339  }
340  };
342 }
343 
344 #endif /* PULSE_TIMER_HH */
345 
types_traits.h
Useful traits for common types.
board::Timer
Timer
Defines all timers available for ATmega644.
Definition: atmega_xx4.h:316
gpio::FastPinType::clear
static void clear()
Set pin level to LOW (i.e.
Definition: gpio.h:599
timer
Defines all API to manipulate AVR Timers.
Definition: pulse_timer.h:116
timer::PulseTimer::PulseTimer
PulseTimer(UNUSED uint16_t pulse_frequency)
Create a PulseTimer with the provided pulse_frequency.
Definition: pulse_timer.h:261
timer.h
Timer API.
timer::Timer
General API to handle an AVR timer.
Definition: timer.h:705
UNUSED
#define UNUSED
Specific GCC attribute to declare an argument or variable unused, so that the compiler does not emit ...
Definition: defines.h:45
utilities.h
General utilities API that have broad application in programs.
bits::BV16
static constexpr uint16_t BV16(uint8_t bit)
Create a uint16_t bitmask for the given bit number.
Definition: bits.h:148
gpio.h
General Purpose (digital) Input Output API.
timer::Calculator
Defines a set of calculation methods for the given NTIMER_ The behavior of these methods is specific ...
Definition: timer.h:302
interrupts.h
General API for handling AVR interrupt vectors.
interrupt::register_handler
void register_handler(Handler &handler)
Register a class instance containing methods that shall be called back by an ISR.
Definition: interrupts.h:157
gpio::FastPinType::set
static void set()
Set pin level to HIGH (i.e.
Definition: gpio.h:590
timer::PulseTimer
Special kind of timer::Timer, specialized in emitting pulses with accurate width, according to a slow...
Definition: pulse_timer.h:252