Compare commits
2 Commits
ca944cb062
...
e02f7dad6b
Author | SHA1 | Date | |
---|---|---|---|
e02f7dad6b | |||
d16242ecbc |
@ -38,8 +38,8 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <atomic>
|
||||
|
||||
|
534
include/drv/liquid_crystal.h
Normal file
534
include/drv/liquid_crystal.h
Normal file
@ -0,0 +1,534 @@
|
||||
/*!
|
||||
* \file drv/liquid_crystal.h
|
||||
* \brief
|
||||
* A liquid crystal display driver
|
||||
*
|
||||
* \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
|
||||
*
|
||||
* <dl class=\"section copyright\"><dt>License</dt><dd>
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* 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.
|
||||
* </dd></dl>
|
||||
*/
|
||||
|
||||
#ifndef TBX_DRV_LIQUID_CRYSTAL_H_
|
||||
#define TBX_DRV_LIQUID_CRYSTAL_H_
|
||||
|
||||
#include <core/core.h>
|
||||
#include <core/crtp.h>
|
||||
#include <utils/print.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <ctime>
|
||||
|
||||
namespace tbx {
|
||||
|
||||
/*!
|
||||
* \class liquid_crystal
|
||||
* \brief
|
||||
* A CRTP driver class for liquid crystal display with parallel interface,
|
||||
* based on Hitachi HD44780 (Samsung KS0066U, or compatible).
|
||||
*
|
||||
* The driver inherits from Print
|
||||
* The Implementation requirements are:
|
||||
* - void bus_impl(data); To set the 4bit/8bit bus
|
||||
* - void rs_pin_impl(state); To set/clear RS pin
|
||||
* - void en_pin_impl(state); To set/clear EN pin
|
||||
* - void power_pin_impl(state); To set/clear PWR pin
|
||||
* - void bl_pin_impl(state); To set/clear BackLight pin
|
||||
* - void delay_usec_impl(usec); To provide delay in usec
|
||||
*
|
||||
* \tparam Impl_t The derived class
|
||||
* \tparam Lines The lines of the LCD
|
||||
* \tparam Columns The coluns of the LCD
|
||||
*/
|
||||
template <typename Impl_t, size_t Lines, size_t Columns, size_t BusSize =4>
|
||||
class liquid_crystal : public Print<liquid_crystal<Impl_t, Lines, Columns, BusSize>, char>{
|
||||
friend Print<liquid_crystal, char>;
|
||||
_CRTP_IMPL(Impl_t);
|
||||
static_assert((BusSize == 4) || (BusSize == 8), "BusSize must be either 4 or 8.");
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Public enumerator to be used as argument to the init() function.
|
||||
* Selects the font size and thus the number of active lines
|
||||
*/
|
||||
enum class FontSize :uint8_t { dots_5x8, dots_5x10 };
|
||||
private:
|
||||
|
||||
// Commands
|
||||
constexpr static uint8_t Cmd_cls = 0x01;
|
||||
constexpr static uint8_t Cmd_RetHome = 0x02;
|
||||
constexpr static uint8_t Cmd_EntryMode = 0x04;
|
||||
constexpr static uint8_t Cmd_DispCtrl = 0x08;
|
||||
constexpr static uint8_t Cmd_Shift = 0x10;
|
||||
constexpr static uint8_t Cmd_FunSet = 0x20;
|
||||
constexpr static uint8_t Cmd_SetGRamAddr= 0x40;
|
||||
constexpr static uint8_t Cmd_SetDRamAddr= 0x80;
|
||||
|
||||
/*
|
||||
* Entry Mode Set -----> 0 0 0 0 0 1 I/D S
|
||||
* ----------------------------------------------------
|
||||
* I/D = 1 Increment Curs
|
||||
* 0 Decrement
|
||||
* S = 1 Display shift
|
||||
* 0 Not
|
||||
*/
|
||||
constexpr static uint8_t Entry_Right = 0x00;
|
||||
constexpr static uint8_t Entry_Left = 0x02;
|
||||
constexpr static uint8_t Entry_ShiftInc = 0x01;
|
||||
constexpr static uint8_t Entry_ShiftDec = 0x00;
|
||||
|
||||
/*
|
||||
* DispOnOffControll --> 0 0 0 0 1 D C B
|
||||
* -------------------------------------------------
|
||||
* D = Display On
|
||||
* C = Cursor On
|
||||
* B = Blinking On
|
||||
*/
|
||||
constexpr static uint8_t Display_On = 0x04;
|
||||
constexpr static uint8_t Display_Off = 0x00;
|
||||
constexpr static uint8_t Cursor_On = 0x02;
|
||||
constexpr static uint8_t Cursor_Off = 0x00;
|
||||
constexpr static uint8_t Blink_On = 0x01;
|
||||
constexpr static uint8_t Blink_Off = 0x00;
|
||||
|
||||
/*
|
||||
* Cursor/Display Shift --> 0 0 0 1 S/C R/L x x
|
||||
* ---------------------------------------------------
|
||||
* S/C = 1 Display Shift
|
||||
* 0 Cursor Shift
|
||||
* R/L = 1 Shift Right
|
||||
* 0 Shift left
|
||||
*/
|
||||
constexpr static uint8_t DisMove_Display= 0x08;
|
||||
constexpr static uint8_t DisMove_Cursor = 0x00;
|
||||
constexpr static uint8_t DisMove_Right = 0x04;
|
||||
constexpr static uint8_t DisMove_Left = 0x00;
|
||||
|
||||
/*
|
||||
* FunctionSet ------> 0 0 1 DL N F x x
|
||||
* ---------------------------------------------------
|
||||
* DL = 1 8bit
|
||||
* 0 4bit
|
||||
* N = 1 2 lines
|
||||
* 0 1 line
|
||||
* F = 1 5x10 dots
|
||||
* 0 5x8 dots
|
||||
*/
|
||||
constexpr static uint8_t FunSet_8bitMode= 0x10;
|
||||
constexpr static uint8_t FunSet_4bitMode= 0x00;
|
||||
constexpr static uint8_t FunSet_2Line = 0x08;
|
||||
constexpr static uint8_t FunSet_1Line = 0x00;
|
||||
constexpr static uint8_t FunSet_5x10dots= 0x04;
|
||||
constexpr static uint8_t FunSet_5x8dots = 0x00;
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Helper class to keep track of the display cursor. As we dont read the cursor
|
||||
* position on the display, in order to implement backspace operation we need to
|
||||
* keep track the cursor position manually.
|
||||
*/
|
||||
struct Cursor {
|
||||
uint8_t inc_x() noexcept {
|
||||
if (++x_ > Columns) x_ =1;
|
||||
return x_;
|
||||
}
|
||||
uint8_t dec_x() noexcept {
|
||||
if (--x_ < 1) x_ =Columns;
|
||||
return x_;
|
||||
}
|
||||
uint8_t inc_y () noexcept {
|
||||
if (++y_ > max_lines) y_ =1;
|
||||
return y_;
|
||||
}
|
||||
uint8_t dec_y () noexcept {
|
||||
if (--y_ < 1) y_ =max_lines;
|
||||
return y_;
|
||||
}
|
||||
uint8_t operator++() noexcept { return inc_x(); }
|
||||
uint8_t operator--() noexcept { return dec_x(); }
|
||||
void set(uint8_t x, uint8_t y) noexcept {
|
||||
x_ = x;
|
||||
y_ = y;
|
||||
}
|
||||
uint8_t get_x() noexcept { return x_; }
|
||||
uint8_t get_y() noexcept { return y_; }
|
||||
void set_lines (uint8_t l) noexcept { max_lines = l; }
|
||||
private:
|
||||
uint8_t x_, y_;
|
||||
uint8_t max_lines;
|
||||
};
|
||||
private:
|
||||
//! \name Implementation requirements
|
||||
//! @{
|
||||
void BUS (uint8_t data) { impl().bus_impl(data); }
|
||||
void RS_Pin (bool state) { impl().rs_pin_impl(state); }
|
||||
void EN_Pin (bool state) { impl().en_pin_impl(state); }
|
||||
void PWR_Pin (bool state) { impl().power_pin_impl(state); }
|
||||
void BL_Pin (bool state) { impl().bl_pin_impl(state); }
|
||||
void delay_usec(size_t usec) {
|
||||
impl().delay_usec_impl(usec);
|
||||
}
|
||||
//! @}
|
||||
|
||||
//! \name Print interface requirements
|
||||
//! @{
|
||||
size_t write_impl (const char* str, size_t size) {
|
||||
size_t ret =0;
|
||||
while (*str && ret < size) {
|
||||
putchar (*str++);
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
size_t write_impl (const char ch) {
|
||||
return (putchar(ch) == ch) ? 1:0;
|
||||
}
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
//! \name Object lifetime
|
||||
//! @{
|
||||
liquid_crystal() noexcept = default; //!< Construct from derived only
|
||||
//~liquid_crystal() = default;
|
||||
liquid_crystal(const liquid_crystal&) = delete; //!< No copies
|
||||
liquid_crystal& operator= (const liquid_crystal&) = delete; //!< No copies
|
||||
//! @}
|
||||
|
||||
private:
|
||||
//! Send enable pulse to display
|
||||
void pulse_enable () {
|
||||
EN_Pin(0);
|
||||
delay_usec (2); // time to settle BUS pin voltages
|
||||
EN_Pin(1);
|
||||
delay_usec (2); // >450 [nsec]
|
||||
EN_Pin(0);
|
||||
delay_usec (50); // > 37 [usec]
|
||||
}
|
||||
|
||||
//! Writes 4/8bit data to display and pulse the EN pin
|
||||
//! \param data The data to write
|
||||
void write_bits (uint8_t data) {
|
||||
if constexpr (BusSize == 4) BUS (data & 0x0F);
|
||||
else BUS (data);
|
||||
pulse_enable ();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Sends commands or character to display by controlling RS pin
|
||||
* \param data The data to send
|
||||
* \param mode Character/command mode (RS pin state)
|
||||
*/
|
||||
void send (uint8_t data, uint8_t mode) {
|
||||
RS_Pin (mode);
|
||||
if constexpr (BusSize == 4) {
|
||||
write_bits (data >> 4);
|
||||
write_bits (data & 0x0F);
|
||||
} else {
|
||||
write_bits (data);
|
||||
}
|
||||
}
|
||||
//! Send a command to display
|
||||
void command (uint8_t c) { send(c, 0); }
|
||||
//! Send a character to display
|
||||
void character (uint8_t c) { send(c, 1); }
|
||||
|
||||
public:
|
||||
//! \name Public API
|
||||
//! @{
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Initialize the display. After construction the object is valid but reflects the init state
|
||||
* of display configuration. In order for the display to be functional it needs initialization.
|
||||
* So the user has to call this function. This function requires a settled environment, so usually
|
||||
* its called after main().
|
||||
* \param mode 4bit or 8bit mode
|
||||
* \param fontSize 5x8 or 5x10 dots font size.
|
||||
*/
|
||||
void init (FontSize fontSize =FontSize::dots_5x8) {
|
||||
disp_mode_ = disp_mode_init_; // Set values to LCD's startup configuration
|
||||
disp_control_ = disp_control_init_;
|
||||
disp_function_=disp_function_init_;
|
||||
|
||||
// Read user configuration
|
||||
if constexpr (BusSize == 4)
|
||||
// note: keep this runtime, so the disp_function reflects lcd's configuration state
|
||||
disp_function_ &= ~FunSet_8bitMode;
|
||||
else
|
||||
disp_function_ |= FunSet_8bitMode;
|
||||
|
||||
if (fontSize == FontSize::dots_5x10) {
|
||||
disp_function_ |= FunSet_5x10dots;
|
||||
disp_function_ &= ~FunSet_2Line;
|
||||
cursor_.set_lines(Lines>>1);
|
||||
} else {
|
||||
disp_function_ &= ~FunSet_5x10dots;
|
||||
disp_function_ |= FunSet_2Line;
|
||||
cursor_.set_lines(Lines);
|
||||
}
|
||||
|
||||
// start with All-zeros
|
||||
BUS (0); EN_Pin(0); RS_Pin(0); BL_Pin(0);
|
||||
|
||||
delay_usec(100000);
|
||||
if constexpr (BusSize == 4) {
|
||||
// 4bit BUS
|
||||
write_bits (0x03); // 1t try
|
||||
delay_usec(20000);
|
||||
write_bits (0x03); // 2nd try
|
||||
delay_usec(5000);
|
||||
write_bits (0x03); // 3rd try
|
||||
delay_usec(5000);
|
||||
write_bits (0x02); // We set 4 bit interface
|
||||
delay_usec (10000);
|
||||
} else {
|
||||
// 8bit BUS
|
||||
write_bits (Cmd_FunSet | disp_function_); // 1st try
|
||||
delay_usec(20000);
|
||||
write_bits (Cmd_FunSet | disp_function_); // 2nd try
|
||||
delay_usec(5000);
|
||||
write_bits (Cmd_FunSet | disp_function_); // 3rd try
|
||||
delay_usec(5000);
|
||||
}
|
||||
command (Cmd_FunSet | disp_function_); // Finally we set #lines and font size
|
||||
delay_usec(5000);
|
||||
|
||||
command (Cmd_DispCtrl | Display_Off); // Display off
|
||||
delay_usec(5000);
|
||||
command (Cmd_cls); // Clear screen
|
||||
delay_usec(5000);
|
||||
command (Cmd_EntryMode | disp_mode_); // Entry mode set
|
||||
delay_usec(5000);
|
||||
|
||||
command (Cmd_RetHome); // Return home
|
||||
delay_usec(10000);
|
||||
display(true); // Finally display On, done.
|
||||
cursor_.set(1, 1);
|
||||
}
|
||||
|
||||
//! Utility function to enable/disable power to display. This has an effect IFF there is a
|
||||
//! power pin on the board
|
||||
void power (bool en) {
|
||||
PWR_Pin(en);
|
||||
}
|
||||
|
||||
//! Utility function to enable/disable backlight. This has an effect IFF there is a
|
||||
//! backlight pin on the board
|
||||
void backlight (bool en) {
|
||||
BL_Pin(en);
|
||||
}
|
||||
|
||||
//! Utility function to send on/off command to display.
|
||||
void display (bool en) {
|
||||
if (en) disp_control_ |= Display_On;
|
||||
else disp_control_ &= ~Display_On;
|
||||
|
||||
command (Cmd_DispCtrl | disp_control_);
|
||||
delay_usec(100);
|
||||
}
|
||||
|
||||
//! Utility function to enable/disable display cursor.
|
||||
void cursor (bool en) {
|
||||
if (en) disp_control_ |= Cursor_On;
|
||||
else disp_control_ &= ~Cursor_On;
|
||||
|
||||
command (Cmd_DispCtrl | disp_control_);
|
||||
delay_usec(100);
|
||||
}
|
||||
|
||||
//! Utility function to enable/disable cursor blinking.
|
||||
void blink (bool en) {
|
||||
if (en) disp_control_ |= Blink_On;
|
||||
else disp_control_ &= ~Blink_On;
|
||||
|
||||
command (Cmd_DispCtrl | disp_control_);
|
||||
delay_usec(100);
|
||||
}
|
||||
|
||||
//! Utility function to enable/disable autoscroll.
|
||||
void autoscroll (bool en) {
|
||||
if (en) disp_mode_ |= Entry_ShiftInc;
|
||||
else disp_mode_ &= ~Entry_ShiftInc;
|
||||
|
||||
command (Cmd_EntryMode | disp_mode_);
|
||||
delay_usec(100);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Tool to set display cursor
|
||||
* \param x The column position (starting with 1)
|
||||
* \param y The line position (starting with 1 at the top of the display)
|
||||
*/
|
||||
void set_cursor (uint8_t x, uint8_t y) {
|
||||
uint8_t offset;
|
||||
switch (y) {
|
||||
default:
|
||||
case 1: offset = 0x0; break;
|
||||
case 2: offset = 0x40; break;
|
||||
case 3: offset = 0x0 + Columns; break;
|
||||
case 4: offset = 0x40 + Columns; break;
|
||||
}
|
||||
command( Cmd_SetDRamAddr | offset | (x-1));
|
||||
cursor_.set(x, y);
|
||||
}
|
||||
|
||||
//! Utility function to set left to right entry mode.
|
||||
//! \note
|
||||
//! This is the default
|
||||
void set_left_to_right () {
|
||||
disp_mode_ |= Entry_Left;
|
||||
command (Cmd_EntryMode | disp_mode_);
|
||||
delay_usec(100);
|
||||
}
|
||||
|
||||
//! Utility function to set right to left entry mode.
|
||||
void set_right_to_left () {
|
||||
disp_mode_ &= ~Entry_Left;
|
||||
command (Cmd_EntryMode | disp_mode_);
|
||||
delay_usec(100);
|
||||
}
|
||||
|
||||
//! Command to scroll display left one position
|
||||
void scroll_left () {
|
||||
command (Cmd_Shift | DisMove_Display | DisMove_Left);
|
||||
}
|
||||
|
||||
//! Command to scroll display right one position
|
||||
void scroll_right () {
|
||||
command (Cmd_Shift | DisMove_Display | DisMove_Right);
|
||||
}
|
||||
|
||||
//! Clears the display and return home
|
||||
void clear() {
|
||||
command (Cmd_cls);
|
||||
cursor_.set(1, 1);
|
||||
delay_usec(2000);
|
||||
}
|
||||
|
||||
//! return home without clearing the display
|
||||
void home() {
|
||||
command (Cmd_RetHome);
|
||||
cursor_.set(1, 1);
|
||||
delay_usec(2000);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Create custom character and store it to LCD.
|
||||
* \param location The location to store the character [0..7] allowed
|
||||
* \param charmap The character map buffer with the font
|
||||
*/
|
||||
void create_char (uint8_t location, uint8_t charmap[]) {
|
||||
location &= 0x7; // we only have 8 locations 0-7
|
||||
command(Cmd_SetGRamAddr | (location << 3));
|
||||
for (size_t i=0; i<8; ++i) {
|
||||
character(charmap[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Send an ascii character to liquid crystal display.
|
||||
* \param ch the character to send
|
||||
* \return the character send.
|
||||
*
|
||||
* \note
|
||||
* This is the driver's "putchar()" functionality to glue.
|
||||
* Tailor this function to redirect stdout to display.
|
||||
*/
|
||||
int putchar (int ch) {
|
||||
// LCD Character dispatcher
|
||||
switch (ch) {
|
||||
case 0:
|
||||
// don't send null termination to device
|
||||
break;
|
||||
case '\n':
|
||||
cursor_.inc_y();
|
||||
set_cursor (1, cursor_.get_y());
|
||||
break;
|
||||
case '\r':
|
||||
set_cursor (1, cursor_.get_y());
|
||||
break;
|
||||
case '\v':
|
||||
home ();
|
||||
break;
|
||||
case '\f':
|
||||
set_cursor (1, 1);
|
||||
break;
|
||||
case '\b':
|
||||
--cursor_;
|
||||
set_cursor (cursor_.get_x(), cursor_.get_y());
|
||||
character (' ');
|
||||
--cursor_;
|
||||
set_cursor (cursor_.get_x(), cursor_.get_y());
|
||||
break;
|
||||
default:
|
||||
character (ch);
|
||||
++cursor_;
|
||||
break;
|
||||
}
|
||||
//ANSI C (C99) compatible mode
|
||||
return ch;
|
||||
}
|
||||
//! @}
|
||||
|
||||
private:
|
||||
//! \name Data members
|
||||
//! @{
|
||||
|
||||
//! The init entry mode of the display after power up
|
||||
static constexpr uint8_t disp_mode_init_ = Entry_Left | Entry_ShiftDec;
|
||||
//! The init control mode of the display after power up
|
||||
static constexpr uint8_t disp_control_init_ = Display_Off | Cursor_Off | Blink_Off;
|
||||
//! The init function set of the display after power up
|
||||
static constexpr uint8_t disp_function_init_= FunSet_8bitMode | FunSet_1Line | FunSet_5x8dots;
|
||||
|
||||
Cursor cursor_{};
|
||||
uint8_t disp_mode_ {disp_mode_init_};
|
||||
uint8_t disp_control_ {disp_control_init_};
|
||||
uint8_t disp_function_ {disp_function_init_};
|
||||
/*!
|
||||
* \note
|
||||
* When the display powers up, it is configured as follows:
|
||||
* 1. Display clear
|
||||
* 2. Function set: 0x10
|
||||
* DL = 1; 8-bit interface data
|
||||
* N = 0; 1-line display
|
||||
* F = 0; 5x8 dot character font
|
||||
* 3. Display on/off control: 0x00
|
||||
* D = 0; Display off
|
||||
* C = 0; Cursor off
|
||||
* B = 0; Blinking off
|
||||
* 4. Entry mode set: 0x02
|
||||
* I/D = 1; Increment by 1
|
||||
* S = 0; No shift
|
||||
*/
|
||||
|
||||
//! @}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* TBX_DRV_LIQUID_CRYSTAL_H_ */
|
@ -38,8 +38,11 @@
|
||||
#include <cont/equeue.h>
|
||||
|
||||
#include <utils/shared.h>
|
||||
#include <utils/print.h>
|
||||
#include <utils/timer_delay.h>
|
||||
|
||||
#include <drv/cli_device.h>
|
||||
#include <drv/liquid_crystal.h>
|
||||
#include <drv/gpio.h>
|
||||
|
||||
#endif /* TBX_H_ */
|
||||
|
314
include/utils/print.h
Normal file
314
include/utils/print.h
Normal file
@ -0,0 +1,314 @@
|
||||
/*!
|
||||
* \file utils/print.h
|
||||
* \brief
|
||||
* A CRTP base class to provide print interface
|
||||
*
|
||||
* \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
|
||||
*
|
||||
* <dl class=\"section copyright\"><dt>License</dt><dd>
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* 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.
|
||||
* </dd></dl>
|
||||
*/
|
||||
|
||||
#ifndef TBX_UTILS_PRINT_H_
|
||||
#define TBX_UTILS_PRINT_H_
|
||||
|
||||
#include <core/core.h>
|
||||
#include <core/crtp.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <math.h>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <limits>
|
||||
|
||||
namespace tbx {
|
||||
|
||||
/*!
|
||||
* \class Print
|
||||
* \brief
|
||||
* A CRTP print interface
|
||||
*
|
||||
* Requirements:
|
||||
* - size_t write_impl(const Char_t* buffer, size_t size) : Return the number of \c Char_t written
|
||||
* - size_t write_(const Char_t ch) : Return the number of \c Char_t written (normally one).
|
||||
*
|
||||
* \tparam Impl_t The derived type
|
||||
* \tparam Char_t The char type to use
|
||||
*/
|
||||
template <typename Impl_t, typename Char_t>
|
||||
class Print {
|
||||
_CRTP_IMPL(Impl_t);
|
||||
|
||||
public:
|
||||
using value_type = Char_t;
|
||||
using pointer_type = Char_t*;
|
||||
using iterator_type = Char_t*;
|
||||
using const_iterator_type = const Char_t*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = size_t;
|
||||
using str_view_t = std::basic_string_view<Char_t>;
|
||||
|
||||
//! Enumerator for number base formating
|
||||
enum class Base {
|
||||
BIN =2, OCT =8, DEC =10, HEX =16
|
||||
};
|
||||
|
||||
private:
|
||||
//! \name CRTP requirements
|
||||
//! @{
|
||||
size_t write_(const Char_t* buffer, size_t size) {
|
||||
return impl().write_impl(buffer, size);
|
||||
}
|
||||
size_t write_(const Char_t ch) {
|
||||
return impl().write_impl(ch);
|
||||
}
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
Print() noexcept = default; //!< Construct from derived only
|
||||
|
||||
private:
|
||||
//! Helper tool to convert strong enums to their underlying type
|
||||
template <typename E>
|
||||
constexpr typename std::underlying_type_t<E> value(E e) noexcept {
|
||||
return static_cast<typename std::underlying_type_t<E>>(e);
|
||||
}
|
||||
|
||||
size_t print_unsigned(unsigned long n, Base base); // integer conversion base tool
|
||||
size_t print_double(double number, uint8_t digits); // double conversion base tool
|
||||
|
||||
public:
|
||||
/*!
|
||||
* \brief
|
||||
* Prints a string view
|
||||
* \param str The string view to print
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
size_t print(const str_view_t str) {
|
||||
return write_(str.data(), str.size());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Prints a string
|
||||
* \param str Pointer to string to print
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
size_t print(const Char_t* str) {
|
||||
if (str == nullptr)
|
||||
return 0;
|
||||
return write_(str, std::strlen(str));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Prints a buffer of size \c size. If there is a null termination
|
||||
* before the end of the buffer, prints up to termination.
|
||||
*
|
||||
* \param str Pointer to string buffer to print
|
||||
* \param size The size of buffer
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
size_t print(const Char_t* str, size_t size) {
|
||||
if (str == nullptr)
|
||||
return 0;
|
||||
return write_(str, size);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Prints a \c Char_t
|
||||
* \param ch The Char_t to print
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
size_t print(Char_t ch) {
|
||||
return write_ (ch);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Convert and print a long.
|
||||
* \param n The number to print
|
||||
* \param base The number base to use for conversion.
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
size_t print(long n, Base base =Base::DEC) {
|
||||
size_t cnt =0;
|
||||
if (n < 0) {
|
||||
n = -n;
|
||||
cnt = write_ ('-');
|
||||
}
|
||||
return cnt + print_unsigned((unsigned long)n, base);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Convert and print an int.
|
||||
* \param n The number to print
|
||||
* \param base The number base to use for conversion.
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
size_t print(int n, Base base= Base::DEC) {
|
||||
return print ((long)n, base);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Convert and print an unsigned long.
|
||||
* \param n The number to print
|
||||
* \param base The number base to use for conversion.
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
size_t print(unsigned long n, Base base= Base::DEC) {
|
||||
return print_unsigned ((unsigned long)n, base);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Convert and print an unsigned int.
|
||||
* \param n The number to print
|
||||
* \param base The number base to use for conversion.
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
size_t print(unsigned int n, Base base= Base::DEC) {
|
||||
return print_unsigned ((unsigned long)n, base);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Convert and print adouble
|
||||
* \param n The number to print
|
||||
* \param digits The number of decimal digits to print
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
size_t print(double n, uint8_t digits = 2) {
|
||||
return print_double (n, digits);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Perfect forwarder to print functionality with a new line termination
|
||||
* \tparam Ts The types of parameters
|
||||
* \param args The arguments to pass
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
template <typename ...Ts>
|
||||
size_t println(Ts&& ...args) {
|
||||
size_t r = print (std::forward<Ts>(args)...);
|
||||
r += write_ ('\n');
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Converts and prints an unsigned long
|
||||
*
|
||||
* \tparam Impl_t The derived type
|
||||
* \tparam Char_t The char type to use
|
||||
*
|
||||
* \param n The number to print
|
||||
* \param base The number base to use
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
template <typename Impl_t, typename Char_t>
|
||||
size_t Print<Impl_t, Char_t>::print_unsigned(unsigned long n, Base base) {
|
||||
Char_t buf[8 *sizeof(Char_t) * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
|
||||
Char_t *str = &buf[sizeof(buf) - 1];
|
||||
|
||||
*str = '\0';
|
||||
|
||||
do {
|
||||
Char_t c = n % value(base);
|
||||
n /= value(base);
|
||||
|
||||
*--str = c < 10 ? c + '0' : c + 'A' - 10;
|
||||
} while(n);
|
||||
|
||||
return write_(str, std::strlen(str));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Converts and prints a double
|
||||
*
|
||||
* \note
|
||||
* Internally, this implementation uses a long to store the integer part of the number.
|
||||
* Thus overflows for numbers bigger than std::numeric_limits<long>::max() / min().
|
||||
* For these numbers it prints "ovf" instead.
|
||||
*
|
||||
* \tparam Impl_t The derived type
|
||||
* \tparam Char_t The char type to use
|
||||
*
|
||||
* \param n The number to print
|
||||
* \param digits The number of decimal digits to print
|
||||
* \return The number of printed \c Char_t
|
||||
*/
|
||||
template <typename Impl_t, typename Char_t>
|
||||
size_t Print<Impl_t, Char_t>::print_double(double number, uint8_t digits) {
|
||||
size_t n = 0;
|
||||
|
||||
if (std::isnan(number)) return print("nan");
|
||||
if (std::isinf(number)) return print("inf");
|
||||
if (number > (double)std::numeric_limits<long>::max())
|
||||
return print ("ovf");
|
||||
if (number < (double)std::numeric_limits<long>::min())
|
||||
return print ("-ovf");
|
||||
|
||||
// Handle negative numbers
|
||||
if (number < 0.0) {
|
||||
n += write_ ('-');
|
||||
number = -number;
|
||||
}
|
||||
|
||||
// Round correctly so that print(1.999, 2) prints as "2.00"
|
||||
double rounding = 0.5;
|
||||
for (uint8_t i=0; i<digits; ++i)
|
||||
rounding /= 10.0;
|
||||
|
||||
number += rounding;
|
||||
|
||||
// Extract the integer part of the number and print it
|
||||
unsigned long int_part = (unsigned long)number;
|
||||
double remainder = number - (double)int_part;
|
||||
n += print(int_part);
|
||||
|
||||
// Print the decimal point, but only if there are digits beyond
|
||||
if (digits > 0) {
|
||||
n += print('.');
|
||||
}
|
||||
|
||||
// Extract digits from the remainder one at a time
|
||||
while (digits-- > 0) {
|
||||
remainder *= 10.0;
|
||||
unsigned int toPrint = (unsigned int)(remainder);
|
||||
n += print(toPrint);
|
||||
remainder -= toPrint;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif /* TBX_UTILS_PRINT_H_ */
|
333
include/utils/timer_delay.h
Normal file
333
include/utils/timer_delay.h
Normal file
@ -0,0 +1,333 @@
|
||||
/*!
|
||||
* \file utils/timer_delay.h
|
||||
* \brief
|
||||
* A CRTP timer delay utility
|
||||
*
|
||||
* \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
|
||||
*
|
||||
* <dl class=\"section copyright\"><dt>License</dt><dd>
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* 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.
|
||||
* </dd></dl>
|
||||
*/
|
||||
|
||||
#ifndef TBX_UTILS_TIMER_DELAY_H_
|
||||
#define TBX_UTILS_TIMER_DELAY_H_
|
||||
|
||||
#include <core/core.h>
|
||||
#include <core/crtp.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace tbx {
|
||||
|
||||
/*!
|
||||
* \class timer_delay
|
||||
* \brief
|
||||
* A CRTP hw timer based, delay implementation.
|
||||
*
|
||||
* CRTP requirements:
|
||||
* - int set_frequency_impl (size_t freq, Counter_t ticks)
|
||||
* Initialize and start the hw timer with tick frequency \c freq and reload value \c ticks
|
||||
* - volatile Counter_t* get_value_ptr_impl (Counter_t discard)
|
||||
* Return a pointer to hw timer counter value register. The \c discard argument should discarded.
|
||||
*
|
||||
* \tparam Impl_t The derived class
|
||||
* \tparam Counter_t The hw timer's type
|
||||
*/
|
||||
template <typename Impl_t, typename Counter_t>
|
||||
class timer_delay {
|
||||
_CRTP_IMPL(Impl_t);
|
||||
using value_t = volatile Counter_t;
|
||||
using marker_t = std::make_signed_t<std::remove_cv_t<Counter_t>>;
|
||||
|
||||
//! \name CRTP requirements
|
||||
//! @{
|
||||
private:
|
||||
int set_frequency (size_t freq, Counter_t ticks) {
|
||||
return impl().set_frequency_impl(freq, ticks);
|
||||
}
|
||||
volatile Counter_t* get_value_ptr (Counter_t discard = Counter_t{}) {
|
||||
return impl().get_value_ptr_impl (discard);
|
||||
}
|
||||
//! @}
|
||||
|
||||
//! \name Object lifetime
|
||||
//! @{
|
||||
protected:
|
||||
//! \brief
|
||||
//! Create and initialize
|
||||
//! \param freq The required hw timer's frequency
|
||||
//! \param ticks The required timer's reload value
|
||||
timer_delay (size_t freq, Counter_t ticks) {
|
||||
init (freq, ticks);
|
||||
}
|
||||
timer_delay() noexcept = default; //!< Default object is valid, but non-usable
|
||||
timer_delay(const timer_delay&) = delete; //!< No copies
|
||||
timer_delay& operator=(const timer_delay&) = delete; //!< No copies
|
||||
//! \note
|
||||
//! We are not initializing the timer via default ctor, in order to be able to
|
||||
//! declare a timer_delay object globally and initialize it after the call to main().
|
||||
//! @}
|
||||
|
||||
private:
|
||||
//! Period to frequency compile time tool
|
||||
constexpr Counter_t period2freq (double period) noexcept {
|
||||
return (Counter_t)(1 / period);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Return the systems best approximation for ticks per msec
|
||||
* \return The calculated value or zero if no calculation can apply
|
||||
*/
|
||||
Counter_t ticks_per_msec () {
|
||||
Counter_t tck = (Counter_t)(frequency / period2freq(0.001));
|
||||
return (tck <= 1) ? 1 : tck;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Return the systems best approximation for ticks per usec
|
||||
* \return The calculated value or zero if no calculation can apply
|
||||
*/
|
||||
Counter_t ticks_per_usec () {
|
||||
Counter_t tck = (Counter_t)(frequency / period2freq(0.000001));
|
||||
return (tck <= 1) ? 1 : tck;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Return the systems best approximation for ticks per usec
|
||||
* \return The calculated value or zero if no calculation can apply
|
||||
*/
|
||||
Counter_t ticks_per_100nsec () {
|
||||
Counter_t tck = (Counter_t)(frequency / period2freq(0.0000001));
|
||||
return (tck <= 1) ? 1 : tck;
|
||||
}
|
||||
|
||||
public:
|
||||
/*!
|
||||
* \brief
|
||||
* Initializes both object members and hw timer.
|
||||
*
|
||||
* \param freq The required hw timer's frequency
|
||||
* \param ticks The required timer's reload value
|
||||
* \return
|
||||
*/
|
||||
bool init (size_t freq, Counter_t ticks) {
|
||||
if (set_frequency(freq, ticks))
|
||||
return false;
|
||||
volatile Counter_t* v = get_value_ptr();
|
||||
|
||||
value = (v != nullptr) ? v : value;
|
||||
frequency = freq;
|
||||
max_ticks = ticks;
|
||||
tp1ms = ticks_per_msec();
|
||||
tp1us = ticks_per_usec();
|
||||
tp100ns= ticks_per_100nsec();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* A code based delay implementation, using hw timer for timing.
|
||||
* This is NOT accurate but it ensures that the time passed is always
|
||||
* more than the requested value.
|
||||
* The delay values are multiplications of 1 msec.
|
||||
* \param msec Time in msec for delay
|
||||
*/
|
||||
void delay_ms (int msec) {
|
||||
marker_t m, m2, m1 = (marker_t)*value;
|
||||
|
||||
msec *= tp1ms;
|
||||
|
||||
// Eat the time difference from msec value.
|
||||
do {
|
||||
m2 = (marker_t)(*value);
|
||||
m = m2 - m1;
|
||||
msec -= (m>=0) ? m : max_ticks + m;
|
||||
m1 = m2;
|
||||
} while (msec>0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* A code based delay implementation, using hw timer for timing.
|
||||
* This is NOT accurate but it ensures that the time passed is always
|
||||
* more than the requested value.
|
||||
* The delay values are multiplications of 1 usec.
|
||||
* \param usec Time in usec for delay
|
||||
*/
|
||||
void delay_us (int usec) {
|
||||
marker_t m, m2, m1 = (marker_t)*value;
|
||||
|
||||
usec *= tp1us;
|
||||
if ((marker_t)(*value) - m1 > usec) // Very small delays may return here.
|
||||
return;
|
||||
|
||||
// Eat the time difference from usec value.
|
||||
do {
|
||||
m2 = (marker_t)(*value);
|
||||
m = m2 - m1;
|
||||
usec -= (m>=0) ? m : max_ticks + m;
|
||||
m1 = m2;
|
||||
} while (usec>0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* A code based delay implementation, using hw timer for timing.
|
||||
* This is NOT accurate but it ensures that the time passed is always
|
||||
* more than the requested value.
|
||||
* The delay values are multiplications of 100 nsec.
|
||||
* \param _100nsec Time in 100nsec for delay
|
||||
*/
|
||||
void delay_100ns (int _100nsec) {
|
||||
marker_t m, m2, m1 = (marker_t)*value;
|
||||
|
||||
_100nsec *= tp100ns;
|
||||
if ((marker_t)(*value) - m1 > _100nsec) // Very small delays may return here.
|
||||
return;
|
||||
|
||||
// Eat the time difference from _100nsec value.
|
||||
do {
|
||||
m2 = (marker_t)(*value);
|
||||
m = m2 - m1;
|
||||
_100nsec -= (m>=0) ? m : max_ticks + m;
|
||||
m1 = m2;
|
||||
} while (_100nsec>0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* A code based polling version delay implementation, using hw timer for timing.
|
||||
* This is NOT accurate but it ensures that the time passed is always
|
||||
* more than the requested value.
|
||||
* The delay values are multiplications of 1 msec.
|
||||
* \param msec Time in msec for delay
|
||||
* \return The status of ongoing delay
|
||||
* \arg false: Delay time has passed
|
||||
* \arg true: Delay is ongoing, keep calling
|
||||
*/
|
||||
bool check_msec (int msec) {
|
||||
static marker_t m1=-1, cnt;
|
||||
marker_t m, m2;
|
||||
|
||||
if (m1 == -1) {
|
||||
m1 = *value;
|
||||
cnt = tp1ms * msec;
|
||||
}
|
||||
|
||||
// Eat the time difference from msec value.
|
||||
if (cnt>0) {
|
||||
m2 = (marker_t)(*value);
|
||||
m = m2-m1;
|
||||
cnt -= (m>=0) ? m : max_ticks + m;
|
||||
m1 = m2;
|
||||
return 1; // wait
|
||||
} else {
|
||||
m1 = -1;
|
||||
return 0; // do not wait any more
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* A code based polling version delay implementation, using hw timer for timing.
|
||||
* This is NOT accurate but it ensures that the time passed is always
|
||||
* more than the requested value.
|
||||
* The delay values are multiplications of 1 usec.
|
||||
* \param usec Time in usec for delay
|
||||
* \return The status of ongoing delay
|
||||
* \arg false: Delay time has passed
|
||||
* \arg true: Delay is ongoing, keep calling
|
||||
*/
|
||||
bool check_usec (int usec) {
|
||||
static marker_t m1=-1, cnt;
|
||||
marker_t m, m2;
|
||||
|
||||
if (m1 == -1) {
|
||||
m1 = *value;
|
||||
cnt = tp1us * usec;
|
||||
}
|
||||
|
||||
// Eat the time difference from usec value.
|
||||
if (cnt>0) {
|
||||
m2 = (marker_t)(*value);
|
||||
m = m2-m1;
|
||||
cnt -= (m>=0) ? m : max_ticks + m;
|
||||
m1 = m2;
|
||||
return 1; // wait
|
||||
} else {
|
||||
m1 = -1;
|
||||
return 0; // do not wait any more
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* A code based polling version delay implementation, using hw timer for timing.
|
||||
* This is NOT accurate but it ensures that the time passed is always
|
||||
* more than the requested value.
|
||||
* The delay values are multiplications of 100 nsec.
|
||||
* \param
|
||||
* _100nsec Time in 100nsec for delay
|
||||
* \return The status of ongoing delay
|
||||
* \arg false: Delay time has passed
|
||||
* \arg true: Delay is ongoing, keep calling
|
||||
*/
|
||||
bool check_100nsec (int _100nsec) {
|
||||
static marker_t m1=-1, cnt;
|
||||
marker_t m, m2;
|
||||
|
||||
if (m1 == -1) {
|
||||
m1 = *value;
|
||||
cnt = tp100ns * _100nsec;
|
||||
}
|
||||
|
||||
// Eat the time difference from _100nsec value.
|
||||
if (cnt>0) {
|
||||
m2 = (marker_t)(*value);
|
||||
m = m2-m1;
|
||||
cnt -= (m>=0) ? m : max_ticks + m;
|
||||
m1 = m2;
|
||||
return 1; // wait
|
||||
}
|
||||
else {
|
||||
m1 = -1;
|
||||
return 0; // do not wait any more
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr Counter_t zero = 0; //!< A always zero place
|
||||
|
||||
value_t* value {(value_t*)&zero}; //!< Pointer to hw timer's counter register
|
||||
//!< We initialize it to &zero to avoid nullptr dereference
|
||||
size_t frequency{}; //!< The frequency of the timer
|
||||
Counter_t max_ticks{}; //!< The reload value of the timer
|
||||
Counter_t tp1ms{}; //!< ticks per ms temporary variable
|
||||
Counter_t tp1us{}; //!< ticks per us temporary variable
|
||||
Counter_t tp100ns{}; //!< ticks per 100ns temporary variable
|
||||
};
|
||||
|
||||
|
||||
} // namespace tbx
|
||||
|
||||
#endif /* TBX_UTILS_TIMER_DELAY_H_ */
|
Loading…
x
Reference in New Issue
Block a user