Compare commits
9
Commits
4bce68697f
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
795655edda | ||
|
|
c84fd4a614 | ||
|
|
43cfb349e9 | ||
|
|
be77dffa01 | ||
|
|
fce49ee963 | ||
|
|
887bbf391e | ||
|
|
e02f7dad6b | ||
|
|
d16242ecbc | ||
|
|
ca944cb062 |
+40
-9
@@ -115,8 +115,9 @@ class sequencer {
|
||||
EXPECT, //!< Expects data from implementation via get()
|
||||
OR_EXPECT, //!< Expects data from implementation via get() in conjunction with previous EXPECT
|
||||
DETECT, //!< Detects data into rx buffer without receiving them via contents()
|
||||
OR_DETECT //!< Detects data into rx buffer without receiving them via contents() in conjunction with
|
||||
OR_DETECT, //!< Detects data into rx buffer without receiving them via contents() in conjunction with
|
||||
//!< previous DETECT
|
||||
OTHERWISE //!< An "else" path if the EXPECT[, OR_EXPECT[, OR_EXPECT ... ]] block timesout.
|
||||
|
||||
//! \note
|
||||
//! The \c DETECT extra incoming channel serve the purpose of sneak into receive
|
||||
@@ -346,6 +347,7 @@ class sequencer {
|
||||
case control_t::OR_EXPECT: skip_while = control_t::OR_EXPECT; break;
|
||||
case control_t::DETECT:
|
||||
case control_t::OR_DETECT: skip_while = control_t::OR_DETECT; break;
|
||||
case control_t::OTHERWISE: skip_while = control_t::OTHERWISE; break;
|
||||
}
|
||||
s = step;
|
||||
while (script[++s].control == skip_while)
|
||||
@@ -357,6 +359,17 @@ class sequencer {
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t Steps>
|
||||
size_t expect_end (const script_t<Steps>& script, size_t step) {
|
||||
while ((++step < Steps) && (script[step].control == control_t::OR_EXPECT)) ;
|
||||
return step;
|
||||
}
|
||||
|
||||
template <size_t Steps>
|
||||
size_t detect_end (const script_t<Steps>& script, size_t step) {
|
||||
while ((++step < Steps) && (script[step].control == control_t::OR_DETECT)) ;
|
||||
return step;
|
||||
}
|
||||
//! @}
|
||||
|
||||
|
||||
@@ -452,34 +465,52 @@ class sequencer {
|
||||
case control_t::OR_EXPECT:
|
||||
resp_size = get_(buffer);
|
||||
if (resp_size) {
|
||||
size_t s = step ; do{
|
||||
for (size_t s = step ; s < expect_end(script, step) ; ++s) {
|
||||
if (script[s].match != nullptr && script[s].match({buffer, resp_size}, script[s].token)) {
|
||||
handle_ (script[s].handler, {buffer, resp_size});
|
||||
std::tie(step, status) = action_ (script, s);
|
||||
break;
|
||||
}
|
||||
} while ((++s < Steps) && (script[s].control == control_t::OR_EXPECT));
|
||||
}
|
||||
}
|
||||
if (record.timeout && (clock_() - mark) >= record.timeout) {
|
||||
size_t s = expect_end(script, step);
|
||||
if ((s < Steps) && (script[s].control == control_t::OTHERWISE)) {
|
||||
handle_ (script[s].handler, {buffer, resp_size});
|
||||
std::tie(step, status) = action_ (script, s);
|
||||
} else {
|
||||
return exit_error.value;
|
||||
}
|
||||
}
|
||||
if (record.timeout && (clock_() - mark) >= record.timeout)
|
||||
return exit_error.value;
|
||||
break;
|
||||
|
||||
case control_t::DETECT:
|
||||
case control_t::OR_DETECT:
|
||||
resp_size = contents_(buffer);
|
||||
if (resp_size) {
|
||||
size_t s = step ; do {
|
||||
for (size_t s = step ; s < detect_end(script, step) ; ++s) {
|
||||
if (script[s].match != nullptr && script[s].match({buffer, resp_size}, script[s].token)) {
|
||||
handle_ (script[s].handler, {buffer, resp_size});
|
||||
std::tie(step, status) = action_ (script, s);
|
||||
break;
|
||||
}
|
||||
} while ((++s < Steps) && (script[s].control == control_t::OR_DETECT));
|
||||
}
|
||||
}
|
||||
if (record.timeout && (clock_() - mark) >= record.timeout) {
|
||||
size_t s = detect_end(script, step);
|
||||
if ((s < Steps) && (script[s].control == control_t::OTHERWISE)) {
|
||||
handle_ (script[s].handler, {buffer, resp_size});
|
||||
std::tie(step, status) = action_ (script, s);
|
||||
} else {
|
||||
return exit_error.value;
|
||||
}
|
||||
}
|
||||
if (record.timeout && (clock_() - mark) >= record.timeout)
|
||||
return exit_error.value;
|
||||
break;
|
||||
|
||||
case control_t::OTHERWISE:
|
||||
handle_ (script[step].handler, {buffer, resp_size});
|
||||
std::tie(step, status) = action_ (script, step);
|
||||
break;
|
||||
} // switch (record.control)
|
||||
|
||||
} while ( status == seq_status_t::CONTINUE);
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
#include <core/core.h>
|
||||
#include <core/ring_iterator.h>
|
||||
|
||||
#include <cont/range.h>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
|
||||
+67
-62
@@ -38,8 +38,8 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <atomic>
|
||||
|
||||
@@ -124,7 +124,8 @@ class cli_device
|
||||
//! Publish delimiter
|
||||
constexpr static char delimiter = Delimiter;
|
||||
|
||||
enum flush_type { keep =0, flush };
|
||||
enum Flush_t { Keep =0, Flush };
|
||||
enum Receive_t { Get =0, Detect };
|
||||
|
||||
//! Required types for inetd async handler operation
|
||||
//! @{
|
||||
@@ -247,7 +248,7 @@ class cli_device
|
||||
* \param token Pointer to store the parsed tokens
|
||||
* \return A (number of characters parsed, marker found) pair
|
||||
*/
|
||||
template <char Marker = '%'>
|
||||
template <char Marker>
|
||||
std::pair<size_t, bool> parse_ (const char* expected, const string_view buffer, char* token) {
|
||||
do {
|
||||
if (*expected == Marker) {
|
||||
@@ -273,53 +274,23 @@ class cli_device
|
||||
return std::make_pair(0, false);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Analyze the response of a command based on \c expected.
|
||||
*
|
||||
* Tries to receive data with timeout and match them against expected string_view.
|
||||
* For each Marker inside the expected string the value gets extracted, converted and
|
||||
* copied to \c vargs pointer array.
|
||||
*
|
||||
* \param expected The expected string view
|
||||
* \param timeout the timeout in CPU time
|
||||
* \param vargs Pointer to variable arguments array
|
||||
* \param nargs Size of variable arguments array
|
||||
* \return
|
||||
*/
|
||||
template<char Marker = '%', typename T>
|
||||
bool response_ (const string_view expected, clock_t timeout, T* vargs, size_t nargs) {
|
||||
char buffer[N], token[N], *pbuffer = buffer;
|
||||
|
||||
size_t v =0, sz =0;
|
||||
for (auto ex = expected.begin() ; ex != expected.end() ; ) {
|
||||
clock_t mark = clock(); // mark the time
|
||||
while (sz <= 0) { // if buffer is empty get buffer with timeout
|
||||
sz = receive(buffer);
|
||||
pbuffer = buffer;
|
||||
if ((timeout != 0 )&& ((clock() - mark) >= timeout))
|
||||
return false;
|
||||
}
|
||||
// try to parse
|
||||
auto [step, marker] = parse_<Marker> (ex, {pbuffer, sz}, token);
|
||||
if (!step)
|
||||
return false; // discard buffer and fail
|
||||
|
||||
if (marker && v < nargs)
|
||||
extract_(token, vargs[v++]);
|
||||
|
||||
pbuffer += step;
|
||||
sz -= (step <= sz) ? step: sz;
|
||||
++ex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//! @}
|
||||
|
||||
//! \name public functionality
|
||||
//! @{
|
||||
public:
|
||||
|
||||
//! Clears the incoming data buffer
|
||||
void clear () noexcept {
|
||||
rx_q.clear();
|
||||
streams_.store(size_t(0), std::memory_order_release);
|
||||
}
|
||||
|
||||
//! \return Returns the size of the incoming data buffer
|
||||
size_t size() noexcept {
|
||||
return rx_q.size();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Transmit data to modem
|
||||
@@ -370,14 +341,48 @@ class cli_device
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Clears the incoming data buffer
|
||||
void clear () noexcept {
|
||||
rx_q.clear();
|
||||
}
|
||||
/*!
|
||||
* Analyze the response of a command based on \c expected.
|
||||
*
|
||||
* Tries to receive data via get() path with timeout and match them against expected string_view.
|
||||
* For each Marker inside the expected string the value gets extracted, converted and
|
||||
* copied to \c vargs pointer array.
|
||||
*
|
||||
* \param expected The expected string view
|
||||
* \param timeout the timeout in CPU time
|
||||
* \param vargs Pointer to variable arguments array
|
||||
* \param nargs Size of variable arguments array
|
||||
* \return
|
||||
*/
|
||||
template<Receive_t Recv, char Marker, typename T>
|
||||
bool response (const string_view expected, clock_t timeout, T* vargs, size_t nargs) {
|
||||
char buffer[N], token[N], *pbuffer = buffer;
|
||||
|
||||
//! \return Returns the size of the incoming data buffer
|
||||
size_t size() noexcept {
|
||||
return rx_q.size();
|
||||
size_t v =0, sz =0;
|
||||
for (auto ex = expected.begin() ; ex != expected.end() ; ) {
|
||||
clock_t mark = clock(); // mark the time
|
||||
while (sz <= 0) { // if buffer is empty get buffer with timeout
|
||||
if constexpr (Recv == Get)
|
||||
sz = receive(buffer);
|
||||
else
|
||||
sz = contents(buffer);
|
||||
pbuffer = buffer;
|
||||
if ((timeout != 0 )&& ((clock() - mark) >= timeout))
|
||||
return false;
|
||||
}
|
||||
// try to parse
|
||||
auto [step, marker] = parse_<Marker> (ex, {pbuffer, sz}, token);
|
||||
if (!step)
|
||||
return false; // discard buffer and fail
|
||||
|
||||
if (marker && v < nargs)
|
||||
extract_(token, vargs[v++]);
|
||||
|
||||
pbuffer += step;
|
||||
sz -= (step <= sz) ? step: sz;
|
||||
++ex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -387,7 +392,7 @@ class cli_device
|
||||
* This function executes 3 steps.
|
||||
* - Clears the incoming buffer if requested by template parameter
|
||||
* - Sends the command to device
|
||||
* - Waits to get the response and parse it accordingly to \c expected \see response_()
|
||||
* - Waits to get the response and parse it accordingly to \c expected \see response()
|
||||
*
|
||||
* The user can mark spots inside the expected string using the \c Marker ['%'] character.
|
||||
* These spots will be extracted to tokens upon parsing. If the user passes \c values parameters,
|
||||
@@ -399,7 +404,7 @@ class cli_device
|
||||
* \param timeout The timeout in CPU time (leave it for 0 - no timeout)
|
||||
* \param values The value pointer arguments to get the converted tokens
|
||||
*
|
||||
* \tparam Flush Flag to indicate if we flush the buffer before command or not
|
||||
* \tparam Flush Flag to indicate if we Flush the buffer before command or not
|
||||
* \tparam Marker The marker character
|
||||
* \tparam Ts The type of the values to read from response marked with \c Marker
|
||||
* \warning The types MUST be the same
|
||||
@@ -416,31 +421,31 @@ class cli_device
|
||||
* cli.command("AT+CREG?\r\n", "%%%OK\r\n", 1000);
|
||||
*
|
||||
* // extract a number from response without timeout (blocking)
|
||||
* cli.command<flush>("AT+CREG?\r\n", "\r\n+CREG: 0,%\r\n\r\nOK\r\n", 0, &status);
|
||||
* cli.command<Flush>("AT+CREG?\r\n", "\r\n+CREG: 0,%\r\n\r\nOK\r\n", 0, &status);
|
||||
*
|
||||
* // extract a number and discard the last 2 lines
|
||||
* cli.command<flush>("AT+CREG?\r\n", "\r\n+CREG: 0,%\r\n%%", 1000, &status);
|
||||
* cli.command<Flush>("AT+CREG?\r\n", "\r\n+CREG: 0,%\r\n%%", 1000, &status);
|
||||
*
|
||||
* // discard first line, read the 2nd to str, discard the 3rd line.
|
||||
* // expect the last to be "OK\r\n"
|
||||
* cli.command<flush>("AT+CREG?\r\n", "", 100000);
|
||||
* cli.command<keep>("", "%", 1000);
|
||||
* cli.command<keep>("", "%%", 1000, str);
|
||||
* cli.command<keep>("", "OK\r\n", 1000);
|
||||
* cli.command<Flush>("AT+CREG?\r\n", "", 100000);
|
||||
* cli.command<Keep>("", "%", 1000);
|
||||
* cli.command<Keep>("", "%%", 1000, str);
|
||||
* cli.command<Keep>("", "OK\r\n", 1000);
|
||||
* \endcode
|
||||
*/
|
||||
template<flush_type Flush =flush, char Marker = '%', typename ...Ts>
|
||||
template<Receive_t Recv =Get, Flush_t Flsh =Flush, char Marker = '%', typename ...Ts>
|
||||
bool command (const string_view cmd, const string_view expected, clock_t timeout, Ts* ...values) {
|
||||
constexpr size_t Nr = sizeof...(Ts);
|
||||
|
||||
front<typelist<Ts...>>* vargs[Nr] = {values...}; // read all args to local buffer
|
||||
if constexpr (Flush == flush) {
|
||||
if constexpr (Flsh == Flush) {
|
||||
clear ();
|
||||
}
|
||||
if (transmit(cmd.data(), cmd.size()) != cmd.size()) // send command
|
||||
return false;
|
||||
// parse the response and return the status
|
||||
return response_<Marker>(expected, timeout, vargs, Nr);
|
||||
return response<Recv, Marker>(expected, timeout, vargs, Nr);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -0,0 +1,536 @@
|
||||
/*!
|
||||
* \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
|
||||
* \tparam BusSize The LCD bus size (4 or 8 bits)
|
||||
*/
|
||||
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:
|
||||
constexpr static size_t lines = Lines;
|
||||
constexpr static size_t columns = Columns;
|
||||
/*!
|
||||
* 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':
|
||||
clear();
|
||||
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_ */
|
||||
@@ -0,0 +1,720 @@
|
||||
/*!
|
||||
* \file drv/sd_spi.h
|
||||
* \brief
|
||||
* SD card driver using SPI 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_DRV_SD_SPI_H_
|
||||
#define TBX_DRV_SD_SPI_H_
|
||||
|
||||
#include <core/core.h>
|
||||
#include <core/crtp.h>
|
||||
|
||||
//#include <drv/diskio.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <utility>
|
||||
|
||||
namespace tbx {
|
||||
|
||||
/*!
|
||||
*
|
||||
* http://elm-chan.org/docs/mmc/mmc_e.html
|
||||
*
|
||||
* CRTP requirements
|
||||
* bool WP_impl (); // write protect, true => write protect
|
||||
* bool CD_impl (); // check disk present, true => present
|
||||
* void CS_impl (bool select); // Chip select, true => select
|
||||
* void PWR_impl(bool state); // SD power, true => power the card
|
||||
* data_type SPI_rw_impl (data_type); // SPI read-write functionality
|
||||
* bool SPI_set_clk_impl(uint32_t clk); // SPI set clock functionality
|
||||
* clock_t clock_impl(); // get system's CPU time
|
||||
*/
|
||||
template <typename Impl_t>
|
||||
class sd_card {
|
||||
_CRTP_IMPL(Impl_t);
|
||||
|
||||
using data_type = uint8_t;
|
||||
|
||||
// Driver settings
|
||||
constexpr static clock_t SD_WaitTimeout = 500; // 500 [CPU time]
|
||||
constexpr static clock_t SD_PowerTimeout= 250; // 250 [CPU time]
|
||||
constexpr static clock_t SD_RxTimeout = 100; // 100 [CPU time]
|
||||
constexpr static clock_t SD_InitTimeout = 2000; // 2000 [CPU time]
|
||||
constexpr static uint32_t MaxInitClock = 400000; // 400000 [Hz]
|
||||
|
||||
// MMC/SDC definitions
|
||||
constexpr static data_type CMD_MSB = 0x40;
|
||||
constexpr static data_type CMD_CRC_LSB = 0x01;
|
||||
|
||||
constexpr static data_type CMD0 = (CMD_MSB | 0); //!< GO_IDLE_STATE
|
||||
constexpr static data_type CMD1 = (CMD_MSB | 1); //!< SEND_OP_COND (MMC)
|
||||
constexpr static data_type CMD8 = (CMD_MSB | 8); //!< SEND_IF_COND
|
||||
constexpr static data_type CMD9 = (CMD_MSB | 9); //!< SEND_CSD
|
||||
constexpr static data_type CMD10 = (CMD_MSB | 10); //!< SEND_CID
|
||||
constexpr static data_type CMD12 = (CMD_MSB | 12); //!< STOP_TRANSMISSION
|
||||
constexpr static data_type CMD16 = (CMD_MSB | 16); //!< SET_BLOCKLEN
|
||||
constexpr static data_type CMD17 = (CMD_MSB | 17); //!< READ_SINGLE_BLOCK
|
||||
constexpr static data_type CMD18 = (CMD_MSB | 18); //!< READ_MULTIPLE_BLOCK
|
||||
constexpr static data_type CMD23 = (CMD_MSB | 23); //!< SET_BLOCK_COUNT (MMC)
|
||||
constexpr static data_type CMD24 = (CMD_MSB | 24); //!< WRITE_BLOCK
|
||||
constexpr static data_type CMD25 = (CMD_MSB | 25); //!< WRITE_MULTIPLE_BLOCK
|
||||
constexpr static data_type CMD55 = (CMD_MSB | 55); //!< APP_CMD
|
||||
constexpr static data_type CMD58 = (CMD_MSB | 58); //!< READ_OCR
|
||||
|
||||
constexpr static data_type ACMD13 = (0xC0 + 13); //!< SD_STATUS (SDC)
|
||||
constexpr static data_type ACMD23 = (0xC0 + 23); //!< SET_WR_BLK_ERASE_COUNT (SDC)
|
||||
constexpr static data_type ACMD41 = (0xC0 + 41); //!< SEND_OP_COND (SDC)
|
||||
|
||||
constexpr static data_type R1_READY_STATE = 0x00; //!< status for card in the ready state
|
||||
constexpr static data_type R1_IDLE_STATE = 0x01; //!< status for card in the idle state
|
||||
constexpr static data_type R1_ILLEGAL_COMMAND = 0x04; //!< status bit for illegal command
|
||||
constexpr static data_type DATA_START_BLOCK = 0xFE; //!< start data token for read or write single block
|
||||
constexpr static data_type STOP_TRAN_TOKEN = 0xFD; //!< stop token for write multiple blocks
|
||||
constexpr static data_type WRITE_MULTIPLE_TOKEN= 0xFC; //!< start data token for write multiple blocks
|
||||
constexpr static data_type DATA_RES_MASK = 0x1F; //!< mask for data response tokens after a write block operation
|
||||
constexpr static data_type DATA_RES_ACCEPTED = 0x05; //!< write data accepted token
|
||||
|
||||
// MMC card type flags (MMC_GET_TYPE)
|
||||
//! \note
|
||||
//! These types are compatible with FatFS types
|
||||
constexpr static data_type CT_NONE = 0x00;
|
||||
constexpr static data_type CT_MMC = 0x01; //!< MMC ver 3
|
||||
constexpr static data_type CT_SD1 = 0x02; //!< SD ver 1
|
||||
constexpr static data_type CT_SD2 = 0x04; //!< SD ver 2
|
||||
constexpr static data_type CT_SDC = (CT_SD1|CT_SD2); //!< SD
|
||||
constexpr static data_type CT_BLOCK = 0x08; //!< Block addressing
|
||||
|
||||
public:
|
||||
|
||||
enum status_t : uint8_t {
|
||||
ST_OK =0,
|
||||
ST_NOINIT = 1,
|
||||
ST_NODISK = 2,
|
||||
ST_WRPROTECT = 3,
|
||||
ST_ERROR = 4
|
||||
};
|
||||
|
||||
enum ioctl_cmd {
|
||||
// Fatfs compatibility
|
||||
IOCTL_SYNC =0, //!< Flush disk cache (for write functions)
|
||||
IOCTL_GET_SECTOR_COUNT =1, //!< Get media size (for only f_mkfs())
|
||||
IOCTL_GET_SECTOR_SIZE =2, //!< Get sector size (for multiple sector size (_MAX_SS >= 1024))
|
||||
IOCTL_GET_BLOCK_SIZE =3, //!< Get erase block size (for only f_mkfs())
|
||||
IOCTL_ERASE_SECTOR =4, //!< Force erased a block of sectors (for only _USE_ERASE)
|
||||
|
||||
// Generics
|
||||
IOCTL_POWER =5, //!< Get/Set power status
|
||||
IOCTL_LOCK =6, //!< Lock/Unlock media removal
|
||||
IOCTL_EJECT =7, //!< Eject media
|
||||
IOCTL_FORMAT =8, //!< Create physical format on the media
|
||||
|
||||
// SD/MMC specific
|
||||
IOCTL_MMC_GET_TYPE =10, //!< Get card type
|
||||
IOCTL_MMC_GET_CSD =11, //!< Get CSD
|
||||
IOCTL_MMC_GET_CID =12, //!< Get CID
|
||||
IOCTL_MMC_GET_OCR =13, //!< Get OCR
|
||||
IOCTL_MMC_GET_SDSTAT =14, //!< Get SD status
|
||||
};
|
||||
|
||||
public:
|
||||
sd_card() :
|
||||
status{ST_NOINIT} { }
|
||||
|
||||
sd_card(const sd_card&) = delete;
|
||||
sd_card& operator=(const sd_card&) = delete;
|
||||
|
||||
private:
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Calculate the maximum data transfer rate per one data line
|
||||
* from the CSD.
|
||||
* TRAN_SPEED is the CSD[103..96]
|
||||
*
|
||||
* TRAN_SPEED bit code
|
||||
* ---------------------------------------------------
|
||||
* 2:0 | transfer rate unit
|
||||
* | 0=100kbit/s, 1=1Mbit/s, 2=10Mbit/s,
|
||||
* | 3=100Mbit/s, 4... 7=reserved
|
||||
* ---------------------------------------------------
|
||||
* 6:3 | time value
|
||||
* --------------------------------------------------
|
||||
* 7 | Reserved
|
||||
*
|
||||
* \param csd Pointer to CSD array 128bit.
|
||||
* \return The maximum spi baud rate.
|
||||
*/
|
||||
uint32_t csd2bautrate (data_type *csd) {
|
||||
data_type brmul = 0;
|
||||
uint32_t br = 100000; // 100Kbit
|
||||
|
||||
// Mask [2..0] bits of TRAN_SPEED
|
||||
brmul = csd[3] & 0x07;
|
||||
while (brmul--)
|
||||
br *= 10;
|
||||
return br;
|
||||
}
|
||||
|
||||
void delay (clock_t t) {
|
||||
clock_t mark = impl().clock_impl();
|
||||
while (impl().clock_impl() - mark < t)
|
||||
;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Check if SD Card is present.
|
||||
* \return The sd card present status
|
||||
* \arg false Is NOT present
|
||||
* \arg true Is present.
|
||||
*/
|
||||
bool is_present () {
|
||||
return impl().CD_impl();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Check if SD Card is write protected.
|
||||
* \return The write protect status
|
||||
* \arg false Is NOT write protected
|
||||
* \arg true Is write protected.
|
||||
*/
|
||||
bool is_write_protected () {
|
||||
return impl().WP_impl();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Powers up or down the SD Card.
|
||||
* \param on On/Off flag.
|
||||
* \return The new power state state
|
||||
*/
|
||||
bool power (bool on) {
|
||||
impl().PWR_impl(on);
|
||||
return pwr_flag = on;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Check if SD Card is powered.
|
||||
* \return The power status
|
||||
* \arg false The drive is not powered
|
||||
* \arg true The drive is powered
|
||||
*/
|
||||
bool power () { return pwr_flag; }
|
||||
|
||||
/*!
|
||||
* \brief Chip-select control
|
||||
* \param state True to Select, false to de-select.
|
||||
*/
|
||||
void select() {
|
||||
spi_tx(0xFF);
|
||||
impl().CS_impl(false);
|
||||
spi_tx(0xFF);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief De-select SD Card and release SPI bus
|
||||
* \return None.
|
||||
*/
|
||||
void release () {
|
||||
spi_tx(0xFF);
|
||||
impl().CS_impl(true);
|
||||
spi_tx(0xFF);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Transmit a byte to SD/MMC via SPI
|
||||
* \param data The data to send to the SPI bus.
|
||||
*/
|
||||
void spi_tx (data_type data) {
|
||||
impl().SPI_rw_impl (data);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Receive a byte to SD/MMC via SPI.
|
||||
* \return The data received from SPI bus.
|
||||
*/
|
||||
data_type spi_rx () {
|
||||
return impl().SPI_rw_impl (0xFF);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Keep calling spi_rx until response \c resp.
|
||||
*
|
||||
* \param resp the response we wait for
|
||||
* \param timeout timeout for the operation
|
||||
* \return
|
||||
* \arg true Ready
|
||||
* \arg false NOT ready.
|
||||
*/
|
||||
bool spi_wait_for (data_type resp, clock_t timeout) {
|
||||
data_type res;
|
||||
clock_t mark = impl().clock_impl();
|
||||
|
||||
do
|
||||
res = spi_rx ();
|
||||
while ((res != resp) && ((impl().clock_impl() - mark) < timeout));
|
||||
|
||||
return (res == resp);
|
||||
}
|
||||
|
||||
bool activate (bool state) {
|
||||
if (state) {
|
||||
power(true); // power on with delay
|
||||
delay (SD_PowerTimeout);
|
||||
impl().CS_impl(1); // make sure CS is high
|
||||
for (size_t i=0 ; i<10 ; ++i) // 80 dummy clocks with DI high
|
||||
spi_tx(0xFF);
|
||||
status = ST_NOINIT; // mark the status
|
||||
}
|
||||
else {
|
||||
power(false); // power off
|
||||
impl().CS_impl(0); // keep CS pin voltage low
|
||||
status = ST_NOINIT; // mark the status
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Receive a data packet from MMC/SD
|
||||
*
|
||||
* \param buffer Pointer to data buffer to store received data
|
||||
* \param n Byte count (must be multiple of 4)
|
||||
* \return The operation status
|
||||
* \arg false Fail
|
||||
* \arg true Success.
|
||||
*/
|
||||
bool rx_datablock (data_type* buffer, size_t n) {
|
||||
|
||||
if (! spi_wait_for(DATA_START_BLOCK, SD_RxTimeout))
|
||||
return false;
|
||||
|
||||
/*!
|
||||
* Receive the data block into buffer and make sure
|
||||
* we receive multiples of 4
|
||||
*/
|
||||
n += (n%4) ? 4-(n%4):0;
|
||||
for ( ; n>0 ; --n)
|
||||
*buffer++ = spi_rx ();
|
||||
|
||||
spi_rx (); // Discard CRC
|
||||
spi_rx ();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Transmit a data block (512bytes) to MMC/SD
|
||||
*
|
||||
* \param buffer Pointer to 512 byte data block to be transmitted
|
||||
* \param token Data/Stop token
|
||||
* \return The operation status
|
||||
* \arg false Fail
|
||||
* \arg true Success.
|
||||
*/
|
||||
bool tx_datablock (const data_type* buffer, data_type token) {
|
||||
if (!spi_wait_for(0xFF, SD_WaitTimeout))
|
||||
return false;
|
||||
|
||||
spi_tx(token); // transmit data token
|
||||
if (token != STOP_TRAN_TOKEN) {
|
||||
// if its data token, transmit the 512 byte block
|
||||
size_t cnt = 512;
|
||||
do
|
||||
spi_tx(*buffer++);
|
||||
while (--cnt);
|
||||
spi_tx(0xFF); // CRC (Dummy)
|
||||
spi_tx(0xFF);
|
||||
data_type r = spi_rx(); // Receive data response
|
||||
if ((r & DATA_RES_MASK) != DATA_RES_ACCEPTED) // If not accepted, return with error
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Send a command packet to SD/MMC and return the response
|
||||
*
|
||||
* \param cmd Command byte
|
||||
* \param arg Argument
|
||||
* \return The response as operation status
|
||||
*/
|
||||
data_type command (data_type cmd, uint32_t arg) {
|
||||
data_type n, r;
|
||||
|
||||
if (cmd & 0x80) {
|
||||
/*!
|
||||
* SD_ACMD<n> is the command sequence of CMD55-SD_CMD<n>
|
||||
*/
|
||||
cmd &= 0x7F;
|
||||
r = command (CMD55, 0);
|
||||
if (r > 1)
|
||||
return r;
|
||||
}
|
||||
|
||||
// Send command packet
|
||||
spi_tx (cmd); // Start + Command index
|
||||
spi_tx ((data_type)(arg>>24)); // Argument [31..24]
|
||||
spi_tx ((data_type)(arg>>16)); // Argument [23..16]
|
||||
spi_tx ((data_type)(arg>>8)); // Argument [15..8]
|
||||
spi_tx ((data_type)arg); // Argument [7..0]
|
||||
|
||||
if (cmd == CMD0) n = 0x94; // Valid CRC for CMD0(0)
|
||||
else if (cmd == CMD8) n = 0x86; // Valid CRC for CMD8(0x1AA)
|
||||
else n = 0x00;
|
||||
spi_tx (n | CMD_CRC_LSB);
|
||||
|
||||
// Receive command response
|
||||
if (cmd == CMD12)
|
||||
spi_rx (); // Skip a stuff byte when stop reading
|
||||
|
||||
// Wait for a valid response in timeout of 0xFF attempts
|
||||
size_t nn = 0xFF;
|
||||
do
|
||||
r = spi_rx ();
|
||||
while ((r & 0x80) && --nn);
|
||||
|
||||
return r; // Return with the response value
|
||||
}
|
||||
|
||||
bool do_command_until (data_type done, data_type cmd, uint32_t arg, clock_t timeout) {
|
||||
clock_t mark = impl().clock_impl();
|
||||
data_type ret;
|
||||
do
|
||||
ret = command (cmd, arg);
|
||||
while (ret != done && impl().clock_impl() - mark < timeout);
|
||||
return ret == done;
|
||||
}
|
||||
|
||||
public:
|
||||
bool get_CSD (data_type* csd) {
|
||||
bool ret = false;
|
||||
select(); // select card's CS
|
||||
if (command (CMD9, 0) == R1_READY_STATE && rx_datablock (csd, 16)) // READ_CSD
|
||||
ret = true;
|
||||
release(); // release card's CS
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool get_CID (data_type* cid) {
|
||||
bool ret = false;
|
||||
select(); // select card's CS
|
||||
if (command (CMD10, 0) == R1_READY_STATE && rx_datablock (cid, 16)) // READ_CID
|
||||
ret = true;
|
||||
release(); // release card's CS
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool get_OCR (data_type* ocr) {
|
||||
bool ret = false;
|
||||
select(); // select card's CS
|
||||
// Receive OCR as an R3 response (4 bytes)
|
||||
if (command (CMD58, 0) == 0) { // READ_OCR
|
||||
for (size_t n = 0; n < 4; ++n)
|
||||
*ocr++ = spi_rx ();
|
||||
ret = true;
|
||||
}
|
||||
release(); // release card's CS
|
||||
return ret;
|
||||
}
|
||||
|
||||
// bool get_SDSTAT (data_type* sdstat) {
|
||||
// bool ret = false;
|
||||
// select(); // select card's CS
|
||||
// if (command (ACMD13, 0) == 0) { // SD_STATUS
|
||||
// spi_rx ();
|
||||
// if (rx_datablock (sdstat, 64))
|
||||
// ret = true;
|
||||
// }
|
||||
// release(); // release card's CS
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
bool sync () {
|
||||
select(); // select card's CS
|
||||
bool st = spi_wait_for(0xFF, SD_WaitTimeout); // flush
|
||||
release(); // release card's CS
|
||||
return st;
|
||||
}
|
||||
|
||||
size_t sector_count() {
|
||||
size_t ret =0;
|
||||
data_type csd[16];
|
||||
|
||||
select(); // select card's CS
|
||||
if (get_CSD(csd)) {
|
||||
if ((csd[0] >> 6) == 1) {
|
||||
// SDC version 2.00
|
||||
size_t csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
|
||||
ret = csize << 10;
|
||||
}
|
||||
else {
|
||||
// SDC version 1.XX or MMC
|
||||
uint8_t n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
|
||||
size_t csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
|
||||
ret = csize << (n - 9);
|
||||
}
|
||||
}
|
||||
release(); // release card's CS
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t sector_size() const { return 512; }
|
||||
|
||||
size_t block_size() {
|
||||
size_t ret =0;
|
||||
data_type csd[16];
|
||||
|
||||
select(); // select card's CS
|
||||
if (card_type & CT_SD2) {
|
||||
// SDC version 2.00
|
||||
if (command (ACMD13, 0) == R1_READY_STATE) {
|
||||
spi_rx (); // Read SD status
|
||||
if (rx_datablock (csd, 16)) { // Read partial block
|
||||
for (size_t n = 64 - 16; n; n--) // Purge trailing data
|
||||
spi_rx ();
|
||||
ret = 16UL << (csd[10] >> 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// SDC version 1.XX or MMC
|
||||
if (get_CSD(csd)) { // Read CSD
|
||||
if (card_type & CT_SD1) // SDC version 1.XX
|
||||
ret = (((csd[10] & 63) << 1) + ((uint16_t)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
|
||||
else // MMC
|
||||
ret = ((uint16_t)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
|
||||
}
|
||||
}
|
||||
release(); // release card's CS
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* De-Initialize SD Drive.
|
||||
* \return None
|
||||
*/
|
||||
void deinit () {
|
||||
card_type = data_type{};
|
||||
status = status_t{};
|
||||
activate (0); // finally power off the card
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Initialize SD Drive.
|
||||
*
|
||||
* \return The status of the operation
|
||||
* \arg false On error.
|
||||
* \arg true On success.
|
||||
*/
|
||||
bool init () {
|
||||
uint32_t clk;
|
||||
data_type ocr[4], csd[16];
|
||||
|
||||
clk = 400000; // Start at lower clk
|
||||
impl().SPI_set_clk_impl(clk);
|
||||
|
||||
activate (0); // Initially power off the card
|
||||
if (!is_present()) { // check for presence
|
||||
status = ST_NODISK;
|
||||
return false;
|
||||
}
|
||||
activate (1); // activate and wait for PowerTimeout delay
|
||||
|
||||
select(); // select card
|
||||
data_type type = CT_NONE;
|
||||
|
||||
if (command (CMD0, 0) == R1_IDLE_STATE) { // Command to enter Idle state
|
||||
if (command (CMD8, 0x1AA) == 1) { // check SD version
|
||||
// SDHC
|
||||
for (size_t n=0 ; n<4 ; ++n) // Get trailing return value of R7 response
|
||||
ocr[n] = spi_rx ();
|
||||
if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
|
||||
// Wait for leaving idle state (ACMD41 with HCS bit)
|
||||
bool st = do_command_until(R1_READY_STATE, ACMD41, 1UL << 30, SD_InitTimeout);
|
||||
|
||||
if (st && get_OCR(ocr))
|
||||
type = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
|
||||
}
|
||||
} else {
|
||||
data_type cmd;
|
||||
// SDSC or MMC
|
||||
if (command (ACMD41, 0) <= 1) { // SDSC
|
||||
type = CT_SD1; cmd = ACMD41;
|
||||
} else { // MMC
|
||||
type = CT_MMC; cmd = CMD1;
|
||||
}
|
||||
// Wait for leaving idle state (ACMD41 || CMD1)
|
||||
bool st = do_command_until(R1_READY_STATE, cmd, 0, SD_InitTimeout);
|
||||
// On failure, set R/W block length to 512 (For FAT compatibility)
|
||||
if (!st || command (CMD16, 512) != R1_READY_STATE)
|
||||
type = CT_NONE;
|
||||
}
|
||||
}
|
||||
card_type = type;
|
||||
release (); // Initialization ended
|
||||
|
||||
if (type != CT_NONE) {
|
||||
// Success
|
||||
get_CSD(csd);
|
||||
clk = csd2bautrate(csd);
|
||||
impl().SPI_set_clk_impl(clk);
|
||||
status = ST_OK;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
activate(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
status_t get_status () const { return status; }
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Read Sector(s)
|
||||
*
|
||||
* \param sector Start sector number (LBA)
|
||||
* \param buf Pointer to the data buffer to store read data
|
||||
* \param count Sector (512 bytes) count (1..255)
|
||||
* \return The status of the operation
|
||||
* \arg false On error.
|
||||
* \arg true On success.
|
||||
*/
|
||||
bool read (size_t sector, data_type *buf, size_t count) {
|
||||
|
||||
if (status != ST_OK) return false;
|
||||
|
||||
if (!(card_type & CT_BLOCK)) // Convert to byte address if needed
|
||||
sector *= 512;
|
||||
select();
|
||||
if (count == 1) { //Single block read
|
||||
if (command (CMD17, sector) == 0)
|
||||
if (rx_datablock (buf, 512))
|
||||
count = 0;
|
||||
}
|
||||
else { // Multiple block read
|
||||
if (command (CMD18, sector) == 0) {
|
||||
do {
|
||||
if (!rx_datablock (buf, 512))
|
||||
break;
|
||||
buf += 512;
|
||||
} while (--count);
|
||||
command (CMD12, 0); // STOP_TRANSMISSION
|
||||
}
|
||||
}
|
||||
release ();
|
||||
return (count == 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* Write Sector(s)
|
||||
*
|
||||
* \param sector Start sector number (LBA)
|
||||
* \param buf Pointer to the data to be written
|
||||
* \param count Sector(512 bytes) count (1..255)
|
||||
* \return The status of the operation
|
||||
* \arg false On error.
|
||||
* \arg true On success.
|
||||
*/
|
||||
bool write (size_t sector, const data_type *buf, size_t count) {
|
||||
|
||||
if (!count) return false;
|
||||
if (status != ST_OK) return false;
|
||||
|
||||
if (!(card_type & CT_BLOCK)) // Convert to byte address if needed
|
||||
sector *= 512;
|
||||
|
||||
select();
|
||||
if (count == 1) { // Single block write
|
||||
if ((command (CMD24, sector) == 0) && tx_datablock (buf, 0xFE))
|
||||
count = 0;
|
||||
} else { // Multiple block write
|
||||
if (card_type & CT_SDC)
|
||||
command (ACMD23, count);
|
||||
if (command (CMD25, sector) == 0) {
|
||||
do {
|
||||
if (!tx_datablock (buf, WRITE_MULTIPLE_TOKEN))
|
||||
break;
|
||||
buf += 512;
|
||||
} while (--count);
|
||||
if (!tx_datablock (0, STOP_TRAN_TOKEN)) // STOP token
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
release ();
|
||||
return (count == 0);
|
||||
}
|
||||
|
||||
bool ioctl (ioctl_cmd cmd, void* buffer) {
|
||||
switch (cmd) {
|
||||
// SD/MMC specific
|
||||
case IOCTL_MMC_GET_TYPE: *(data_type*)buffer = card_type; return true;
|
||||
case IOCTL_MMC_GET_CSD: return get_CSD ((data_type*)buffer);
|
||||
case IOCTL_MMC_GET_CID: return get_CID ((data_type*)buffer);
|
||||
case IOCTL_MMC_GET_OCR: return get_OCR ((data_type*)buffer);
|
||||
case IOCTL_MMC_GET_SDSTAT: return false;
|
||||
|
||||
// Generic
|
||||
case IOCTL_POWER:
|
||||
switch (*(data_type*)buffer) {
|
||||
case 0: *((data_type*)buffer+1) = (data_type)activate(0); return true;
|
||||
case 1: *((data_type*)buffer+1) = (data_type)activate(0); return true;
|
||||
case 2: *((data_type*)buffer+1) = (data_type)power(); return true;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case IOCTL_LOCK:
|
||||
case IOCTL_EJECT:
|
||||
case IOCTL_FORMAT:
|
||||
return false;
|
||||
|
||||
// FatFS compatibility
|
||||
case IOCTL_SYNC: return sync();
|
||||
case IOCTL_GET_SECTOR_COUNT:return (*(size_t*) buffer = sector_count() != 0);
|
||||
case IOCTL_GET_SECTOR_SIZE: return (*(size_t*) buffer = sector_size() != 0);
|
||||
case IOCTL_GET_BLOCK_SIZE: return (*(size_t*) buffer = block_size() != 0);
|
||||
case IOCTL_ERASE_SECTOR: return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
data_type card_type{};
|
||||
status_t status{};
|
||||
bool pwr_flag{};
|
||||
|
||||
};
|
||||
|
||||
} // namespace tbx
|
||||
|
||||
#endif /* TBX_DRV_SD_SPI_H_ */
|
||||
@@ -37,9 +37,14 @@
|
||||
#include <cont/queue.h>
|
||||
#include <cont/equeue.h>
|
||||
|
||||
#include <utils/json.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>
|
||||
#include <drv/sd_spi.h>
|
||||
|
||||
#endif /* TBX_H_ */
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*!
|
||||
* \file files.h
|
||||
* \brief
|
||||
* File functionality header
|
||||
*
|
||||
* \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
|
||||
*
|
||||
* <dl class=\"section copyright\"><dt>License</dt><dd>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of Christos Choutouridis. The intellectual
|
||||
* and technical concepts contained herein are proprietary to
|
||||
* Christos Choutouridis and are protected by copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from Christos Choutouridis.
|
||||
* </dd></dl>
|
||||
*/
|
||||
#ifndef JSON_H_
|
||||
#define JSON_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <type_traits>
|
||||
#include <string_view>
|
||||
#include <array>
|
||||
|
||||
using size_t = std::size_t;
|
||||
|
||||
struct jpair_t {
|
||||
std::string_view key;
|
||||
std::string_view value;
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
struct json_dec_t {
|
||||
|
||||
using string_view = std::string_view;
|
||||
|
||||
json_dec_t(const char* buffer, size_t size) noexcept :
|
||||
buffer_(buffer, size), valid_(true) {
|
||||
enum state_t {
|
||||
ST=0, KEY, COLON, VALUE, SP
|
||||
} state = ST;
|
||||
|
||||
size_t pairs =0;
|
||||
char* begin = nullptr;
|
||||
int s =0;
|
||||
bool str_value = false; // flag to indicate the value is string
|
||||
|
||||
for (size_t i=0 ; i<size ; ++i) {
|
||||
switch (state) {
|
||||
case ST:
|
||||
if (std::isspace(buffer[i]))
|
||||
continue; // skip white space
|
||||
if (buffer[i] == '{')
|
||||
state = KEY;
|
||||
break;
|
||||
|
||||
case KEY:
|
||||
if (pairs >= N) {
|
||||
valid_ = false;
|
||||
break;
|
||||
}
|
||||
if (std::isspace(buffer[i]))
|
||||
continue; // skip white space
|
||||
if (buffer[i] == '\"') {
|
||||
if (!begin)
|
||||
begin = (char*)&buffer[i+1];
|
||||
else {
|
||||
s = (char*)&buffer[i] - begin;
|
||||
if (s > 0)
|
||||
pairs_[pairs].key = std::string_view{begin, (size_t)s};
|
||||
else
|
||||
pairs_[pairs].key = std::string_view{};
|
||||
begin =nullptr;
|
||||
s =0;
|
||||
state = COLON;
|
||||
}
|
||||
}
|
||||
else if (buffer[i] == '}')
|
||||
return;
|
||||
break;
|
||||
|
||||
case COLON:
|
||||
if (std::isspace(buffer[i]))
|
||||
continue; // skip white space
|
||||
if (buffer[i] == ':')
|
||||
state = VALUE;
|
||||
else {
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
if (pairs >= N) {
|
||||
valid_ = false;
|
||||
break;
|
||||
}
|
||||
if (!begin && std::isspace(buffer[i])) // consume pre-spaces
|
||||
continue;
|
||||
else if (!begin && !std::isspace(buffer[i])) { // first character
|
||||
if (buffer[i] == '\"') {
|
||||
begin = (char*)&buffer[i+1];
|
||||
str_value = true;
|
||||
}
|
||||
else {
|
||||
begin = (char*)&buffer[i];
|
||||
str_value = false;
|
||||
}
|
||||
}
|
||||
else if (begin) {
|
||||
if (str_value && (buffer[i] == '\"')) {
|
||||
s = (char*)&buffer[i] - begin;
|
||||
if (s > 0)
|
||||
pairs_[pairs].value = std::string_view{begin, (size_t)s};
|
||||
else
|
||||
pairs_[pairs].value = std::string_view{};
|
||||
++pairs;
|
||||
begin =nullptr;
|
||||
s =0;
|
||||
state = SP;
|
||||
}
|
||||
else if (!str_value && (std::isspace(buffer[i]) || buffer[i] == ',' || buffer[i] == '}')) {
|
||||
s = (char*)&buffer[i] - begin;
|
||||
if (s > 0)
|
||||
pairs_[pairs].value = std::string_view{begin, (size_t)s};
|
||||
else
|
||||
pairs_[pairs].value = std::string_view{};
|
||||
++pairs;
|
||||
begin =nullptr;
|
||||
s =0;
|
||||
if (std::isspace(buffer[i]))
|
||||
state = SP;
|
||||
else if (buffer[i] == ',')
|
||||
state = KEY;
|
||||
else if (buffer[i] == '}')
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SP:
|
||||
if (std::isspace(buffer[i]))
|
||||
continue; // skip white space
|
||||
else if (buffer[i] == ',')
|
||||
state = KEY;
|
||||
else if (buffer[i] == '}')
|
||||
return;
|
||||
else {
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template<typename T>
|
||||
T get (const char* key) {
|
||||
T t{};
|
||||
for (auto& it : pairs_) {
|
||||
if (it.key.compare(key) == 0) {
|
||||
extract_(it.value, &t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
bool is_valid() const { return valid_; }
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Convert the text pointed by \c str to a value and store it to
|
||||
* \c value. The type of conversion is deduced by the compiler
|
||||
* \tparam T The type of the value
|
||||
* \param str pointer to string with the value
|
||||
* \param value pointer to converted value
|
||||
*/
|
||||
void extract_(std::string_view str, bool* value) {
|
||||
*value = (
|
||||
!std::strncmp(str.data(), "true", str.size()) ||
|
||||
!std::strncmp(str.data(), "True", str.size()) ||
|
||||
!std::strncmp(str.data(), "TRUE", str.size()) ||
|
||||
!std::strncmp(str.data(), "1", str.size())
|
||||
) ? true : false;
|
||||
}
|
||||
void extract_(std::string_view str, int* value) {
|
||||
*value = std::atoi(str.data());
|
||||
}
|
||||
void extract_(std::string_view str, unsigned int* value) {
|
||||
*value = (unsigned int)std::atoi(str.data());
|
||||
}
|
||||
void extract_(std::string_view str, long* value) {
|
||||
*value = std::atol(str.data());
|
||||
}
|
||||
void extract_(std::string_view str, unsigned long* value) {
|
||||
*value = (unsigned long)std::atol(str.data());
|
||||
}
|
||||
void extract_(std::string_view str, double* value) {
|
||||
*value = std::atof(str.data());
|
||||
}
|
||||
void extract_(std::string_view str, char** value) {
|
||||
*value = (char*)str.data();
|
||||
}
|
||||
void extract_(std::string_view str, string_view* value) {
|
||||
*value = str;
|
||||
}
|
||||
//! Specialization (as overload function) to handle void* types
|
||||
void extract_ (const char* str, void* value) noexcept {
|
||||
(void)*str; (void)value;
|
||||
}
|
||||
private:
|
||||
std::string_view buffer_;
|
||||
std::array<jpair_t, N> pairs_;
|
||||
bool valid_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* JSON_H_ */
|
||||
@@ -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_impl(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_ */
|
||||
@@ -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_ */
|
||||
Reference in New Issue
Block a user