libyggdrasil  v1.0.0
timer.hpp
Go to the documentation of this file.
1  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * _____.___. .___ .__.__ *
3  * \__ | | ____ ____ __| _/___________ _____|__| | *
4  * / | |/ ___\ / ___\ / __ |\_ __ \__ \ / ___/ | | *
5  * \____ / /_/ > /_/ > /_/ | | | \// __ \_\___ \| | |__ *
6  * / ______\___ /\___ /\____ | |__| (____ /____ >__|____/ *
7  * \/ /_____//_____/ \/ \/ \/ *
8  * - Midgard - *
9  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10  * This software can be used by students and other personal of the *
11  * Bern University of Applied Sciences under the terms of the MIT *
12  * license. *
13  * For other persons this software is under the terms of the GNU *
14  * General Public License version 2. *
15  * *
16  * Copyright © 2021, Bern University of Applied Sciences. *
17  * All rights reserved. *
18  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26 #pragma once
27 
29 
30 #include <string>
31 #include <string_view>
32 #include <array>
33 
34 #include <cstdlib>
35 
36 #include <cmsis_gcc.h>
37 #include <limits>
38 #include <cstdio>
39 #include <cmath>
40 #include <stdio.h>
41 
42 namespace bsp::mid::drv {
43 
52  template<auto Context, u8 Channel, typename Size>
53  struct TimerChannel {
54  static_assert(Channel >= 1 && Channel <= 4, "Channel index out of range");
55 
62  ALWAYS_INLINE bool startPwm() const noexcept {
63  if(!hasPwmModule()) return false;
64  switch(Channel) {
65  case 1: Context->Instance->CCER |= TIM_CCER_CC1E; break; // Enable capture compare output for channel 1
66  case 2: Context->Instance->CCER |= TIM_CCER_CC2E; break; // Enable capture compare output for channel 2
67  case 3: Context->Instance->CCER |= TIM_CCER_CC3E; break; // Enable capture compare output for channel 3
68  case 4: Context->Instance->CCER |= TIM_CCER_CC4E; break; // Enable capture compare output for channel 4
69  default: bsp::unreachable();
70  }
71  Context->Instance->CR1 = TIM_CR1_CEN; // Enable the counter
72  return true;
73  }
74 
81  ALWAYS_INLINE bool stopPwm() const noexcept {
82  if(!hasPwmModule()) return false;
83  switch(Channel) {
84  case 1: Context->Instance->CCER &= ~TIM_CCER_CC1E; break; // Disable capture compare output for channel 1
85  case 2: Context->Instance->CCER &= ~TIM_CCER_CC2E; break; // Disable capture compare output for channel 2
86  case 3: Context->Instance->CCER &= ~TIM_CCER_CC3E; break; // Disable capture compare output for channel 3
87  case 4: Context->Instance->CCER &= ~TIM_CCER_CC4E; break; // Disable capture compare output for channel 4
88  default: bsp::unreachable();
89  }
90  if(Context->Instance->CCER == 0) Context->Instance->CR1 &= ~TIM_CR1_CEN; // Disable the counter when all pwm channels are disabled
91  return true;
92  }
93 
100  ALWAYS_INLINE bool setPolarityHigh(bool highActive = true) const noexcept {
101  if(!hasPwmModule()) return false;
102  if(!highActive){
103  switch(Channel) {
104  case 1: Context->Instance->CCER |= TIM_CCER_CC1P; break; // Capture/Compare 1 output Polarity to low
105  case 2: Context->Instance->CCER |= TIM_CCER_CC2P; break; // Capture/Compare 2 output Polarity to low
106  case 3: Context->Instance->CCER |= TIM_CCER_CC3P; break; // Capture/Compare 3 output Polarity to low
107  case 4: Context->Instance->CCER |= TIM_CCER_CC4P; break; // Capture/Compare 4 output Polarity to low
108  default: bsp::unreachable();
109  }
110  }
111  else {
112  switch(Channel) {
113  case 1: Context->Instance->CCER &= ~TIM_CCER_CC1P; break; // Capture/Compare 1 output Polarity to high
114  case 2: Context->Instance->CCER &= ~TIM_CCER_CC2P; break; // Capture/Compare 2 output Polarity to high
115  case 3: Context->Instance->CCER &= ~TIM_CCER_CC3P; break; // Capture/Compare 3 output Polarity to high
116  case 4: Context->Instance->CCER &= ~TIM_CCER_CC4P; break; // Capture/Compare 4 output Polarity to high
117  default: bsp::unreachable();
118  }
119  }
120 
121  return true;
122  }
123 
130  ALWAYS_INLINE bool setDutyCycle(float dutyCycle) const noexcept {
131  if(!hasPwmModule()) return false;
132  dutyCycle = std::abs(dutyCycle); // Make sure that the value is positive
133  if(dutyCycle > 100) dutyCycle = 100; // Limit the duty cycle to 100
134  Size arr = Context->Instance->ARR; // Get auto reload register
135  Size ccr = 0;
136  if(dutyCycle != 0) ccr = arr / 100 * dutyCycle; // Calculate capture compare register value
137  switch(Channel) {
138  case 1: Context->Instance->CCR1 = ccr; break; // Set the duty cycle for channel 1
139  case 2: Context->Instance->CCR2 = ccr; break; // Set the duty cycle for channel 2
140  case 3: Context->Instance->CCR3 = ccr; break; // Set the duty cycle for channel 3
141  case 4: Context->Instance->CCR4 = ccr; break; // Set the duty cycle for channel 4
142  default: bsp::unreachable();
143  }
144 
145  return true;
146  }
147 
148  private:
154  bool hasPwmModule() const noexcept{
155  if ((Context->Instance == TIM6) || // Checks if the timer does not have a pwm module (less to check)
156  (Context->Instance == TIM7) ||
157  (Context->Instance == TIM10) ||
158  (Context->Instance == TIM13) ||
159  (Context->Instance == TIM14)) {
160  return false;
161  }
162  return true;
163  }
164 
165  };
166 
167 
175  template<auto Context, typename Size>
176  struct TimerEncoder {
177 
181  enum class Direction : u8 {
182  Clockwise,
184  };
185 
189  enum class Mode : u8 {
192  };
193 
200  ALWAYS_INLINE bool enable() const noexcept {
201  if(!hasEncoderModule()) return false; // Check if the timer got a pwm modul
202  Context->Instance->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E; // Enable capture compare channel 1 and 2
203  Context->Instance->CR1 = TIM_CR1_CEN; // Enable the timer
204  return true;
205  }
206 
213  ALWAYS_INLINE bool disable() const noexcept {
214  if(!hasEncoderModule()) return false; // Check if the timer got a pwm modul
215  Context->Instance->CR1 &= ~TIM_CR1_CEN; // Disable the timer
216  Context->Instance->CCER &= ~(TIM_CCER_CC1E | TIM_CCER_CC2E); // Disable capture compare channel 1 and 2
217  return true;
218  }
219 
226  ALWAYS_INLINE Size getCount() const noexcept {
227  return Context->Instance->CNT; // Read the counter value form the register
228  }
229 
235  ALWAYS_INLINE void setCount(Size cnt) const noexcept {
236  Context->Instance->CNT = cnt; // Write the new counter value to the register
237  }
238 
246  return (Context->Instance->CR1 & TIM_CR1_DIR) ? Direction::CounterClockwise : Direction::Clockwise;
247  }
248 
254  ALWAYS_INLINE void setMode(Mode mode) const noexcept {
255  Context->Instance->CR1 &= ~TIM_CR1_CEN; // Disable the timer
256  Context->Instance->SMCR &= ~(TIM_SMCR_SMS_0 | TIM_SMCR_SMS_1); // Reset the mode bits
257  switch(mode) { // Set the new mode according to
258  case Mode::_48StepsPerTurn: Context->Instance->SMCR |= TIM_SMCR_SMS_0; break; // Counter counts up/down on TI1FP1 edge depending on TI2FP2 level
259  case Mode::_96StepsPerTurn: Context->Instance->SMCR |= TIM_SMCR_SMS_0 | TIM_SMCR_SMS_1; break; // Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the level of the other input
260  }
261  Context->Instance->CR1 = TIM_CR1_CEN; // Enable the timer
262  }
263 
270  ALWAYS_INLINE bool init() const noexcept {
271  if(!hasEncoderModule()) return false; // Check if the timer got a pwm modul
272  Context->Instance->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E; // Enable capture compare 1 and 2
273  Context->Instance->SMCR |= TIM_SMCR_SMS_0 | TIM_SMCR_SMS_1; // Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the level of the other input
274  Context->Instance->CR1 = TIM_CR1_CEN; // Enable the timer
275  return true;
276  }
277 
278  private:
284  bool hasEncoderModule() const noexcept{
285  if ((Context->Instance == TIM1) || // All timer with a Encoder Module
286  (Context->Instance == TIM2) ||
287  (Context->Instance == TIM3) ||
288  (Context->Instance == TIM4) ||
289  (Context->Instance == TIM5) ||
290  (Context->Instance == TIM8)) {
291  return true;
292  }
293  return false;
294  }
295  };
296 
304  template<auto Context, typename Size>
306 
307 
311  ALWAYS_INLINE void start() const noexcept {
312  Context->Instance->CR1 = TIM_CR1_CEN; // Enable the timer
313  }
314 
318  ALWAYS_INLINE void stop() const noexcept {
319  Context->Instance->CR1 &= ~TIM_CR1_CEN; // Disable the timer
320  }
321 
325  ALWAYS_INLINE void reset() const noexcept {
326  Context->Instance->CNT = 0;
327  }
328 
334  u64 getTimeToOverflow() const noexcept {
335  return cntToNanoSeconds(std::numeric_limits<Size>::max());
336  }
337 
343  std::string getFormattedTimeToOverflow() const noexcept {
344  return formatToString(cntToNanoSeconds(std::numeric_limits<Size>::max()));
345  }
346 
352  u64 getPassedTime() const noexcept {
353  return cntToNanoSeconds(Context->Instance->CNT);
354  }
355 
361  std::string getFormattedPassedTime() const noexcept {
362  return formatToString(cntToNanoSeconds(Context->Instance->CNT));
363  }
364 
371  std::string formatToString(u64 passedTime) const noexcept {
372  std::string buffer(0xFF, 0x00);
373  u32 s = passedTime / 1E9;
374  u16 ms = static_cast<u32>(passedTime / 1E6) % 1000;
375  u16 us = static_cast<u32>(passedTime / 1E3) % 1000;
376  u16 ns = passedTime % 1000;
377 
378  snprintf(buffer.data(), buffer.size(), "%lus %dms %dus %dns", s, ms, us, ns);
379 
380  return buffer;
381  }
382 
383 
384  private:
385 
392  u64 cntToNanoSeconds(Size cntValue) const noexcept {
393  float timerFrequency;
394 
395  if ((Context->Instance == TIM1) || // Get the timerFrequency before prescaler
396  (Context->Instance == TIM8) || // this is depending on the APBx bus different
397  (Context->Instance == TIM9) ||
398  (Context->Instance == TIM10) ||
399  (Context->Instance == TIM11)) {
400  float pclk2 = static_cast<float>(SystemCoreClock >> APBPrescTable[(RCC->CFGR & RCC_CFGR_PPRE2) >> RCC_CFGR_PPRE2_Pos]); // Get the timer clock before prescaler for APB2
401  if ((RCC->CFGR & RCC_CFGR_PPRE2) == 0) timerFrequency = pclk2; // Timer frequency when APB2 prescaler = 1
402  else timerFrequency = 2 * pclk2; // Timer frequency when APB2 prescaler > 1
403 
404  }
405  else {
406  float pclk1 = static_cast<float>(SystemCoreClock >> APBPrescTable[(RCC->CFGR & RCC_CFGR_PPRE1) >> RCC_CFGR_PPRE1_Pos]); // Get the timer clock before prescaler for APB1
407  if ((RCC->CFGR & RCC_CFGR_PPRE1) == 0) timerFrequency = pclk1; // Timer frequency when APB1 prescaler = 1
408  else timerFrequency = 2 * pclk1; // Timer frequency when APB1 prescaler > 1
409  }
410 
411  return static_cast<u64>(((cntValue) / timerFrequency) * 1E9); // Calculate the passed time in ns
412  }
413 
414 
415  };
416 
424  template<auto Context, typename Size>
425  struct Timer {
426 
432  template<u8 Number>
433  static constexpr auto Channel = TimerChannel<Context, Number, Size>();
434 
438  static constexpr auto Encoder = TimerEncoder<Context, Size>();
439 
444 
450  static bool init() {
451  return true;
452  }
453 
459  static bool deinit() {
460  return true;
461  }
462 
463 
467  static void enable() {
468  Context->Instance->CR1 = TIM_CR1_CEN; // Enable the timer
469  }
470 
474  static void disable() {
475  Context->Instance->CR1 &= ~TIM_CR1_CEN; // Disable the timer
476  }
477 
478 
484  static Size getCount() {
485  return Context->Instance->CNT;
486  }
487 
493  static void setCount(Size cnt) {
494  Context->Instance->CNT = cnt;
495  }
496 
503  static u32 getPwmFrequency() {
504  auto arr = Context->Instance->ARR;
505  auto psc = (Context->Instance->PSC + 1);
506  if(psc == 0) psc = 1;
507 
508  if ((Context->Instance == TIM1) ||
509  (Context->Instance == TIM8) ||
510  (Context->Instance == TIM9) ||
511  (Context->Instance == TIM10) ||
512  (Context->Instance == TIM11)) {
513 
514  u32 pclk2 = (SystemCoreClock >> APBPrescTable[(RCC->CFGR & RCC_CFGR_PPRE2) >> RCC_CFGR_PPRE2_Pos]); // Get the timer clock before prescaler for APB1
515  if ((RCC->CFGR & RCC_CFGR_PPRE2) == 0) return (pclk2 / psc) / arr; // Return the pwm frequency when APB1 prescaler = 1
516  else return 2 * (pclk2 / psc) / arr; // Return the pwm frequency when APB1 prescaler > 1
517 
518  }
519  else {
520  u32 pclk1 = (SystemCoreClock >> APBPrescTable[(RCC->CFGR & RCC_CFGR_PPRE1) >> RCC_CFGR_PPRE1_Pos]); // Get the timer clock before prescaler for APB2
521  if ((RCC->CFGR & RCC_CFGR_PPRE1) == 0) return (pclk1 / psc) / arr; // Return the pwm frequency when APB2 prescaler = 1
522  else return 2 * (pclk1 / psc) / arr; // Return the pwm frequency when APB2 prescaler > 1
523 
524  }
525  }
526 
537  static bool setPwmFrequency(u32 f_hz, Size resolution = 0){
538  u32 timerFrequency; // Timerfrequency depending on the RCC configuration
539  u32 psc = 0;
540  Size arr = 0;
541 
542  std::array<float, 4> dutyCycle; // Save the actual duty cycle for each channel
543  dutyCycle[0] = intToDuty(Context->Instance->CCR1);
544  dutyCycle[1] = intToDuty(Context->Instance->CCR2);
545  dutyCycle[2] = intToDuty(Context->Instance->CCR3);
546  dutyCycle[3] = intToDuty(Context->Instance->CCR4);
547 
548 
549 
550 
551  if(resolution){
552  Context->Instance->ARR = resolution; // Set a new auto reload value
553  }
554  arr = Context->Instance->ARR; // Get the auto reload register
555 
556  if ((Context->Instance == TIM1) || // Get the timerFrequency before prescaler
557  (Context->Instance == TIM8) || // this is depending on the APBx bus different
558  (Context->Instance == TIM9) ||
559  (Context->Instance == TIM10) ||
560  (Context->Instance == TIM11)) {
561  u32 pclk2 = (SystemCoreClock >> APBPrescTable[(RCC->CFGR & RCC_CFGR_PPRE2) >> RCC_CFGR_PPRE2_Pos]); // Get the timer clock before prescaler for APB2
562  if ((RCC->CFGR & RCC_CFGR_PPRE2) == 0) timerFrequency = pclk2; // Pwm frequency when APB2 prescaler = 1
563  else timerFrequency = 2 * pclk2; // Pwm frequency when APB2 prescaler > 1
564 
565  }
566  else {
567  u32 pclk1 = (SystemCoreClock >> APBPrescTable[(RCC->CFGR & RCC_CFGR_PPRE1) >> RCC_CFGR_PPRE1_Pos]); // Get the timer clock before prescaler for APB1
568  if ((RCC->CFGR & RCC_CFGR_PPRE1) == 0) timerFrequency = pclk1; // Pwm frequency when APB1 prescaler = 1
569  else timerFrequency = 2 * pclk1; // Pwm frequency when APB1 prescaler > 1
570  }
571 
572  if((f_hz * arr) > timerFrequency) return false; // Check if the timer frequency is not to low
573 
574  psc = std::round((timerFrequency / (f_hz * arr)) - 1); // Calculate the prescaler
575 
576  if(psc > 0xFFFF) return false; // Check if prescaler is in the possible range from u16
577  else{
578  Context->Instance->CR1 &= ~TIM_CR1_CEN; // Stop counter to set the new prescaler and until the new duty cycle values are set
579  Context->Instance->PSC = psc; // Set the new prescaler
580  }
581 
582  // Reset the stored duty cycle for each channel according to the resolution
583  Context->Instance->CCR1 = dutyToInt(dutyCycle[0]);
584  Context->Instance->CCR2 = dutyToInt(dutyCycle[1]);
585  Context->Instance->CCR3 = dutyToInt(dutyCycle[2]);
586  Context->Instance->CCR4 = dutyToInt(dutyCycle[3]);
587 
588  Context->Instance->CR1 = TIM_CR1_CEN; // Enable the timer again
589 
590  return true;
591  }
592 
593 
594 
595  private:
596 
603  static constexpr Size dutyToInt(float dutyCycle){
604  if(dutyCycle > 100) dutyCycle = 100;
605  else if(dutyCycle <= 0) return 0;
606  Size arr = Context->Instance->ARR;
607  return static_cast<Size>(arr / 100 * dutyCycle);
608  }
609 
616  static constexpr float intToDuty(Size intDutyCycle){
617  Size arr = Context->Instance->ARR;
618  if(arr == 0) return 0;
619  return static_cast<float>(intDutyCycle) / static_cast<float>(arr) * 100.0F;
620  }
621  };
622 
623 }
bsp::mid::drv::Timer::disable
static void disable()
Disable the counter.
Definition: timer.hpp:474
bsp::mid::drv::TimerEncoder
Timer encoder implementation for Midgard.
Definition: timer.hpp:176
bsp::mid::drv::Timer
Timer implementation for Midgard.
Definition: timer.hpp:425
bsp::mid::drv::Timer::Encoder
static constexpr auto Encoder
Timer in encoder mode.
Definition: timer.hpp:438
bsp::mid::drv::TimerEncoder::Mode::_48StepsPerTurn
@ _48StepsPerTurn
bsp::mid::drv::TimerProfileCounter::start
ALWAYS_INLINE void start() const noexcept
Start the counter.
Definition: timer.hpp:311
u16
uint16_t u16
Definition: types.h:37
u8
uint8_t u8
Unsigned integer definitions.
Definition: types.h:36
bsp::mid::drv::TimerProfileCounter::stop
ALWAYS_INLINE void stop() const noexcept
Stop the counter.
Definition: timer.hpp:318
bsp::mid::drv::Timer::enable
static void enable()
Enable the counter.
Definition: timer.hpp:467
bsp::mid::drv::TimerEncoder::getDirection
ALWAYS_INLINE Direction getDirection() const noexcept
Get the direction of the last rotation.
Definition: timer.hpp:245
bsp::mid::drv::Timer::deinit
static bool deinit()
Deinit function.
Definition: timer.hpp:459
timer.hpp
Frontend for the Timer abstraction.
u64
uint64_t u64
Definition: types.h:39
bsp::mid::drv::Timer::setPwmFrequency
static bool setPwmFrequency(u32 f_hz, Size resolution=0)
Set the pwm frequency and (optional) the maximal ticks within on cycle for all channels.
Definition: timer.hpp:537
bsp::mid::drv::Timer::getCount
static Size getCount()
Get the counter value.
Definition: timer.hpp:484
ALWAYS_INLINE
#define ALWAYS_INLINE
Definition: attributes.h:34
bsp::mid::drv::TimerChannel::setPolarityHigh
ALWAYS_INLINE bool setPolarityHigh(bool highActive=true) const noexcept
Start set pwm polarity.
Definition: timer.hpp:100
bsp::mid::drv::TimerEncoder::disable
ALWAYS_INLINE bool disable() const noexcept
Disable the encoder mode.
Definition: timer.hpp:213
u32
uint32_t u32
Definition: types.h:38
bsp::mid::drv::Timer::ProfileCounter
static constexpr auto ProfileCounter
Timer used as profile counter.
Definition: timer.hpp:443
bsp::mid::drv::TimerEncoder::Direction::CounterClockwise
@ CounterClockwise
bsp::mid::drv::TimerEncoder::enable
ALWAYS_INLINE bool enable() const noexcept
Enable the encoder mode.
Definition: timer.hpp:200
bsp::mid::drv::TimerProfileCounter::getPassedTime
u64 getPassedTime() const noexcept
Get the time passed time since the start.
Definition: timer.hpp:352
bsp::mid::drv::TimerChannel::stopPwm
ALWAYS_INLINE bool stopPwm() const noexcept
Stop PWM generation for the channel.
Definition: timer.hpp:81
bsp::mid::drv::Timer::init
static bool init()
Init function.
Definition: timer.hpp:450
bsp::mid::drv::TimerEncoder::setCount
ALWAYS_INLINE void setCount(Size cnt) const noexcept
Set the encoder counter value.
Definition: timer.hpp:235
bsp::mid::drv::TimerEncoder::init
ALWAYS_INLINE bool init() const noexcept
Initialization function for the encoder.
Definition: timer.hpp:270
bsp::mid::drv::TimerProfileCounter
Profile counter implementation for Midgard.
Definition: timer.hpp:305
bsp::mid::drv::TimerProfileCounter::reset
ALWAYS_INLINE void reset() const noexcept
Reset the counter to 0.
Definition: timer.hpp:325
bsp::mid::drv::TimerChannel
Timer channel implementation for Midgard.
Definition: timer.hpp:53
bsp::mid::drv::TimerEncoder::Mode
Mode
Modes for the encoder, 48 odr 96 steps per turn are possible.
Definition: timer.hpp:189
bsp::mid::drv::Timer::setCount
static void setCount(Size cnt)
Set the counter value.
Definition: timer.hpp:493
bsp::mid::drv::Timer::Channel
static constexpr auto Channel
Timer channel.
Definition: timer.hpp:433
bsp::mid::drv::TimerProfileCounter::getFormattedPassedTime
std::string getFormattedPassedTime() const noexcept
Get the time passed time since the start.
Definition: timer.hpp:361
bsp::mid::drv::TimerEncoder::Direction::Clockwise
@ Clockwise
bsp::mid::drv::TimerProfileCounter::getTimeToOverflow
u64 getTimeToOverflow() const noexcept
Get the time to an overflow.
Definition: timer.hpp:334
bsp::mid::drv::Timer::getPwmFrequency
static u32 getPwmFrequency()
Get the pwm frequency.
Definition: timer.hpp:503
bsp::mid::drv::TimerProfileCounter::formatToString
std::string formatToString(u64 passedTime) const noexcept
Formats the passed time in ns to a string.
Definition: timer.hpp:371
bsp::mid::drv::TimerChannel::startPwm
ALWAYS_INLINE bool startPwm() const noexcept
Start PWM generation for the channel.
Definition: timer.hpp:62
bsp::mid::drv::TimerEncoder::getCount
ALWAYS_INLINE Size getCount() const noexcept
Get the counter value.
Definition: timer.hpp:226
bsp::mid::drv::TimerEncoder::Mode::_96StepsPerTurn
@ _96StepsPerTurn
bsp::mid::drv::TimerEncoder::setMode
ALWAYS_INLINE void setMode(Mode mode) const noexcept
Set the mode of the encoder (48 or 96 counts per turn)
Definition: timer.hpp:254
bsp::mid::drv::TimerEncoder::Direction
Direction
Last known turning direction of the encoder.
Definition: timer.hpp:181
bsp::mid::drv
Definition: adc.hpp:32
bsp::mid::drv::TimerChannel::setDutyCycle
ALWAYS_INLINE bool setDutyCycle(float dutyCycle) const noexcept
Set the duty cycle as a float value.
Definition: timer.hpp:130
bsp::mid::drv::TimerProfileCounter::getFormattedTimeToOverflow
std::string getFormattedTimeToOverflow() const noexcept
Get the time to an overflow.
Definition: timer.hpp:343