Initial UI setup

This commit is contained in:
Sylvain Schneider
2026-05-22 17:24:30 +02:00
parent 975717cb67
commit 0165dfe8f0
316 changed files with 78404 additions and 31 deletions

View File

@@ -0,0 +1,45 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "ICommBus.h"
#include <chrono>
#include <expected>
#include <span>
namespace sdi_uToolBox::comm
{
//--------------------------------------------------------------
class I2CBus : public ICommBus
{
public:
using ReadBuf = std::span<uint8_t>;
using WriteBuf = std::span<const uint8_t>;
using Timeout = std::chrono::milliseconds;
using ReturnType = std::expected<size_t, ReturnStatus>;
public:
I2CBus() = default; // Default constructor
virtual ~I2CBus() override = default; // Default destructor
I2CBus(const I2CBus &) = delete; // Copy constructor
I2CBus(I2CBus &&) = delete; // Move constructor
I2CBus &operator=(const I2CBus &) = delete; // Copy assignment operator
I2CBus &operator=(I2CBus &&) = delete; // Move assignment operator
virtual ReturnType write(uint8_t address, WriteBuf buf, Timeout timeout) const = 0; // Write data to I2C device
virtual ReturnType read(uint8_t address, ReadBuf buf, Timeout timeout) const = 0; // Read data from I2C device
virtual ReturnStatus isDeviceReady(uint8_t address) const = 0; // Check if I2C device is ready
};
//--------------------------------------------------------------
} // namespace sdi_uToolBox::comm

View File

@@ -0,0 +1,62 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "I2CBus.h"
#include "ICommDevice.h"
namespace sdi_uToolBox::comm
{
//--------------------------------------------------------------
class I2CDevice : public ICommDevice
{
public:
I2CDevice() = delete; // Default constructor
explicit I2CDevice(I2CBus &bus, uint8_t address); // Constructor
virtual ~I2CDevice() override = default; // Default destructor
I2CDevice(const I2CDevice &) = delete; // Copy constructor
I2CDevice &operator=(const I2CDevice &) = delete; // Copy assignment operator
I2CDevice(I2CDevice &&) = delete; // Move constructor
I2CDevice &operator=(I2CDevice &&) = delete; // Move assignment operator
ReturnType write(WriteBuf buf, Timeout timeout) override; // Write data to device
ReturnType read(ReadBuf buf, Timeout timeout) override; // Read data from device
protected:
I2CBus &m_bus; // Reference to I2C bus
uint8_t m_address; // I2C device address
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Constructor */
inline I2CDevice::I2CDevice(I2CBus &bus, const uint8_t address)
: m_bus(bus)
, m_address(address)
{
// Nothing to do
}
//--------------------------------------------------------------
/* Write data to device */
inline ICommDevice::ReturnType I2CDevice::write(const WriteBuf buf, const Timeout timeout)
{
return m_bus.write(m_address, buf, timeout);
}
//--------------------------------------------------------------
/* Read data from device */
inline ICommDevice::ReturnType I2CDevice::read(const ReadBuf buf, const Timeout timeout)
{
return m_bus.read(m_address, buf, timeout);
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::comm

View File

@@ -0,0 +1,34 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../defs.h"
namespace sdi_uToolBox::comm
{
//--------------------------------------------------------------
class ICommBus
{
public:
ICommBus() = default; // Default constructor
virtual ~ICommBus() = default; // Default destructor
ICommBus(const ICommBus &) = delete; // Copy constructor
ICommBus &operator=(const ICommBus &) = delete; // Copy assignment operator
ICommBus(ICommBus &&) = delete; // Move constructor
ICommBus &operator=(ICommBus &&) = delete; // Move assignment operator
virtual ReturnStatus open() = 0; // Open communication bus
virtual ReturnStatus close() = 0; // Close communication bus
};
//--------------------------------------------------------------
} // namespace sdi_uToolBox::comm

View File

@@ -0,0 +1,46 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../defs.h"
#include <chrono>
#include <expected>
#include <span>
namespace sdi_uToolBox::comm
{
//--------------------------------------------------------------
class ICommDevice
{
public:
using ReadBuf = std::span<uint8_t>;
using WriteBuf = std::span<const uint8_t>;
using Timeout = std::chrono::milliseconds;
using ReturnType = std::expected<size_t, ReturnStatus>;
static constexpr auto INFINITE_DURATION = Timeout(-1);
public:
ICommDevice() = default; // Default constructor
virtual ~ICommDevice() = default; // Default destructor
ICommDevice(const ICommDevice &) = delete; // Copy constructor
ICommDevice &operator=(const ICommDevice &) = delete; // Copy assignment operator
ICommDevice(ICommDevice &&) = delete; // Move constructor
ICommDevice &operator=(ICommDevice &&) = delete; // Move assignment operator
virtual ReturnType write(WriteBuf buf, Timeout timeout = INFINITE_DURATION) = 0; // Write data to device
virtual ReturnType read(ReadBuf buf, Timeout timeout = INFINITE_DURATION) = 0; // Read data from device
};
//--------------------------------------------------------------
} // namespace sdi_uToolBox::comm

View File

@@ -0,0 +1,47 @@
//{{copyright}}
//{{version}}
//{{license}}
#pragma once
#include "ICommBus.h"
#include <chrono>
#include <expected>
#include <span>
namespace sdi_uToolBox::comm
{
//--------------------------------------------------------------
class SerialBus : public ICommBus
{
public:
using ReadBuf = std::span<uint8_t>;
using WriteBuf = std::span<const uint8_t>;
using Timeout = std::chrono::milliseconds;
using ReturnType = std::expected<size_t, ReturnStatus>;
public:
SerialBus() = default; // Default constructor
virtual ~SerialBus() override = default; // Default destructor
SerialBus(const SerialBus&) = delete; // Copy constructor
SerialBus(SerialBus&&) = delete; // Move constructor
SerialBus& operator=(const SerialBus&) = delete; // Copy assignment operator
SerialBus& operator=(SerialBus&&) = delete; // Move assignment operator
virtual ReturnType write(WriteBuf buf, Timeout timeout) const = 0; // Write data to UART device
virtual ReturnType read(ReadBuf buf, Timeout timeout) const = 0; // Read data from UART device
virtual ReturnType readUntilIdle(ReadBuf buf, Timeout timeout) const; // Read data until IDLE event
};
//--------------------------------------------------------------
/* Read data until IDLE event */
inline SerialBus::ReturnType SerialBus::readUntilIdle(ReadBuf buf, Timeout timeout) const
{
return std::unexpected(ReturnStatus::IncompatibleBaseType);
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::comm

View File

@@ -0,0 +1,51 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "ICommBus.h"
#include <chrono>
#include <expected>
#include <span>
namespace sdi_uToolBox::comm
{
//--------------------------------------------------------------
class UARTBus : public ICommBus
{
public:
using ReadBuf = std::span<uint8_t>;
using WriteBuf = std::span<const uint8_t>;
using Timeout = std::chrono::milliseconds;
using ReturnType = std::expected<size_t, ReturnStatus>;
public:
UARTBus() = default; // Default constructor
virtual ~UARTBus() override = default; // Default destructor
UARTBus(const UARTBus &) = delete; // Copy constructor
UARTBus(UARTBus &&) = delete; // Move constructor
UARTBus &operator=(const UARTBus &) = delete; // Copy assignment operator
UARTBus &operator=(UARTBus &&) = delete; // Move assignment operator
virtual ReturnType write(WriteBuf buf, Timeout timeout) const = 0; // Write data to UART device
virtual ReturnType read(ReadBuf buf, Timeout timeout) const = 0; // Read data from UART device
virtual ReturnType readUntilIdle(ReadBuf buf, Timeout timeout) const; // Read data until IDLE event
};
//--------------------------------------------------------------
/* Read data until IDLE event */
inline UARTBus::ReturnType UARTBus::readUntilIdle(ReadBuf buf, Timeout timeout) const
{
return std::unexpected(ReturnStatus::IncompatibleBaseType);
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::comm

View File

@@ -0,0 +1,58 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <FreeRTOSHeaders.h>
#include <sdi_toolBox/dateTime/iPause.h>
namespace sdi_uToolBox::dateTime::FreeRTOS
{
//--------------------------------------------------------------
/**
* @brief FreeRTOS pause implementation.
*
* Implements IPause to provide a non-blocking pause using vTaskDelay
* in FreeRTOS environments.
*/
class Pause : public sdi_toolBox::dateTime::IPause
{
public:
Pause() = default; // Default constructor
~Pause() = default; // Default destructor
Pause(const Pause &obj) = default; // Copy constructor
Pause(Pause &&obj) noexcept = default; // Move constructor
Pause &operator=(const Pause &obj) = default; // Copy assignment operator
Pause &operator=(Pause &&obj) noexcept = default; // Move assignment operator
explicit Pause(const Duration &duration); // Constructor
/**
* @brief Pause execution for the specified duration using vTaskDelay.
* @param duration Duration of the pause.
*/
void wait(const Duration &duration) const override;
};
//--------------------------------------------------------------
/* Constructor */
inline Pause::Pause(const Duration &duration)
{
wait(duration);
}
//--------------------------------------------------------------
/* Pause execution for the specified duration */
inline void Pause::wait(const Duration &duration) const
{
vTaskDelay(pdMS_TO_TICKS(duration.count()));
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::dateTime::FreeRTOS

View File

@@ -0,0 +1,57 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <FreeRTOSHeaders.h>
#include <sdi_toolBox/dateTime/iTimer.h>
namespace sdi_uToolBox::dateTime::FreeRTOS
{
//--------------------------------------------------------------
/**
* @brief FreeRTOS timer implementation using the RTOS tick count.
*
* Implements ITimer for FreeRTOS environments, using xTaskGetTickCount()
* to provide timing based on the RTOS tick counter.
*/
class Timer : public sdi_toolBox::dateTime::ITimer
{
public:
Timer(); // Default constructor
~Timer() = default; // Default destructor
Timer(const Timer &obj) = default; // Copy constructor
Timer(Timer &&obj) noexcept = default; // Move constructor
Timer &operator=(const Timer &obj) = default; // Copy assignment operator
Timer &operator=(Timer &&obj) noexcept = default; // Move assignment operator
/**
* @brief Returns the current time point using the FreeRTOS tick count.
* @return The current time point.
*/
[[nodiscard]] TimePoint now() const override;
};
//--------------------------------------------------------------
/* Default constructor */
inline Timer::Timer()
{
reset();
}
//--------------------------------------------------------------
/* Get the current timepoint */
inline Timer::TimePoint Timer::now() const
{
const auto ms = xTaskGetTickCount() * portTICK_PERIOD_MS;
return TimePoint(Duration(ms));
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::dateTime::FreeRTOS

View File

@@ -0,0 +1,58 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <STM32Headers.h>
#include <sdi_toolBox/dateTime/iPause.h>
namespace sdi_uToolBox::dateTime
{
//--------------------------------------------------------------
/**
* @brief STM32 pause implementation.
*
* Implements IPause to provide a blocking pause using HAL_Delay
* on STM32 platforms.
*/
class Pause : public sdi_toolBox::dateTime::IPause
{
public:
Pause() = default; // Default constructor
~Pause() = default; // Default destructor
Pause(const Pause &obj) = default; // Copy constructor
Pause(Pause &&obj) noexcept = default; // Move constructor
Pause &operator=(const Pause &obj) = default; // Copy assignment operator
Pause &operator=(Pause &&obj) noexcept = default; // Move assignment operator
explicit Pause(const Duration &duration); // Constructor
/**
* @brief Pause execution for the specified duration using HAL_Delay.
* @param duration Duration of the pause.
*/
void wait(const Duration &duration) const override;
};
//--------------------------------------------------------------
/* Constructor */
inline Pause::Pause(const Duration &duration)
{
wait(duration);
}
//--------------------------------------------------------------
/* Pause execution for the specified duration */
inline void Pause::wait(const Duration &duration) const
{
HAL_Delay(duration.count());
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::dateTime

View File

@@ -0,0 +1,57 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <STM32Headers.h>
#include <sdi_toolBox/dateTime/iTimer.h>
namespace sdi_uToolBox::dateTime
{
//--------------------------------------------------------------
/**
* @brief STM32 timer implementation using HAL_GetTick().
*
* Implements ITimer for STM32 platforms, using HAL_GetTick() to
* provide millisecond-precision timing since system startup.
*/
class Timer : public sdi_toolBox::dateTime::ITimer
{
public:
Timer(); // Default constructor
~Timer() = default; // Default destructor
Timer(const Timer &obj) = default; // Copy constructor
Timer(Timer &&obj) noexcept = default; // Move constructor
Timer &operator=(const Timer &obj) = default; // Copy assignment operator
Timer &operator=(Timer &&obj) noexcept = default; // Move assignment operator
/**
* @brief Returns the current time point using HAL_GetTick().
* @return The current time point.
*/
[[nodiscard]] TimePoint now() const override;
};
//--------------------------------------------------------------
/* Default constructor */
inline Timer::Timer()
{
reset();
}
//--------------------------------------------------------------
/* Get the current timepoint */
inline Timer::TimePoint Timer::now() const
{
const auto ms = HAL_GetTick();
return TimePoint(Duration(ms));
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::dateTime

View File

@@ -0,0 +1,26 @@
//{{copyright}}
//{{version}}
//{{license}}
#pragma once
#include <cstdint>
namespace sdi_uToolBox
{
//--------------------------------------------------------------
/* Return status enumeration */
enum class ReturnStatus : uint8_t
{
Ok,
Busy,
Timeout,
Error,
IncompatibleBaseType,
InvalidHandle,
BufferOverflow,
};
//--------------------------------------------------------------
} // namespace sdi_uToolBox

View File

@@ -0,0 +1,154 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <FreeRTOSHeaders.h>
#include <chrono>
#include <mutex>
namespace sdi_uToolBox::generic::FreeRTOS
{
//--------------------------------------------------------------
class Mutex
{
public:
Mutex(); // Constructor
~Mutex(); // Destructor
Mutex(const Mutex &other) = delete; // Copy constructor
Mutex(Mutex &&other) = delete; // Move constructor
Mutex &operator=(const Mutex &other) = delete; // Copy assignment
Mutex &operator=(Mutex &&other) = delete; // Move assignment
void lock() const; // Locks the mutex, blocks if the mutex is not available
void unlock() const; // Unlocks the mutex
bool try_lock() const; // Tries to lock the mutex, returns if the mutex is not available
bool try_lock_for(const std::chrono::milliseconds &duration) const; // Attempts to lock the mutex, returns if the mutex is not available
protected:
SemaphoreHandle_t m_xSemaphore = nullptr;
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Constructor */
inline Mutex::Mutex()
{
m_xSemaphore = xSemaphoreCreateMutex();
configASSERT(m_xSemaphore);
}
//--------------------------------------------------------------
/* Destructor */
inline Mutex::~Mutex()
{
if (!m_xSemaphore)
vSemaphoreDelete(m_xSemaphore);
}
//--------------------------------------------------------------
/* Locks the mutex, blocks if the mutex is not available */
inline void Mutex::lock() const
{
if (xSemaphoreTake(m_xSemaphore, portMAX_DELAY) != pdTRUE)
configASSERT(0);
}
//--------------------------------------------------------------
/* Unlocks the mutex */
inline void Mutex::unlock() const
{
xSemaphoreGive(m_xSemaphore);
}
//--------------------------------------------------------------
/* Tries to lock the mutex, returns if the mutex is not available */
inline bool Mutex::try_lock() const
{
return try_lock_for(std::chrono::milliseconds(0));
}
//--------------------------------------------------------------
/* Attempts to lock the mutex, returns if the mutex has been unavailable for the specified time duration */
inline bool Mutex::try_lock_for(const std::chrono::milliseconds &duration) const
{
const auto waitDelay = pdMS_TO_TICKS(duration.count());
if (xSemaphoreTakeRecursive(m_xSemaphore, waitDelay) == pdTRUE)
return true;
return false;
}
//--------------------------------------------------------------
/* --- */
//--------------------------------------------------------------
class RecursiveMutex
{
public:
RecursiveMutex(); // Constructor
~RecursiveMutex(); // Destructor
RecursiveMutex(const RecursiveMutex &other) = delete; // Copy constructor
RecursiveMutex(RecursiveMutex &&other) = delete; // Move constructor
RecursiveMutex &operator=(const RecursiveMutex &other) = delete; // Copy assignment
RecursiveMutex &operator=(RecursiveMutex &&other) = delete; // Move assignment
void lock() const; // Locks the mutex, blocks if the mutex is not available
void unlock() const; // Unlocks the mutex
bool try_lock() const; // Tries to lock the mutex, returns if the mutex is not available
bool try_lock_for(const std::chrono::milliseconds &duration) const; // Attempts to lock the mutex, returns if the mutex is not available
protected:
SemaphoreHandle_t m_xSemaphore = nullptr;
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Constructor */
inline RecursiveMutex::RecursiveMutex()
{
m_xSemaphore = xSemaphoreCreateRecursiveMutex();
configASSERT(m_xSemaphore);
}
//--------------------------------------------------------------
/* Destructor */
inline RecursiveMutex::~RecursiveMutex()
{
if (!m_xSemaphore)
vSemaphoreDelete(m_xSemaphore);
}
//--------------------------------------------------------------
/* Locks the mutex, blocks if the mutex is not available */
inline void RecursiveMutex::lock() const
{
if (xSemaphoreTakeRecursive(m_xSemaphore, portMAX_DELAY) != pdTRUE)
configASSERT(0);
}
//--------------------------------------------------------------
/* Unlocks the mutex */
inline void RecursiveMutex::unlock() const
{
xSemaphoreGiveRecursive(m_xSemaphore);
}
//--------------------------------------------------------------
/* Tries to lock the mutex, returns if the mutex is not available */
inline bool RecursiveMutex::try_lock() const
{
return try_lock_for(std::chrono::milliseconds(0));
}
//--------------------------------------------------------------
/* Attempts to lock the mutex, returns if the mutex has been unavailable for the specified time duration */
inline bool RecursiveMutex::try_lock_for(const std::chrono::milliseconds &duration) const
{
const auto waitDelay = pdMS_TO_TICKS(duration.count());
if (xSemaphoreTakeRecursive(m_xSemaphore, waitDelay) == pdTRUE)
return true;
return false;
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::generic::FreeRTOS

View File

@@ -0,0 +1,127 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <FreeRTOSHeaders.h>
#include <chrono>
namespace sdi_uToolBox::generic::FreeRTOS
{
//--------------------------------------------------------------
class Semaphore
{
using Timeout = std::chrono::milliseconds;
public:
Semaphore() = delete; // Default constructor
~Semaphore(); // Default destructor
Semaphore(const Semaphore &other) = delete; // Copy constructor
Semaphore(Semaphore &&other) = delete; // Move constructor
Semaphore &operator=(const Semaphore &other) = delete; // Copy assignment
Semaphore &operator=(Semaphore &&other) = delete; // Move assignment
explicit Semaphore(size_t maxCount = 1, size_t initialCount = 0); // Constructor
bool give() const; // Give/release the semaphore
bool giveFromISR() const; // Give/release the semaphore from ISR
bool take(Timeout timeout) const; // Take/acquire the semaphore with timeout
bool take() const; // Take/acquire the semaphore without timeout (blocking)
protected:
SemaphoreHandle_t m_xSemaphore = nullptr;
};
//--------------------------------------------------------------
/* Constructor */
inline Semaphore::Semaphore(const size_t maxCount, const size_t initialCount)
{
if (maxCount == 1)
{
// Binary semaphore
m_xSemaphore = xSemaphoreCreateBinary();
if (m_xSemaphore && initialCount > 0)
xSemaphoreGive(m_xSemaphore);
}
else
{
// Counting semaphore
m_xSemaphore = xSemaphoreCreateCounting(maxCount, initialCount);
}
configASSERT(m_xSemaphore);
}
//--------------------------------------------------------------
/* Default destructor */
inline Semaphore::~Semaphore()
{
if (m_xSemaphore)
vSemaphoreDelete(m_xSemaphore);
}
//--------------------------------------------------------------
/* Give/release the semaphore */
inline bool Semaphore::give() const
{
return xSemaphoreGive(m_xSemaphore) == pdTRUE;
}
//--------------------------------------------------------------
/* Give/release the semaphore from ISR */
inline bool Semaphore::giveFromISR() const
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
const bool result = xSemaphoreGiveFromISR(m_xSemaphore, &xHigherPriorityTaskWoken) == pdTRUE;
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); // Request a context switch if needed
return result;
}
//--------------------------------------------------------------
/* Take/acquire the semaphore with timeout */
inline bool Semaphore::take(const Timeout timeout) const
{
const TickType_t xTicksToWait = (timeout.count() == 0) ? 0 : pdMS_TO_TICKS(timeout.count());
return xSemaphoreTake(m_xSemaphore, xTicksToWait) == pdTRUE;
}
//--------------------------------------------------------------
/* Take/acquire the semaphore without timeout (blocking) */
inline bool Semaphore::take() const
{
return xSemaphoreTake(m_xSemaphore, portMAX_DELAY) == pdTRUE;
}
//--------------------------------------------------------------
/* --- */
//--------------------------------------------------------------
/* Binary Semaphore helper class */
class BinarySemaphore : public Semaphore
{
public:
explicit BinarySemaphore(const bool initialState = false)
: Semaphore(1, initialState ? 1 : 0)
{
}
};
//--------------------------------------------------------------
/* --- */
//--------------------------------------------------------------
class CountingSemaphore : public Semaphore
{
/* Counting Semaphore helper class */
public:
explicit CountingSemaphore(const uint32_t maxCount, const uint32_t initialCount = 0)
: Semaphore(maxCount, initialCount)
{
}
};
//--------------------------------------------------------------
} // namespace sdi_uToolBox::generic::FreeRTOS

View File

@@ -0,0 +1,154 @@
//{{copyright}}
//{{version}}
//{{license}}
#pragma once
#include <FreeRTOSHeaders.h>
#include <string>
namespace sdi_uToolBox::generic::FreeRTOS
{
//--------------------------------------------------------------
class Task
{
public:
Task() = default; // Constructor
virtual ~Task(); // Destructor
Task(const Task &other) = delete; // Copy constructor
Task(Task &&other) noexcept = delete; // Move constructor
Task &operator=(const Task &other) = delete; // Copy assignment
Task &operator=(Task &&other) noexcept = delete; // Move assignment
bool startTask(); // Create and start a FreeRTOS task
void stopTask(); // Stop and destroy the task
[[nodiscard]] bool isTaskRunning() const; // Return true if the task is running
[[nodiscard]] TaskHandle_t getTaskHandle() const; // Return the task handle
protected:
TaskHandle_t m_taskHandle = nullptr;
virtual void on_taskCreated(); // Function called after successful task creation (before execution)
virtual void on_taskStarted(); // Function called after task starts
virtual void on_taskStopped(); // Function called before task stops
private:
virtual void processTask() = 0; // Task implementation
[[nodiscard]] virtual std::string getTaskName() const; // Return a name for the task
[[nodiscard]] virtual size_t getTaskStackSize() const; // Return stack size in words, not bytes
[[nodiscard]] virtual size_t getTaskPriority() const; // Return priority at which the task is created
bool m_runningFlag = false;
static void taskCode(void *pThis); // Task implementation
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Destructor */
inline Task::~Task()
{
stopTask();
}
//--------------------------------------------------------------
/* Create and start a FreeRTOS task */
inline bool Task::startTask()
{
if (isTaskRunning())
return true;
const auto xReturned = xTaskCreate(taskCode, // Function that implements the task
getTaskName().c_str(), // Text name for the task
getTaskStackSize(), // Stack size in words, not bytes
(void *)this, // Parameter passed into the task
getTaskPriority(), // Priority at which the task is created
&m_taskHandle); // Used to pass out the created task's handle
m_runningFlag = (xReturned == pdPASS);
if (m_runningFlag)
on_taskCreated();
return m_runningFlag;
}
//--------------------------------------------------------------
/* Stop and destroy the task */
inline void Task::stopTask()
{
if (m_taskHandle)
vTaskDelete(m_taskHandle);
m_taskHandle = nullptr;
m_runningFlag = false;
}
//--------------------------------------------------------------
/* Return true if the task is running */
inline bool Task::isTaskRunning() const
{
return m_runningFlag;
}
//--------------------------------------------------------------
/* Return the task handle */
inline TaskHandle_t Task::getTaskHandle() const
{
return m_taskHandle;
}
//--------------------------------------------------------------
/* Function called after successful task creation (before execution) */
inline void Task::on_taskCreated()
{
// Nothing to do...
}
//--------------------------------------------------------------
/* Function called after task starts */
inline void Task::on_taskStarted()
{
// Nothing to do...
}
//--------------------------------------------------------------
/* Function called after before stops */
inline void Task::on_taskStopped()
{
// Nothing to do...
}
//--------------------------------------------------------------
/* Return a name for the task */
inline std::string Task::getTaskName() const
{
return "";
}
//--------------------------------------------------------------
/* Return stack size in words, not bytes */
inline size_t Task::getTaskStackSize() const
{
return configMINIMAL_STACK_SIZE;
}
//--------------------------------------------------------------
/* Return priority at which the task is created */
inline size_t Task::getTaskPriority() const
{
return tskIDLE_PRIORITY + 1;
}
//--------------------------------------------------------------
/* Task implementation */
inline void Task::taskCode(void *pThis)
{
const auto task = static_cast<Task *>(pThis);
// At task starts
if (task->m_runningFlag)
task->on_taskStarted();
// Main task loop
while (task->m_runningFlag)
task->processTask();
// At task stops
task->on_taskStopped();
task->stopTask();
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::generic::FreeRTOS

View File

@@ -0,0 +1,187 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../../defs.h"
#include <FreeRTOSHeaders.h>
#include <algorithm>
#include <span>
namespace sdi_uToolBox::generic::FreeRTOS
{
//--------------------------------------------------------------
/**
* @class TaskInspector
* @brief Utility class for inspecting FreeRTOS task statistics and runtime information.
*
* This class provides static methods to retrieve information about tasks,
* such as their name, priority, runtime statistics, and stack usage.
* It is designed for embedded systems and avoids dynamic memory allocation.
*
* @note Required FreeRTOSConfig.h options:
* - configUSE_TRACE_FACILITY must be set to 1
* - configUSE_RECURSIVE_MUTEXES must be set to 1
*/
class TaskInspector
{
static constexpr size_t MAX_TASKS = 16;
public:
/**
* @struct TaskInfo
* @brief Structure holding detailed information about a FreeRTOS task.
*/
struct TaskInfo
{
const char *taskName = nullptr; // Task name
UBaseType_t currentPriority = 0; // Task current priority
UBaseType_t basePriority = 0; // Task base priority
uint32_t runTimeCounter = 0; // Total run time counter
eTaskState currentState = eInvalid; // Task state
UBaseType_t stackHighWaterMark = 0; // Stack high watermark
};
/**
* @struct RuntimeStats
* @brief Structure holding global runtime statistics for all tasks.
*/
struct RuntimeStats
{
uint32_t totalRunTime = 0; // Total run time of all tasks
uint32_t numberOfTasks = 0; // Number of tasks
};
/**
* @struct TaskRuntimeStat
* @brief Structure holding runtime statistics for a single task.
*/
struct TaskRuntimeStat
{
const char *taskName = nullptr; // Task name
uint32_t runTimeCounter = 0; // Total run time counter
Real cpuUsage = 0.0f; // CPU usage percentage
};
public:
TaskInspector() = delete; // Default constructor
~TaskInspector() = default; // Default destructor
TaskInspector(const TaskInspector &obj) = delete; // Copy constructor
TaskInspector(TaskInspector &&obj) noexcept = delete; // Move constructor
TaskInspector &operator=(const TaskInspector &obj) = delete; // Copy assignment operator
TaskInspector &operator=(TaskInspector &&obj) noexcept = delete; // Move assignment operator
/**
* @brief Retrieves information about a specific task or the current task if no handle is provided.
* @param taskHandle The handle of the task to inspect (optional).
* @return TaskInfo structure with the task's information.
*/
[[nodiscard]] static TaskInfo getTaskInfo(TaskHandle_t taskHandle = nullptr);
/**
* @brief Retrieves global runtime statistics for all tasks.
* @return RuntimeStats structure with the global runtime statistics.
*/
[[nodiscard]] static RuntimeStats GetRuntimeStats();
/**
* @brief Retrieves the number of tasks currently running.
* @return The number of tasks.
*/
[[nodiscard]] static size_t GetTaskCount();
/**
* @brief Retrieves runtime statistics for all tasks.
* @param statsArray A span to an array where task runtime statistics will be stored.
* @return The number of tasks for which statistics were retrieved.
*/
[[nodiscard]] static size_t GetTasksRuntimeStats(std::span<TaskRuntimeStat> statsArray);
};
//--------------------------------------------------------------
/* Retrieves information about the current task */
inline TaskInspector::TaskInfo TaskInspector::getTaskInfo(TaskHandle_t taskHandle)
{
#if (configUSE_RECURSIVE_MUTEXES != 1)
# error "configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h"
#endif
#if (configUSE_TRACE_FACILITY != 1)
# error "configUSE_TRACE_FACILITY must be set to 1 in FreeRTOSConfig.h"
#endif
TaskInfo taskInfo;
if (!taskHandle)
{
taskHandle = xTaskGetCurrentTaskHandle();
if (!taskHandle)
return taskInfo;
}
// Get task info
TaskStatus_t taskStatus;
vTaskGetInfo(taskHandle, &taskStatus, pdTRUE, eInvalid);
// Fill task info structure
taskInfo.taskName = taskStatus.pcTaskName;
taskInfo.currentPriority = taskStatus.uxCurrentPriority;
taskInfo.basePriority = taskStatus.uxBasePriority;
taskInfo.runTimeCounter = taskStatus.ulRunTimeCounter;
taskInfo.currentState = taskStatus.eCurrentState;
taskInfo.stackHighWaterMark = taskStatus.usStackHighWaterMark;
return taskInfo;
}
//--------------------------------------------------------------
/* Retrieves global runtime statistics for all tasks */
inline TaskInspector::RuntimeStats TaskInspector::GetRuntimeStats()
{
#if (configUSE_TRACE_FACILITY != 1)
# error "configUSE_TRACE_FACILITY must be set to 1 in FreeRTOSConfig.h"
#endif
RuntimeStats stats;
stats.numberOfTasks = uxTaskGetNumberOfTasks();
uxTaskGetSystemState(nullptr, 0, &stats.totalRunTime);
return stats;
}
//--------------------------------------------------------------
/* Retrieves the number of tasks */
inline size_t TaskInspector::GetTaskCount()
{
return uxTaskGetNumberOfTasks();
}
//--------------------------------------------------------------
/* Retrieves runtime statistics for all tasks */
inline size_t TaskInspector::GetTasksRuntimeStats(std::span<TaskRuntimeStat> statsArray)
{
#if (configUSE_TRACE_FACILITY != 1)
# error "configUSE_TRACE_FACILITY must be set to 1 in FreeRTOSConfig.h"
#endif
size_t numTasks = uxTaskGetNumberOfTasks();
numTasks = std::min({ numTasks, statsArray.size(), MAX_TASKS });
TaskStatus_t statusArray[MAX_TASKS];
uint32_t totalRunTime = 0;
uxTaskGetSystemState(statusArray, numTasks, &totalRunTime);
for (size_t i = 0; i < numTasks; i++)
{
auto &stat = statsArray[i];
stat.taskName = statusArray[i].pcTaskName;
stat.runTimeCounter = statusArray[i].ulRunTimeCounter;
stat.cpuUsage = (totalRunTime > 0) ? (100.0f * static_cast<Real>(stat.runTimeCounter) / static_cast<Real>(totalRunTime)) : 0.0f;
}
return numTasks;
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::generic::FreeRTOS

View File

@@ -0,0 +1,73 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <functional>
namespace sdi_uToolBox::generic
{
//--------------------------------------------------------------
class IGPIO
{
public:
enum class PinType
{
Invalid,
Input,
ReversedInput,
Output, // HIGH level to enable
ReversedOutput, // LOW level to enable
};
public:
IGPIO() = default; // Constructor
virtual ~IGPIO() = default; // Destructor
IGPIO(const IGPIO &other) = delete; // Copy constructor
IGPIO(IGPIO &&other) noexcept = delete; // Move constructor
IGPIO &operator=(const IGPIO &other) = delete; // Copy assignment
IGPIO &operator=(IGPIO &&other) noexcept = delete; // Move assignment
virtual void set(bool enable = true) const = 0; // Sets/Resets IO state
void reset() const; // Resets IO state
virtual bool get() const = 0; // Returns IO state
virtual void toggle() const; // Flips IO state
using Callback = std::function<void()>;
void setITCallback(const Callback &callback); // Defines the callback function for a possible interruption
protected:
Callback m_itCallback = nullptr;
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Resets IO state */
inline void IGPIO::reset() const
{
set(false);
}
//--------------------------------------------------------------
/* Flips IO state */
inline void IGPIO::toggle() const
{
const auto newValue = !get();
set(newValue);
}
//--------------------------------------------------------------
/* Defines the callback function for a possible interruption */
inline void IGPIO::setITCallback(const Callback &callback)
{
m_itCallback = callback;
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::generic

View File

@@ -0,0 +1,69 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <cstdint>
#include <span>
namespace sdi_uToolBox::generic
{
//--------------------------------------------------------------
class II2CBus
{
public:
enum class I2CStatus
{
Ok,
Timeout,
Error,
Busy,
};
public:
II2CBus() = default; // Constructor
virtual ~II2CBus() = default; // Destructor
II2CBus(const II2CBus &other) = delete; // Copy constructor
II2CBus(II2CBus &&other) noexcept = delete; // Move constructor
II2CBus &operator=(const II2CBus &other) = delete; // Copy assignment
II2CBus &operator=(II2CBus &&other) noexcept = delete; // Move assignment
virtual I2CStatus read(const uint8_t chipAddress, const std::span<uint8_t> &buffer) = 0; // Read data from I2C device
I2CStatus read(const uint8_t chipAddress, uint8_t &value); // Read data from I2C device
virtual I2CStatus write(const uint8_t chipAddress, const std::span<const uint8_t> &buffer) = 0; // Write data to I2C device
I2CStatus write(const uint8_t chipAddress, const uint8_t &value); // Write data to I2C device
// virtual I2CStatus readEeprom(const uint8_t chipAddress, const uint32_t memoryAddress, const std::span<uint8_t> &buffer) = 0; // Read data from I2C memory device
// virtual I2CStatus writeEeprom(const uint8_t chipAddress, const uint32_t memoryAddress, const std::span<const uint8_t> &buffer) = 0; // Write data to I2C memory device
virtual I2CStatus isDeviceReady(const uint8_t chipAddress) = 0; // Check device readiness
};
//--------------------------------------------------------------
//--------------------------------------------------------------
inline II2CBus::I2CStatus II2CBus::read(const uint8_t chipAddress, uint8_t &value)
{
// Create a std::span pointing to the single byte 'value'
std::span<uint8_t, 1> single_byte_span{ &value, 1 };
return read(chipAddress, single_byte_span);
}
//--------------------------------------------------------------
inline II2CBus::I2CStatus II2CBus::write(const uint8_t chipAddress, const uint8_t &value)
{
// Create a std::span pointing to the single byte 'value'
std::span<const uint8_t, 1> single_byte_span{ &value, 1 };
return write(chipAddress, single_byte_span);
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::generic

View File

@@ -0,0 +1,47 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <chrono>
#include <cstdint>
#include <span>
namespace sdi_uToolBox::generic
{
//--------------------------------------------------------------
class IStream
{
public:
IStream() = default; // Constructor
virtual ~IStream() = default; // Destructor
IStream(const IStream &other) = delete; // Copy constructor
IStream(IStream &&other) noexcept = delete; // Move constructor
IStream &operator=(const IStream &other) = delete; // Copy assignment
IStream &operator=(IStream &&other) noexcept = delete; // Move assignment
virtual size_t write(const std::span<const uint8_t> buffer) = 0; // Writes a block of data to the stream
virtual size_t read(const std::span<uint8_t> buffer) = 0; // Reads a block of data from the stream
virtual size_t read(const std::span<uint8_t> buffer, const std::chrono::milliseconds ms); // If available, wait for block of data from the stream
virtual bool isDataAvailable() const = 0; // Checks if the stream has data available for reading
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* If available, wait for block of data from the stream */
inline size_t IStream::read(const std::span<uint8_t> buffer, const std::chrono::milliseconds ms)
{
return read(buffer);
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::generic

View File

@@ -0,0 +1,113 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../../generic/FreeRTOS/mutex.h"
#include "../HIDBus.h"
namespace sdi_uToolBox::stm32::FreeRTOS
{
//--------------------------------------------------------------
template<size_t QUEUE_SIZE>
class HIDBus : public stm32::HIDBus<0>
{
public:
HIDBus(); // Constructor
~HIDBus() override = default; // Destructor
HIDBus(const HIDBus &other) = delete; // Copy constructor
HIDBus(HIDBus &&other) noexcept = delete; // Move constructor
HIDBus &operator=(const HIDBus &other) = delete; // Copy assignment
HIDBus &operator=(HIDBus &&other) noexcept = delete; // Move assignment
void onReceiveData(std::span<const uint8_t> buf) override;
size_t write(const std::span<const uint8_t> buffer) override;
size_t read(const std::span<uint8_t> buffer) override; // Reads a block of data from the stream
size_t read(const std::span<uint8_t> buffer, const std::chrono::milliseconds ms) override; // If available, wait for block of data from the stream
bool isDataAvailable() const override; // Checks if the stream has data available for reading
protected:
sdi_uToolBox::generic::FreeRTOS::Mutex m_mtx;
QueueHandle_t m_uartRxQueue = nullptr;
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Constructor */
template<size_t QUEUE_SIZE>
inline HIDBus<QUEUE_SIZE>::HIDBus()
{
// Initialization of FreeRTOS queue
m_uartRxQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint8_t));
}
//--------------------------------------------------------------
/* Receive data from interuption */
template<size_t QUEUE_SIZE>
void HIDBus<QUEUE_SIZE>::onReceiveData(std::span<const uint8_t> buf)
{
auto xHigherPriorityTaskWoken = pdFALSE;
// Copy each bytes into the queue
for (const auto byte : buf)
{
xQueueSendFromISR(m_uartRxQueue,
&byte,
&xHigherPriorityTaskWoken);
// If the data reception has woken up a higher priority task,
// force a context switch immediately after the ISR
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
//--------------------------------------------------------------
template<size_t QUEUE_SIZE>
size_t HIDBus<QUEUE_SIZE>::write(const std::span<const uint8_t> buffer)
{
std::lock_guard lock(m_mtx);
return stm32::HIDBus<0>::write(buffer);
}
//--------------------------------------------------------------
/* Reads a block of data from the stream */
template<size_t QUEUE_SIZE>
inline size_t HIDBus<QUEUE_SIZE>::read(const std::span<uint8_t> buffer)
{
return read(buffer, std::chrono::milliseconds(0));
}
//--------------------------------------------------------------
/* If available, wait for block of data from the stream */
template<size_t QUEUE_SIZE>
inline size_t HIDBus<QUEUE_SIZE>::read(const std::span<uint8_t> buffer, const std::chrono::milliseconds ms)
{
for (size_t i = 0; i < buffer.size(); i++)
{
uint8_t received_byte;
if (xQueueReceive(m_uartRxQueue,
&received_byte,
pdMS_TO_TICKS(ms.count())) != pdPASS)
return i;
buffer[i] = received_byte;
}
return buffer.size();
}
//--------------------------------------------------------------
/* Checks if the stream has data available for reading */
template<size_t QUEUE_SIZE>
inline bool HIDBus<QUEUE_SIZE>::isDataAvailable() const
{
const auto count = uxQueueMessagesWaiting(m_uartRxQueue);
return count > 0;
}
//--------------------------------------------------------------
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::FreeRTOS

View File

@@ -0,0 +1,131 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../generic/iGPIO.h"
#include <STM32Headers.h>
#include <array>
namespace sdi_uToolBox::stm32
{
//--------------------------------------------------------------
class GPIO : public sdi_uToolBox::generic::IGPIO
{
public:
struct GpioDefs
{
GPIO_TypeDef *port = nullptr;
uint16_t pin = 0;
PinType type = PinType::Invalid;
};
public:
void configure(const GpioDefs &gpioDefs); // GPIO configuration
[[nodiscard]] GPIO_TypeDef *getPort() const; // Returns IO port
[[nodiscard]] uint16_t getPin() const; // Returns IO pin
[[nodiscard]] PinType getType() const; // Returns IO type
void set(bool enable = true) const override; // Sets/Resets IO state
[[nodiscard]] bool get() const override; // Returns IO state
void toggle() const override; // Flips IO state
void enableEvent(bool enable = true); // Adds / remove GPIO handle in the indexed pin event handle list
static void on_GpioIT(uint16_t pin); // Checks GPIO IT
protected:
GpioDefs m_gpioDefs;
static inline std::array<GPIO *, 16> m_ITListHdl{}; // List of indexed pin IT handles
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* GPIO configuration */
inline void GPIO::configure(const GpioDefs &gpioDefs)
{
m_gpioDefs = gpioDefs;
}
//--------------------------------------------------------------
/* Return IO port */
inline GPIO_TypeDef *GPIO::getPort() const
{
return m_gpioDefs.port;
}
//--------------------------------------------------------------
/* Return IO pin */
inline uint16_t GPIO::getPin() const
{
return m_gpioDefs.pin;
}
//--------------------------------------------------------------
/* Return IO type */
inline GPIO::PinType GPIO::getType() const
{
return m_gpioDefs.type;
}
//--------------------------------------------------------------
/* Set/Reset logical IO state */
inline void GPIO::set(bool enable) const
{
if (m_gpioDefs.type == PinType::ReversedOutput)
enable = !enable;
HAL_GPIO_WritePin(m_gpioDefs.port,
m_gpioDefs.pin,
(enable) ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
//--------------------------------------------------------------
/* Return logical IO state */
inline bool GPIO::get() const
{
const auto state = (GPIO_PIN_SET == HAL_GPIO_ReadPin(m_gpioDefs.port, m_gpioDefs.pin));
if (m_gpioDefs.type == PinType::ReversedOutput || m_gpioDefs.type == PinType::ReversedInput)
return !state;
return state;
}
//--------------------------------------------------------------
/* Flip IO state */
inline void GPIO::toggle() const
{
HAL_GPIO_TogglePin(m_gpioDefs.port, m_gpioDefs.pin);
}
//--------------------------------------------------------------
/* Add / remove GPIO handle in the indexed pin event handle list */
inline void GPIO::enableEvent(bool enable)
{
const size_t pinIndex = __builtin_clz(m_gpioDefs.pin);
if (pinIndex >= m_ITListHdl.size())
return;
if (enable)
m_ITListHdl[pinIndex] = this;
else
m_ITListHdl[pinIndex] = nullptr;
}
//--------------------------------------------------------------
/* Check GPIO IT
*/
inline void GPIO::on_GpioIT(uint16_t pin)
{
const size_t pinIndex = __builtin_clz(pin);
if (pinIndex < m_ITListHdl.size())
{
if (m_ITListHdl[pinIndex] && m_ITListHdl[pinIndex]->getPin() == pin)
m_ITListHdl[pinIndex]->m_itCallback();
}
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32

View File

@@ -0,0 +1,102 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../generic/iStream.h"
#include <STM32Headers.h>
namespace sdi_uToolBox::stm32
{
//--------------------------------------------------------------
template<size_t QUEUE_SIZE>
class HIDBus : public sdi_uToolBox::generic::IStream
{
public:
HIDBus() = default; // Constructor
~HIDBus() override = default; // Destructor
HIDBus(const HIDBus &other) = delete; // Copy constructor
HIDBus(HIDBus &&HIDBus) noexcept = delete; // Move constructor
HIDBus &operator=(const HIDBus &other) = delete; // Copy assignment
HIDBus &operator=(HIDBus &&other) noexcept = delete; // Move assignment
void configure(USBD_HandleTypeDef *usbdHdl); // Bus configuration
[[nodiscard]] USBD_HandleTypeDef *getBus() const; // Returns bus handle
virtual void onReceiveData(std::span<const uint8_t> buf); // Read data
size_t write(const std::span<const uint8_t> buffer) override;
size_t read(const std::span<uint8_t> buffer) override;
bool isDataAvailable() const override;
protected:
USBD_HandleTypeDef *m_usbdHdl = nullptr;
sdi_toolBox::generic::CircularBuffer<uint8_t, QUEUE_SIZE> m_rxBuffer;
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Bus configuration */
template<size_t QUEUE_SIZE>
inline void HIDBus<QUEUE_SIZE>::configure(USBD_HandleTypeDef *usbdHdl)
{
m_usbdHdl = usbdHdl;
}
//--------------------------------------------------------------
/* Returns bus handle */
template<size_t QUEUE_SIZE>
inline USBD_HandleTypeDef *HIDBus<QUEUE_SIZE>::getBus() const
{
return m_usbdHdl;
}
//--------------------------------------------------------------
/* Read data from IT and restart the reading process */
template<size_t QUEUE_SIZE>
inline void HIDBus<QUEUE_SIZE>::onReceiveData(const std::span<const uint8_t> buf)
{
for (const auto &c : buf)
m_rxBuffer.push(c);
}
//--------------------------------------------------------------
template<size_t QUEUE_SIZE>
size_t HIDBus<QUEUE_SIZE>::write(const std::span<const uint8_t> buffer)
{
USBD_CUSTOM_HID_SendReport(m_usbdHdl, const_cast<uint8_t*>(buffer.data()), buffer.size());
return 0;//TODO: correct return value
}
template<size_t QUEUE_SIZE>
size_t HIDBus<QUEUE_SIZE>::read(const std::span<uint8_t> buffer)
{
CriticalSection criticalSection;
for (size_t i = 0; i < buffer.size(); i++)
{
const auto data = m_rxBuffer.pop();
if (!data)
return i;
buffer[i] = *data;
}
return buffer.size();
}
template<size_t QUEUE_SIZE>
bool HIDBus<QUEUE_SIZE>::isDataAvailable() const
{
CriticalSection criticalSection;
return !m_rxBuffer.isEmpty();
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32

View File

@@ -0,0 +1,117 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../generic/iI2CBus.h"
#include <STM32Headers.h>
namespace sdi_uToolBox::stm32
{
//--------------------------------------------------------------
class I2CBus : public sdi_uToolBox::generic::II2CBus
{
public:
void configure(I2C_HandleTypeDef *i2cHdl, // Bus configuration
const uint32_t delay = HAL_MAX_DELAY);
[[nodiscard]] I2C_HandleTypeDef *getBus() const; // Returns bus handle
[[nodiscard]] uint32_t getDelay() const; // Returns timeout delay
I2CStatus write(const uint8_t chipAddress, const std::span<const uint8_t> &buffer) override; // Write data to I2C device
I2CStatus read(const uint8_t chipAddress, const std::span<uint8_t> &buffer) override; // Read data from I2C device
I2CStatus isDeviceReady(const uint8_t chipAddress) override; // Check device readiness
protected:
I2C_HandleTypeDef *m_i2cHandle = nullptr;
uint32_t m_delay = 0;
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Bus configuration */
inline void I2CBus::configure(I2C_HandleTypeDef *i2cHdl, const uint32_t delay)
{
m_i2cHandle = i2cHdl;
m_delay = delay;
}
//--------------------------------------------------------------
/* Returns bus handle */
inline I2C_HandleTypeDef *I2CBus::getBus() const
{
return m_i2cHandle;
}
//--------------------------------------------------------------
/* Return IO pin */
inline uint32_t I2CBus::getDelay() const
{
return m_delay;
}
//--------------------------------------------------------------
/* Write data to I2C device */
inline I2CBus::I2CStatus I2CBus::write(const uint8_t chipAddress, const std::span<const uint8_t> &buffer)
{
const auto ret = HAL_I2C_Master_Transmit(m_i2cHandle,
chipAddress << 1,
const_cast<uint8_t *>(buffer.data()),
buffer.size(),
m_delay);
if (ret == HAL_OK)
return I2CStatus::Ok;
else if (ret == HAL_BUSY)
return I2CStatus::Busy;
else if (ret == HAL_ERROR && m_i2cHandle->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
return I2CStatus::Timeout;
else
return I2CStatus::Error;
}
//--------------------------------------------------------------
/* Read data from I2C device */
inline I2CBus::I2CStatus I2CBus::read(const uint8_t chipAddress, const std::span<uint8_t> &buffer)
{
const auto ret = HAL_I2C_Master_Receive(m_i2cHandle,
chipAddress << 1,
buffer.data(),
buffer.size(),
m_delay);
if (ret == HAL_OK)
return I2CStatus::Ok;
else if (ret == HAL_BUSY)
return I2CStatus::Busy;
else if (ret == HAL_ERROR && m_i2cHandle->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
return I2CStatus::Timeout;
else
return I2CStatus::Error;
}
//--------------------------------------------------------------
/* Check device readiness */
inline I2CBus::I2CStatus I2CBus::isDeviceReady(const uint8_t chipAddress)
{
const auto ret = HAL_I2C_IsDeviceReady(m_i2cHandle,
chipAddress << 1,
5,
m_delay);
if (ret == HAL_OK)
return I2CStatus::Ok;
else if (ret == HAL_BUSY)
return I2CStatus::Busy;
else if (ret == HAL_ERROR && m_i2cHandle->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
return I2CStatus::Timeout;
else
return I2CStatus::Error;
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32

View File

@@ -0,0 +1,83 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../generic/iStream.h"
#include <STM32Headers.h>
#include <array>
#include <cstring>
namespace sdi_uToolBox::stm32
{
//--------------------------------------------------------------
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE>
class UARTBus : public sdi_uToolBox::generic::IStream
{
public:
UARTBus() = default; // Constructor
~UARTBus() override = default; // Destructor
UARTBus(const UARTBus &other) = delete; // Copy constructor
UARTBus(UARTBus &&UARTBus) noexcept = delete; // Move constructor
UARTBus &operator=(const UARTBus &other) = delete; // Copy assignment
UARTBus &operator=(UARTBus &&other) noexcept = delete; // Move assignment
void configure(UART_HandleTypeDef *uartHdl); // Bus configuration
[[nodiscard]] UART_HandleTypeDef *getBus() const; // Returns bus handle
void onReceiveData_fromISR(const size_t size); // Read data from IT and restart the reading process
protected:
UART_HandleTypeDef *m_uartHdl = nullptr;
std::array<uint8_t, UART_RX_BUFFER_SIZE> m_uartRXBuffer;
std::array<uint8_t, UART_TX_BUFFER_SIZE> m_uartTXBuffer;
private:
virtual void initDevice() = 0; // Device initialization
virtual bool startReceiving() = 0; // Start specific receiving method (IT, DMA, ...)
virtual void receiveData_fromISR(const uint8_t *buffer, const size_t size) = 0; // Receive data from interuption
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Bus configuration */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE>
inline void UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::configure(UART_HandleTypeDef *uartHdl)
{
m_uartHdl = uartHdl;
}
//--------------------------------------------------------------
/* Returns bus handle */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE>
inline UART_HandleTypeDef *UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::getBus() const
{
return m_uartHdl;
}
//--------------------------------------------------------------
/* Read data from IT and restart the reading process */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE>
inline void UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::onReceiveData_fromISR(const size_t size)
{
// Read data
receiveData_fromISR(m_uartRXBuffer.data(), size);
// Restart receiving process
if (!startReceiving())
{
// Quick fix in case of failure to start the receiving process
initDevice();
startReceiving();
}
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32

View File

@@ -0,0 +1,140 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "UARTBus.h"
#include "criticalSection.h"
#include <sdi_toolBox/generic/circularBuffer.h>
namespace sdi_uToolBox::stm32
{
//--------------------------------------------------------------
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE, size_t QUEUE_SIZE>
class UARTBusIT : public sdi_uToolBox::stm32::UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>
{
public:
UARTBusIT() = default; // Constructor
~UARTBusIT() override = default; // Destructor
UARTBusIT(const UARTBusIT &other) = delete; // Copy constructor
UARTBusIT(UARTBusIT &&other) noexcept = delete; // Move constructor
UARTBusIT &operator=(const UARTBusIT &other) = delete; // Copy assignment
UARTBusIT &operator=(UARTBusIT &&other) noexcept = delete; // Move assignment
size_t write(const std::span<const uint8_t> buffer) override; // Writes a block of data to the stream
size_t read(const std::span<uint8_t> buffer) override; // Reads a block of data from the stream
bool isDataAvailable() const override; // Checks if the stream has data available for reading
bool isTxReady(); // Returns true if the TX stream is ready and available
void on_transmitComplete_fromISR(); // Interrupt-based transmission is over
protected:
volatile bool m_txBufferIsEmpty = true;
sdi_toolBox::generic::CircularBuffer<uint8_t, QUEUE_SIZE> m_rxBuffer;
private:
void initDevice() override; // Device initialization
bool startReceiving() override; // Start specific receiving method (IT, DMA, ...)
void receiveData_fromISR(const uint8_t *buffer, const size_t size) override; // Receive data from interuption
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Writes a block of data to the stream */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE, size_t QUEUE_SIZE>
inline size_t UARTBusIT<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE, QUEUE_SIZE>::write(const std::span<const uint8_t> buffer)
{
if (!isTxReady())
return 0;
if (UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::m_uartHdl->gState != HAL_UART_STATE_READY)
return 0;
const auto len = std::min(buffer.size(), UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::m_uartTXBuffer.size());
std::memcpy(UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::m_uartTXBuffer.data(),
buffer.data(),
len);
if (HAL_OK != HAL_UART_Transmit_IT(UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::m_uartHdl,
UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::m_uartRXBuffer.data(),
UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::m_uartRXBuffer.size()))
return 0;
m_txBufferIsEmpty = false;
return len;
}
//--------------------------------------------------------------
/* Reads a block of data from the stream */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE, size_t QUEUE_SIZE>
inline size_t UARTBusIT<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE, QUEUE_SIZE>::read(const std::span<uint8_t> buffer)
{
CriticalSection criticalSection;
for (size_t i = 0; i < buffer.size(); i++)
{
const auto data = m_rxBuffer.pop();
if (!data)
return i;
buffer[i] = *data;
}
return buffer.size();
}
//--------------------------------------------------------------
/* Checks if the stream has data available for reading */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE, size_t QUEUE_SIZE>
inline bool UARTBusIT<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE, QUEUE_SIZE>::isDataAvailable() const
{
CriticalSection criticalSection;
return !m_rxBuffer.isEmpty();
}
//--------------------------------------------------------------
/* Returns true if the TX stream is ready and available */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE, size_t QUEUE_SIZE>
inline bool UARTBusIT<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE, QUEUE_SIZE>::isTxReady()
{
return m_txBufferIsEmpty;
}
//--------------------------------------------------------------
/* Interrupt-based transmission is over */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE, size_t QUEUE_SIZE>
inline void UARTBusIT<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE, QUEUE_SIZE>::on_transmitComplete_fromISR()
{
m_txBufferIsEmpty = true;
}
//--------------------------------------------------------------
/* Start specific receiving method (IT, DMA, ...) */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE, size_t QUEUE_SIZE>
inline bool UARTBusIT<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE, QUEUE_SIZE>::startReceiving()
{
return (HAL_OK != HAL_UARTEx_ReceiveToIdle_IT(UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::m_uartHdl,
UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::m_uartRXBuffer.data(),
UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::m_uartRXBuffer.size()));
}
//--------------------------------------------------------------
/* Device initialization */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE, size_t QUEUE_SIZE>
void UARTBusIT<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE, QUEUE_SIZE>::initDevice()
{
// TODO
}
//--------------------------------------------------------------
/* Receive data from interuption */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE, size_t QUEUE_SIZE>
inline void UARTBusIT<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE, QUEUE_SIZE>::receiveData_fromISR(const uint8_t *buffer, const size_t size)
{
// Operations performed within an interrupt routine are considered atomic
for (size_t i = 0; i < size; i++)
m_rxBuffer.push(buffer[i]);
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32

View File

@@ -0,0 +1,89 @@
//{{copyright}}
//{{version}}
//{{license}}
#pragma once
#include "../HIDBus.h"
#include "../../../dateTime/FreeRTOS/timer.h"
#include <sdi_uToolBox/generic/FreeRTOS/mutex.h>
#include <sdi_uToolBox/generic/FreeRTOS/semaphore.h>
namespace sdi_uToolBox::stm32::comm::FreeRTOS
{
//--------------------------------------------------------------
class HIDBus : public comm::HIDBus
{
public:
using Mutex = generic::FreeRTOS::Mutex;
using BinarySemaphore = generic::FreeRTOS::BinarySemaphore;
public:
HIDBus(); // Default constructor
virtual ~HIDBus() = default; // Default destructor
HIDBus(const HIDBus &) = delete; // Copy constructor
HIDBus(HIDBus &&) = delete; // Move constructor
HIDBus &operator=(const HIDBus &) = delete; // Copy assignment operator
HIDBus &operator=(HIDBus &&) = delete; // Move assignment operator
ReturnStatus open() override; // Open UART bus
ReturnStatus close() override; // Close UART bus
ReturnType write(WriteBuf buf, Timeout timeout) const override; // Write data to UART device
ReturnType read(ReadBuf buf, Timeout timeout) const override; // Read data from UART device
void rxCompleteCallback(std::span<const uint8_t> buf) override;
protected:
mutable Mutex m_mtx;
mutable BinarySemaphore m_semaphore{false};
dateTime::FreeRTOS::Timer m_timer;
};
//--------------------------------------------------------------
inline HIDBus::HIDBus()
{
m_genericTimer = &m_timer;
}
//--------------------------------------------------------------
/* Open UART bus */
inline ReturnStatus HIDBus::open()
{
std::scoped_lock lock(m_mtx);
return comm::HIDBus::open();
}
//--------------------------------------------------------------
/* Close UART bus */
inline ReturnStatus HIDBus::close()
{
std::scoped_lock lock(m_mtx);
return comm::HIDBus::close();
}
//--------------------------------------------------------------
/* Write data to UART device */
inline HIDBus::ReturnType HIDBus::write(const WriteBuf buf, const Timeout timeout) const
{
std::scoped_lock lock(m_mtx);
return comm::HIDBus::write(buf, timeout);
}
//--------------------------------------------------------------
/* Read data from UART device */
inline HIDBus::ReturnType HIDBus::read(const ReadBuf buf, const Timeout timeout) const
{
if (!m_semaphore.take(timeout))
return std::unexpected(ReturnStatus::Timeout);
std::scoped_lock lock(m_mtx);
return comm::HIDBus::read(buf, std::chrono::milliseconds(0));
}
//--------------------------------------------------------------
inline void HIDBus::rxCompleteCallback(std::span<const uint8_t> buf)
{
comm::HIDBus::rxCompleteCallback(buf);
(void)m_semaphore.giveFromISR();
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::comm

View File

@@ -0,0 +1,91 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../I2CBus.h"
#include <sdi_uToolBox/generic/FreeRTOS/mutex.h>
namespace sdi_uToolBox::stm32::comm::FreeRTOS
{
//--------------------------------------------------------------
class I2CBus : public stm32::comm::I2CBus
{
public:
using RecursiveMutex = generic::FreeRTOS::RecursiveMutex;
public:
I2CBus() = default; // Default constructor
virtual ~I2CBus() override = default; // Default destructor
I2CBus(const I2CBus &) = delete; // Copy constructor
I2CBus(I2CBus &&) = delete; // Move constructor
I2CBus &operator=(const I2CBus &) = delete; // Copy assignment operator
I2CBus &operator=(I2CBus &&) = delete; // Move assignment operator
ReturnStatus open() override; // Open I2C bus
ReturnStatus close() override; // Close I2C bus
std::unique_lock<RecursiveMutex> getLock() const; // Get unique lock for the bus
ReturnType write(uint8_t address, WriteBuf buf, Timeout timeout) const override; // Write data to I2C device
ReturnType read(uint8_t address, ReadBuf buf, Timeout timeout) const override; // Read data from I2C device
ReturnStatus isDeviceReady(uint8_t address) const override; // Check if I2C device is ready
protected:
mutable RecursiveMutex m_mtx; // Mutex for thread safety
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Open I2C bus */
inline ReturnStatus I2CBus::open()
{
std::scoped_lock lock(m_mtx);
return stm32::comm::I2CBus::open();
}
//--------------------------------------------------------------
/* Close I2C bus */
inline ReturnStatus I2CBus::close()
{
std::scoped_lock lock(m_mtx);
return stm32::comm::I2CBus::close();
}
//--------------------------------------------------------------
/* Get unique lock for the bus */
inline std::unique_lock<I2CBus::RecursiveMutex> I2CBus::getLock() const
{
return std::unique_lock(m_mtx);
}
//--------------------------------------------------------------
/* Write data to I2C device */
inline I2CBus::ReturnType I2CBus::write(const uint8_t address, const WriteBuf buf, const Timeout timeout) const
{
std::scoped_lock lock(m_mtx);
return stm32::comm::I2CBus::write(address, buf, timeout);
}
//--------------------------------------------------------------
/* Read data from I2C device */
inline I2CBus::ReturnType I2CBus::read(const uint8_t address, const ReadBuf buf, const Timeout timeout) const
{
std::scoped_lock lock(m_mtx);
return stm32::comm::I2CBus::read(address, buf, timeout);
}
//--------------------------------------------------------------
/* Check if I2C device is ready */
inline ReturnStatus I2CBus::isDeviceReady(const uint8_t address) const
{
std::scoped_lock lock(m_mtx);
return stm32::comm::I2CBus::isDeviceReady(address);
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::comm::FreeRTOS

View File

@@ -0,0 +1,73 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../UARTBus.h"
#include <sdi_uToolBox/generic/FreeRTOS/mutex.h>
namespace sdi_uToolBox::stm32::comm::FreeRTOS
{
//--------------------------------------------------------------
class UARTBus : public stm32::comm::UARTBus
{
public:
using Mutex = generic::FreeRTOS::Mutex;
public:
UARTBus() = default; // Default constructor
virtual ~UARTBus() override = default; // Default destructor
UARTBus(const UARTBus &) = delete; // Copy constructor
UARTBus(UARTBus &&) = delete; // Move constructor
UARTBus &operator=(const UARTBus &) = delete; // Copy assignment operator
UARTBus &operator=(UARTBus &&) = delete; // Move assignment operator
ReturnStatus open() override; // Open UART bus
ReturnStatus close() override; // Close UART bus
ReturnType write(WriteBuf buf, Timeout timeout) const override; // Write data to UART device
ReturnType read(ReadBuf buf, Timeout timeout) const override; // Read data from UART device
protected:
mutable Mutex m_mtx; // Mutex for thread safety
};
//--------------------------------------------------------------
/* Open UART bus */
inline ReturnStatus UARTBus::open()
{
std::scoped_lock lock(m_mtx);
return stm32::comm::UARTBus::open();
}
//--------------------------------------------------------------
/* Close UART bus */
inline ReturnStatus UARTBus::close()
{
std::scoped_lock lock(m_mtx);
return stm32::comm::UARTBus::close();
}
//--------------------------------------------------------------
/* Write data to UART device */
inline UARTBus::ReturnType UARTBus::write(const WriteBuf buf, const Timeout timeout) const
{
std::scoped_lock lock(m_mtx);
return stm32::comm::UARTBus::write(buf, timeout);
}
//--------------------------------------------------------------
/* Read data from UART device */
inline UARTBus::ReturnType UARTBus::read(const ReadBuf buf, const Timeout timeout) const
{
std::scoped_lock lock(m_mtx);
return stm32::comm::UARTBus::read(buf, timeout);
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::comm::FreeRTOS

View File

@@ -0,0 +1,283 @@
//{{copyright}}
//{{version}}
//{{license}}
#pragma once
#include "../UARTBus.h"
#include <atomic>
#include <sdi_uToolBox/generic/FreeRTOS/mutex.h>
#include <sdi_uToolBox/generic/FreeRTOS/semaphore.h>
namespace sdi_uToolBox::stm32::comm::FreeRTOS
{
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
class UARTBusDMA : public stm32::comm::UARTBus
, public UARTBusCallback
{
public:
using Mutex = generic::FreeRTOS::Mutex;
using BinarySemaphore = generic::FreeRTOS::BinarySemaphore;
public:
UARTBusDMA() = default; // Default constructor
virtual ~UARTBusDMA(); // Default destructor
UARTBusDMA(const UARTBusDMA &) = delete; // Copy constructor
UARTBusDMA(UARTBusDMA &&) = delete; // Move constructor
UARTBusDMA &operator=(const UARTBusDMA &) = delete; // Copy assignment operator
UARTBusDMA &operator=(UARTBusDMA &&) = delete; // Move assignment operator
ReturnStatus configure(UART_HandleTypeDef *handle) override; // Configure UART bus with HAL handle
ReturnStatus open() override; // Open UART bus
ReturnStatus close() override; // Close UART bus
ReturnType write(WriteBuf buf, Timeout timeout) const override; // Write data to UART device
ReturnType read(ReadBuf buf, Timeout timeout) const override; // Read data from UART device
ReturnType readUntilIdle(ReadBuf buf, Timeout timeout) const override; // Read data until IDLE event
// Callback to be called from ISR when TX, RX is complete or error occurs
void txCompleteCallback() const override; // TX complete callback
void rxCompleteCallback() const override; // RX complete callback
void rxEventCallback(uint16_t size) const override; // IDLE line detected callback
void errorCallback() const override; // Error callback
protected:
mutable Mutex m_txMtx; // Mutex for TX thread safety
mutable Mutex m_rxMtx; // Mutex for RX thread safety
mutable BinarySemaphore m_txSemaphore{ false }; // Semaphore for TX completion
mutable BinarySemaphore m_rxSemaphore{ false }; // Semaphore for RX completion
alignas(32) mutable std::array<uint8_t, TX_BUFFER_SIZE> m_txBuffer; // Transmit buffer aligned for DMA
alignas(32) mutable std::array<uint8_t, RX_BUFFER_SIZE> m_rxBuffer; // Receive buffer aligned for DMA
mutable std::atomic<size_t> m_lastTxSize{ 0 };
mutable std::atomic<size_t> m_lastRxSize{ 0 };
mutable std::atomic<bool> m_txError{ false };
mutable std::atomic<bool> m_rxError{ false };
};
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::~UARTBusDMA()
{
UARTBusCallbackRegistry::unregisterInstance(this);
}
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline ReturnStatus UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::configure(UART_HandleTypeDef *handle)
{
const auto ret = UARTBus::configure(handle);
if (ret != ReturnStatus::Ok)
return ret;
UARTBusCallbackRegistry::registerInstance(handle, this);
return ReturnStatus::Ok;
}
//--------------------------------------------------------------
/* Open UART bus */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline ReturnStatus UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::open()
{
std::scoped_lock lock(m_txMtx, m_rxMtx);
return UARTBus::open();
}
//--------------------------------------------------------------
/* Close UART bus */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline ReturnStatus UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::close()
{
std::scoped_lock lock(m_txMtx, m_rxMtx);
__HAL_UART_DISABLE_IT(m_uartHandle, UART_IT_IDLE); // Disable IDLE interrupt
HAL_UART_DMAStop(m_uartHandle); // Abort ongoing DMA operations
return UARTBus::close();
}
//--------------------------------------------------------------
/* Write data to UART device */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBus::ReturnType UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::write(const WriteBuf buf, const Timeout timeout) const
{
std::scoped_lock lock(m_txMtx);
if (!m_uartHandle)
return std::unexpected(ReturnStatus::InvalidHandle);
if (buf.size() > TX_BUFFER_SIZE)
return std::unexpected(ReturnStatus::Error);
// Copy data to internal buffer
std::copy_n(buf.begin(), buf.size(), m_txBuffer.begin());
// Reset flags
m_txError = false;
m_lastTxSize = 0;
// Start transmission with DMA
const auto ret = HAL_UART_Transmit_DMA(m_uartHandle,
m_txBuffer.data(),
buf.size());
if (ret != HAL_OK)
{
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
return std::unexpected(ReturnStatus::Error);
}
// Wait for transmission to complete. The timeout is handled by the semaphore
// The task will be unblocked when the semaphore is given in the DMA ISR
if (!m_txSemaphore.take(timeout))
{
HAL_UART_AbortTransmit(m_uartHandle);
return std::unexpected(ReturnStatus::Timeout);
}
if (m_txError)
return std::unexpected(ReturnStatus::Error);
return m_lastTxSize; // Return number of bytes transmitted
}
//--------------------------------------------------------------
/* Read data from UART device */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBus::ReturnType UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::read(const ReadBuf buf, const Timeout timeout) const
{
std::scoped_lock lock(m_rxMtx);
if (!m_uartHandle)
return std::unexpected(ReturnStatus::InvalidHandle);
if (buf.size() > RX_BUFFER_SIZE)
return std::unexpected(ReturnStatus::Error);
// Reset flags
m_rxError = false;
m_lastRxSize = 0;
// Start reception with DMA
const auto ret = HAL_UART_Receive_DMA(m_uartHandle,
m_rxBuffer.data(),
buf.size());
if (ret != HAL_OK)
{
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
return std::unexpected(ReturnStatus::Error);
}
// Wait for IDLE event or buffer full. The timeout is handled by the semaphore
// The task will be unblocked when the semaphore is given in the DMA ISR
if (!m_rxSemaphore.take(timeout))
{
HAL_UART_AbortReceive(m_uartHandle);
return std::unexpected(ReturnStatus::Timeout);
}
if (m_rxError)
return std::unexpected(ReturnStatus::Error);
// Copy received data to user buffer
std::copy_n(m_rxBuffer.begin(), m_lastRxSize.load(), buf.begin());
return m_lastRxSize; // Return number of bytes received
}
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBus::ReturnType UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::readUntilIdle(ReadBuf buf, Timeout timeout) const
{
std::scoped_lock lock(m_rxMtx);
if (!m_uartHandle)
return std::unexpected(ReturnStatus::InvalidHandle);
if (buf.size() > RX_BUFFER_SIZE)
return std::unexpected(ReturnStatus::Error);
// Reset flags
m_rxError = false;
m_lastRxSize = 0;
// Start reception with DMA (buffer size is the maximum expected)
if (m_uartHandle->RxState != HAL_UART_STATE_READY)
HAL_UART_AbortReceive(m_uartHandle);
const auto ret = HAL_UARTEx_ReceiveToIdle_DMA(m_uartHandle,
m_rxBuffer.data(),
buf.size());
if (ret != HAL_OK)
{
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
return std::unexpected(ReturnStatus::Error);
}
// Wait for reception to complete or idled. The timeout is handled by the semaphore
// The task will be unblocked when the semaphore is given in the ISR
if (!m_rxSemaphore.take(timeout))
{
HAL_UART_AbortReceive(m_uartHandle);
return std::unexpected(ReturnStatus::Timeout);
}
if (m_rxError)
return std::unexpected(ReturnStatus::Error);
// Copy received data to user buffer
std::copy_n(m_rxBuffer.begin(), m_lastRxSize.load(), buf.begin());
return m_lastRxSize;
}
//--------------------------------------------------------------
/* TX complete callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::txCompleteCallback() const
{
m_lastTxSize = m_uartHandle->TxXferSize - m_uartHandle->TxXferCount;
(void)m_txSemaphore.giveFromISR();
}
//--------------------------------------------------------------
/* RX complete callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::rxCompleteCallback() const
{
m_lastRxSize = m_uartHandle->RxXferSize - m_uartHandle->RxXferCount;
(void)m_rxSemaphore.giveFromISR();
}
//--------------------------------------------------------------
/* IDLE line detected callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::rxEventCallback(const uint16_t size) const
{
if (m_uartHandle->RxState == HAL_UART_STATE_READY)
{
// Stop DMA transfer
HAL_UART_AbortReceive(m_uartHandle);
// Calculate received bytes: total size - remaining count
// m_lastRxSize = m_uartHandle->RxXferSize - __HAL_DMA_GET_COUNTER(m_uartHandle->hdmarx);
m_lastRxSize = size;
(void)m_rxSemaphore.giveFromISR();
}
}
//--------------------------------------------------------------
/* Error callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusDMA<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::errorCallback() const
{
if (m_uartHandle->gState == HAL_UART_STATE_BUSY_TX)
{
m_txError = true;
(void)m_txSemaphore.giveFromISR();
}
if (m_uartHandle->RxState == HAL_UART_STATE_BUSY_RX)
{
m_rxError = true;
(void)m_rxSemaphore.giveFromISR();
}
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::comm::FreeRTOS

View File

@@ -0,0 +1,280 @@
//{{copyright}}
//{{version}}
//{{license}}
#pragma once
#include "../UARTBus.h"
#include <atomic>
#include <sdi_uToolBox/generic/FreeRTOS/mutex.h>
#include <sdi_uToolBox/generic/FreeRTOS/semaphore.h>
namespace sdi_uToolBox::stm32::comm::FreeRTOS
{
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
class UARTBusIT : public stm32::comm::UARTBus
, public UARTBusCallback
{
public:
using Mutex = generic::FreeRTOS::Mutex;
using BinarySemaphore = generic::FreeRTOS::BinarySemaphore;
public:
UARTBusIT() = default; // Default constructor
virtual ~UARTBusIT(); // Default destructor
UARTBusIT(const UARTBusIT &) = delete; // Copy constructor
UARTBusIT(UARTBusIT &&) = delete; // Move constructor
UARTBusIT &operator=(const UARTBusIT &) = delete; // Copy assignment operator
UARTBusIT &operator=(UARTBusIT &&) = delete; // Move assignment operator
ReturnStatus configure(UART_HandleTypeDef *handle) override; // Configure UART bus with HAL handle
ReturnStatus open() override; // Open UART bus
ReturnStatus close() override; // Close UART bus
ReturnType write(WriteBuf buf, Timeout timeout) const override; // Write data to UART device
ReturnType read(ReadBuf buf, Timeout timeout) const override; // Read data from UART device
ReturnType readUntilIdle(ReadBuf buf, Timeout timeout) const; // Read data until IDLE event
// Callback to be called from ISR when TX, RX is complete or error occurs
void txCompleteCallback() const override; // TX complete callback
void rxCompleteCallback() const override; // RX complete callback
void rxEventCallback(uint16_t size) const override; // IDLE line detected callback
void errorCallback() const override; // Error callback
protected:
mutable Mutex m_txMtx; // Mutex for TX thread safety
mutable Mutex m_rxMtx; // Mutex for RX thread safety
mutable BinarySemaphore m_txSemaphore{ false }; // Semaphore for TX completion
mutable BinarySemaphore m_rxSemaphore{ false }; // Semaphore for RX completion
mutable std::array<uint8_t, TX_BUFFER_SIZE> m_txBuffer;
mutable std::array<uint8_t, RX_BUFFER_SIZE> m_rxBuffer;
mutable std::atomic<size_t> m_lastTxSize{ 0 };
mutable std::atomic<size_t> m_lastRxSize{ 0 };
mutable std::atomic<bool> m_txError{ false };
mutable std::atomic<bool> m_rxError{ false };
};
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::~UARTBusIT()
{
UARTBusCallbackRegistry::unregisterInstance(this);
}
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline ReturnStatus UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::configure(UART_HandleTypeDef *handle)
{
const auto ret = UARTBus::configure(handle);
if (ret != ReturnStatus::Ok)
return ret;
UARTBusCallbackRegistry::registerInstance(handle, this);
return ReturnStatus::Ok;
}
//--------------------------------------------------------------
/* Open UART bus */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline ReturnStatus UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::open()
{
std::scoped_lock lock(m_txMtx, m_rxMtx);
return UARTBus::open();
}
//--------------------------------------------------------------
/* Close UART bus */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline ReturnStatus UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::close()
{
std::scoped_lock lock(m_txMtx, m_rxMtx);
__HAL_UART_DISABLE_IT(m_uartHandle, UART_IT_IDLE); // Disable IDLE interrupt
return UARTBus::close();
}
//--------------------------------------------------------------
/* Write data to UART device */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBus::ReturnType UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::write(const WriteBuf buf, const Timeout timeout) const
{
std::scoped_lock lock(m_txMtx);
if (!m_uartHandle)
return std::unexpected(ReturnStatus::InvalidHandle);
if (buf.size() > TX_BUFFER_SIZE)
return std::unexpected(ReturnStatus::Error);
// Copy data to internal buffer
std::copy_n(buf.begin(), buf.size(), m_txBuffer.begin());
// Reset flags
m_txError = false;
m_lastTxSize = 0;
// Start transmission with interrupt
const auto ret = HAL_UART_Transmit_IT(m_uartHandle,
m_txBuffer.data(),
buf.size());
if (ret != HAL_OK)
{
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
return std::unexpected(ReturnStatus::Error);
}
// Wait for transmission to complete. The timeout is handled by the semaphore
// The task will be unblocked when the semaphore is given in the ISR
if (!m_txSemaphore.take(timeout))
{
HAL_UART_AbortTransmit(m_uartHandle);
return std::unexpected(ReturnStatus::Timeout);
}
if (m_txError)
return std::unexpected(ReturnStatus::Error);
return m_lastTxSize; // Return number of bytes transmitted
}
//--------------------------------------------------------------
/* Read data from UART device */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBus::ReturnType UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::read(const ReadBuf buf, const Timeout timeout) const
{
std::scoped_lock lock(m_rxMtx);
if (!m_uartHandle)
return std::unexpected(ReturnStatus::InvalidHandle);
if (buf.size() > RX_BUFFER_SIZE)
return std::unexpected(ReturnStatus::Error);
// Reset flags
m_rxError = false;
m_lastRxSize = 0;
// Start reception with interrupt
const auto ret = HAL_UART_Receive_IT(m_uartHandle,
m_rxBuffer.data(),
buf.size());
if (ret != HAL_OK)
{
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
return std::unexpected(ReturnStatus::Error);
}
// Wait for reception to complete. The timeout is handled by the semaphore
// The task will be unblocked when the semaphore is given in the ISR
if (!m_rxSemaphore.take(timeout))
{
HAL_UART_AbortReceive(m_uartHandle);
return std::unexpected(ReturnStatus::Timeout);
}
if (m_rxError)
return std::unexpected(ReturnStatus::Error);
// Copy received data to user buffer
std::copy_n(m_rxBuffer.begin(), m_lastRxSize.load(), buf.begin());
return m_lastRxSize; // Return number of bytes received
}
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBus::ReturnType UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::readUntilIdle(ReadBuf buf, Timeout timeout) const
{
std::scoped_lock lock(m_rxMtx);
if (!m_uartHandle)
return std::unexpected(ReturnStatus::InvalidHandle);
if (buf.size() > RX_BUFFER_SIZE)
return std::unexpected(ReturnStatus::Error);
// Reset flags
m_rxError = false;
m_lastRxSize = 0;
// Start reception with interrupt
const auto ret = HAL_UART_ReceiveToIdle_IT(m_uartHandle,
m_rxBuffer.data(),
buf.size());
if (ret != HAL_OK)
{
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
return std::unexpected(ReturnStatus::Error);
}
// Wait for reception to complete or idled. The timeout is handled by the semaphore
// The task will be unblocked when the semaphore is given in the ISR
if (!m_rxSemaphore.take(timeout))
{
HAL_UART_AbortReceive(m_uartHandle);
return std::unexpected(ReturnStatus::Timeout);
}
if (m_rxError)
return std::unexpected(ReturnStatus::Error);
// Copy received data to user buffer
std::copy_n(m_rxBuffer.begin(), m_lastRxSize.load(), buf.begin());
return m_lastRxSize; // Return number of bytes received
}
//--------------------------------------------------------------
/* TX complete callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::txCompleteCallback() const
{
m_lastTxSize = m_uartHandle->TxXferSize - m_uartHandle->TxXferCount;
(void)m_txSemaphore.giveFromISR();
}
//--------------------------------------------------------------
/* RX complete callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::rxCompleteCallback() const
{
m_lastRxSize = m_uartHandle->RxXferSize - m_uartHandle->RxXferCount;
(void)m_rxSemaphore.giveFromISR();
}
//--------------------------------------------------------------
/* IDLE line detected callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::rxEventCallback(const uint16_t size) const
{
if (m_uartHandle->RxState == HAL_UART_STATE_BUSY_RX)
{
// Stop IT transfer
HAL_UART_AbortReceive_IT(m_uartHandle);
// Calculate received bytes
//m_lastRxSize = m_uartHandle->RxXferSize - m_uartHandle->RxXferCount;
m_lastRxSize = size;
(void)m_rxSemaphore.giveFromISR();
}
}
//--------------------------------------------------------------
/* Error callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::errorCallback() const
{
if (m_uartHandle->gState == HAL_UART_STATE_BUSY_TX)
{
m_txError = true;
(void)m_txSemaphore.giveFromISR();
}
if (m_uartHandle->RxState == HAL_UART_STATE_BUSY_RX)
{
m_rxError = true;
(void)m_rxSemaphore.giveFromISR();
}
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::comm::FreeRTOS

View File

@@ -0,0 +1,131 @@
//{{copyright}}
//{{version}}
//{{license}}
#pragma once
#include "../../comm/SerialBus.h"
#include "../criticalSection.h"
#include "../../dateTime/timer.h"
#include <STM32Headers.h>
#include <chrono>
#include <expected>
#include <span>
namespace sdi_uToolBox::stm32::comm
{
//--------------------------------------------------------------
class HIDBus : public sdi_uToolBox::comm::SerialBus
{
public:
using ReadBuf = std::span<uint8_t>;
using WriteBuf = std::span<const uint8_t>;
using Timeout = std::chrono::milliseconds;
using ReturnType = std::expected<size_t, ReturnStatus>;
public:
HIDBus(); // Default constructor
virtual ~HIDBus() = default; // Default destructor
HIDBus(const HIDBus &) = delete; // Copy constructor
HIDBus(HIDBus &&) = delete; // Move constructor
HIDBus &operator=(const HIDBus &) = delete; // Copy assignment operator
HIDBus &operator=(HIDBus &&) = delete; // Move assignment operator
virtual ReturnStatus configure(USBD_HandleTypeDef *handle); // Configure UART bus with HAL handle
ReturnStatus open() override; // Open UART bus
ReturnStatus close() override; // Close UART bus
ReturnType write(WriteBuf buf, Timeout timeout) const override; // Write data to UART device
ReturnType read(ReadBuf buf, Timeout timeout) const override; // Read data from UART device
virtual void rxCompleteCallback(std::span<const uint8_t> buf);
protected:
USBD_HandleTypeDef *m_hidHandle = nullptr; // HAL UART handle
sdi_uToolBox::dateTime::Timer m_timer;
sdi_toolBox::dateTime::ITimer* m_genericTimer = nullptr;
mutable std::span<const uint8_t> m_rxBuffer;
};
//--------------------------------------------------------------
inline HIDBus::HIDBus()
{
m_genericTimer = &m_timer;
}
//--------------------------------------------------------------
/* Configure UART bus with HAL handle */
inline ReturnStatus HIDBus::configure(USBD_HandleTypeDef *handle)
{
if (!handle)
return ReturnStatus::InvalidHandle;
m_hidHandle = handle;
return ReturnStatus::Ok;
}
//--------------------------------------------------------------
/* Open UART bus */
inline ReturnStatus HIDBus::open()
{
if (!m_hidHandle)
return ReturnStatus::InvalidHandle;
return ReturnStatus::Error;
}
//--------------------------------------------------------------
/* Close UART bus */
inline ReturnStatus HIDBus::close()
{
if (!m_hidHandle)
return ReturnStatus::InvalidHandle;
return ReturnStatus::Error;
}
//--------------------------------------------------------------
/* Write data to UART device */
inline HIDBus::ReturnType HIDBus::write(const WriteBuf buf, const Timeout timeout) const
{
const auto ret = USBD_CUSTOM_HID_SendReport(m_hidHandle,
const_cast<uint8_t *>(buf.data()),
buf.size());
if (ret == USBD_OK)
return buf.size();
if (ret == USBD_BUSY)
return std::unexpected(ReturnStatus::Busy);
return std::unexpected(ReturnStatus::Error);
}
//--------------------------------------------------------------
/* Read data from UART device */
inline HIDBus::ReturnType HIDBus::read(const ReadBuf buf, const Timeout timeout) const
{
m_genericTimer->reset();
while (!m_genericTimer->isElapsed(timeout))
{
CriticalSection criticalSection;
if (m_rxBuffer.empty())
continue;
if (m_rxBuffer.size() > buf.size())
return std::unexpected(ReturnStatus::BufferOverflow);
std::copy_n(m_rxBuffer.begin(), m_rxBuffer.size(), buf.begin());
m_rxBuffer = {};
return m_rxBuffer.size();
}
return std::unexpected(ReturnStatus::Timeout);
}
//--------------------------------------------------------------
inline void HIDBus::rxCompleteCallback(const std::span<const uint8_t> buf)
{
CriticalSection criticalSection;
m_rxBuffer = buf;
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::comm

View File

@@ -0,0 +1,134 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include "../../comm/I2CBus.h"
#include <STM32Headers.h>
#include <chrono>
#include <expected>
#include <span>
namespace sdi_uToolBox::stm32::comm
{
//--------------------------------------------------------------
class I2CBus : public sdi_uToolBox::comm::I2CBus
{
public:
using ReadBuf = std::span<uint8_t>;
using WriteBuf = std::span<const uint8_t>;
using Timeout = std::chrono::milliseconds;
using ReturnType = std::expected<size_t, ReturnStatus>;
public:
I2CBus() = default; // Default constructor
virtual ~I2CBus() = default; // Default destructor
I2CBus(const I2CBus &) = delete; // Copy constructor
I2CBus(I2CBus &&) = delete; // Move constructor
I2CBus &operator=(const I2CBus &) = delete; // Copy assignment operator
I2CBus &operator=(I2CBus &&) = delete; // Move assignment operator
ReturnStatus configure(I2C_HandleTypeDef *handle); // Configure I2C bus with HAL handle
ReturnStatus open() override; // Open I2C bus
ReturnStatus close() override; // Close I2C bus
ReturnType write(uint8_t address, WriteBuf buf, Timeout timeout) const override; // Write data to I2C device
ReturnType read(uint8_t address, ReadBuf buf, Timeout timeout) const override; // Read data from I2C device
ReturnStatus isDeviceReady(uint8_t address) const override; // Check if I2C device is ready
protected:
I2C_HandleTypeDef *m_i2cHandle = nullptr; // HAL I2C handle
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Configure I2C bus with HAL handle */
inline ReturnStatus I2CBus::configure(I2C_HandleTypeDef *handle)
{
if (!handle)
return ReturnStatus::InvalidHandle;
m_i2cHandle = handle;
return ReturnStatus::Ok;
}
//--------------------------------------------------------------
/* Open I2C bus */
inline ReturnStatus I2CBus::open()
{
return ReturnStatus::Error;
}
//--------------------------------------------------------------
/* Close I2C bus */
inline ReturnStatus I2CBus::close()
{
return ReturnStatus::Error;
}
//--------------------------------------------------------------
/* Write data to I2C device */
inline I2CBus::ReturnType I2CBus::write(const uint8_t address, const WriteBuf buf, const Timeout timeout) const
{
const auto ret = HAL_I2C_Master_Transmit(m_i2cHandle,
address << 1,
const_cast<uint8_t *>(buf.data()),
buf.size(),
timeout.count());
if (ret == HAL_OK)
return buf.size();
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
if (ret == HAL_ERROR && m_i2cHandle->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
return std::unexpected(ReturnStatus::Timeout);
return std::unexpected(ReturnStatus::Error);
}
//--------------------------------------------------------------
/* Read data from I2C device */
inline I2CBus::ReturnType I2CBus::read(const uint8_t address, const ReadBuf buf, const Timeout timeout) const
{
const auto ret = HAL_I2C_Master_Receive(m_i2cHandle,
address << 1,
buf.data(),
buf.size(),
timeout.count());
if (ret == HAL_OK)
return buf.size();
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
if (ret == HAL_ERROR && m_i2cHandle->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
return std::unexpected(ReturnStatus::Timeout);
return std::unexpected(ReturnStatus::Error);
}
//--------------------------------------------------------------
/* Check if I2C device is ready */
inline ReturnStatus I2CBus::isDeviceReady(const uint8_t address) const
{
const auto ret = HAL_I2C_IsDeviceReady(m_i2cHandle,
address << 1,
5,
100);
if (ret == HAL_OK)
return ReturnStatus::Ok;
if (ret == HAL_BUSY)
return ReturnStatus::Busy;
if (ret == HAL_ERROR && m_i2cHandle->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
return ReturnStatus::Timeout;
return ReturnStatus::Error;
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::comm

View File

@@ -0,0 +1,201 @@
//{{copyright}}
//{{version}}
//{{license}}
#pragma once
#include "../../comm/SerialBus.h"
#include <STM32Headers.h>
#include <chrono>
#include <expected>
#include <span>
namespace sdi_uToolBox::stm32::comm
{
//--------------------------------------------------------------
class UARTBus : public sdi_uToolBox::comm::SerialBus
{
public:
using ReadBuf = std::span<uint8_t>;
using WriteBuf = std::span<const uint8_t>;
using Timeout = std::chrono::milliseconds;
using ReturnType = std::expected<size_t, ReturnStatus>;
public:
UARTBus() = default; // Default constructor
virtual ~UARTBus() = default; // Default destructor
UARTBus(const UARTBus &) = delete; // Copy constructor
UARTBus(UARTBus &&) = delete; // Move constructor
UARTBus &operator=(const UARTBus &) = delete; // Copy assignment operator
UARTBus &operator=(UARTBus &&) = delete; // Move assignment operator
virtual ReturnStatus configure(UART_HandleTypeDef *handle); // Configure UART bus with HAL handle
ReturnStatus open() override; // Open UART bus
ReturnStatus close() override; // Close UART bus
ReturnType write(WriteBuf buf, Timeout timeout) const override; // Write data to UART device
ReturnType read(ReadBuf buf, Timeout timeout) const override; // Read data from UART device
protected:
UART_HandleTypeDef *m_uartHandle = nullptr; // HAL UART handle
};
//--------------------------------------------------------------
/* Configure UART bus with HAL handle */
inline ReturnStatus UARTBus::configure(UART_HandleTypeDef *handle)
{
if (!handle)
return ReturnStatus::InvalidHandle;
m_uartHandle = handle;
return ReturnStatus::Ok;
}
//--------------------------------------------------------------
/* Open UART bus */
inline ReturnStatus UARTBus::open()
{
if (!m_uartHandle)
return ReturnStatus::InvalidHandle;
return ReturnStatus::Error;
}
//--------------------------------------------------------------
/* Close UART bus */
inline ReturnStatus UARTBus::close()
{
if (!m_uartHandle)
return ReturnStatus::InvalidHandle;
// Abort ongoing operations
HAL_UART_AbortTransmit(m_uartHandle);
HAL_UART_AbortReceive(m_uartHandle);
return ReturnStatus::Error;
}
//--------------------------------------------------------------
/* Write data to UART device */
inline UARTBus::ReturnType UARTBus::write(const WriteBuf buf, const Timeout timeout) const
{
const auto ret = HAL_UART_Transmit(m_uartHandle,
const_cast<uint8_t *>(buf.data()),
buf.size(),
timeout.count());
if (ret == HAL_OK)
return buf.size();
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
if (ret == HAL_TIMEOUT)
return std::unexpected(ReturnStatus::Timeout);
return std::unexpected(ReturnStatus::Error);
}
//--------------------------------------------------------------
/* Read data from UART device */
inline UARTBus::ReturnType UARTBus::read(const ReadBuf buf, const Timeout timeout) const
{
const auto ret = HAL_UART_Receive(m_uartHandle,
buf.data(),
buf.size(),
timeout.count());
if (ret == HAL_OK)
return buf.size();
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
if (ret == HAL_TIMEOUT)
return std::unexpected(ReturnStatus::Timeout);
return std::unexpected(ReturnStatus::Error);
}
//--------------------------------------------------------------
/* --- */
//--------------------------------------------------------------
class UARTBusCallback
{
friend class UARTBusCallbackRegistry;
public:
UARTBusCallback() = default; // Default constructor
virtual ~UARTBusCallback() = default; // Default destructor
UARTBusCallback(const UARTBusCallback &) = default; // Copy constructor
UARTBusCallback &operator=(const UARTBusCallback &) = default; // Copy assignment operator
UARTBusCallback(UARTBusCallback &&) = default; // Move constructor
UARTBusCallback &operator=(UARTBusCallback &&) = default; // Move assignment operator
virtual void txCompleteCallback() const = 0;
virtual void rxCompleteCallback() const = 0;
virtual void rxEventCallback(uint16_t size) const = 0;
virtual void errorCallback() const = 0;
protected:
struct RegistryEntry
{
const UART_HandleTypeDef *handle = nullptr;
const UARTBusCallback *instance = nullptr;
};
};
//--------------------------------------------------------------
/* --- */
//--------------------------------------------------------------
class UARTBusCallbackRegistry
{
static constexpr size_t MAX_UART_INSTANCES = 4;
public:
static bool registerInstance(const UART_HandleTypeDef *handle, const UARTBusCallback *instance); // Register instance with UART handle
static void unregisterInstance(const UARTBusCallback *instance); // Unregister instance with UART handle
static const UARTBusCallback *getInstance(const UART_HandleTypeDef *handle); // Get instance from UART handle
protected:
static inline std::array<UARTBusCallback::RegistryEntry, MAX_UART_INSTANCES> s_registry;
};
//--------------------------------------------------------------
/* Register instance with UART handle */
inline bool UARTBusCallbackRegistry::registerInstance(const UART_HandleTypeDef *handle, const UARTBusCallback *instance)
{
for (auto &entry : s_registry)
{
if (!entry.handle)
{
entry.handle = handle;
entry.instance = instance;
return true; // Successfully registered
}
}
return false; // Registry full
}
//--------------------------------------------------------------
/* Unregister instance with UART handle */
inline void UARTBusCallbackRegistry::unregisterInstance(const UARTBusCallback *instance)
{
for (auto &entry : s_registry)
{
if (entry.instance == instance)
{
entry.handle = nullptr;
entry.instance = nullptr;
}
}
}
//--------------------------------------------------------------
/* Get instance from UART handle */
inline const UARTBusCallback *UARTBusCallbackRegistry::getInstance(const UART_HandleTypeDef *handle)
{
for (const auto &entry : s_registry)
{
if (entry.handle == handle)
return entry.instance;
}
return nullptr;
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::comm

View File

@@ -0,0 +1,198 @@
/* ---
* FONCTIONNEMENT PAS TRES CLAIR, A REVOIR
*/
//{{copyright}}
//{{version}}
//{{license}}
#pragma once
#include "UARTBus.h"
#include <atomic>
namespace sdi_uToolBox::stm32::comm
{
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
class UARTBusIT : public stm32::comm::UARTBus
{
public:
UARTBusIT() = default; // Default constructor
virtual ~UARTBusIT() override = default; // Default destructor
UARTBusIT(const UARTBusIT &) = delete; // Copy constructor
UARTBusIT(UARTBusIT &&) = delete; // Move constructor
UARTBusIT &operator=(const UARTBusIT &) = delete; // Copy assignment operator
UARTBusIT &operator=(UARTBusIT &&) = delete; // Move assignment operator
ReturnType write(WriteBuf buf, Timeout timeout) const override; // Write data to UART device
ReturnType read(ReadBuf buf, Timeout timeout) const override; // Read data from UART device
// Callback to be called from ISR when TX, RX is complete or error occurs
void txCompleteCallback() const; // TX complete callback
void rxCompleteCallback() const; // RX complete callback
void errorCallback() const; // Error callback
protected:
mutable std::array<uint8_t, TX_BUFFER_SIZE> m_txBuffer;
mutable std::array<uint8_t, RX_BUFFER_SIZE> m_rxBuffer;
mutable std::atomic<bool> m_txComplete{ false };
mutable std::atomic<bool> m_rxComplete{ false };
mutable std::atomic<bool> m_txError{ false };
mutable std::atomic<bool> m_rxError{ false };
mutable std::atomic<size_t> m_lastTxSize{ 0 };
mutable std::atomic<size_t> m_lastRxSize{ 0 };
static bool waitForFlag(const std::atomic<bool> &flag, Timeout timeout);
};
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBus::ReturnType UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::write(const WriteBuf buf, const Timeout timeout) const
{
if (!m_uartHandle)
return std::unexpected(ReturnStatus::InvalidHandle);
if (buf.size() > TX_BUFFER_SIZE)
return std::unexpected(ReturnStatus::Error);
// Copy data to internal buffer
std::copy(buf.begin(), buf.end(), m_txBuffer.begin());
// Reset flags
m_txComplete.store(false, std::memory_order_release);
m_txError.store(false, std::memory_order_release);
m_lastTxSize.store(0, std::memory_order_release);
// Start transmission with interrupt
const auto ret = HAL_UART_Transmit_IT(m_uartHandle,
const_cast<uint8_t *>(m_txBuffer.data()),
buf.size());
if (ret != HAL_OK)
{
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
return std::unexpected(ReturnStatus::Error);
}
// Wait for transmission to complete
if (!waitForFlag(m_txComplete, timeout))
{
HAL_UART_AbortTransmit(m_uartHandle);
return std::unexpected(ReturnStatus::Timeout);
}
// Check for errors
if (m_txError.load(std::memory_order_acquire))
return std::unexpected(ReturnStatus::Error);
return m_lastTxSize.load(std::memory_order_acquire);
}
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline UARTBus::ReturnType UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::read(ReadBuf buf, const Timeout timeout) const
{
if (!m_uartHandle)
return std::unexpected(ReturnStatus::InvalidHandle);
if (buf.size() > RX_BUFFER_SIZE)
return std::unexpected(ReturnStatus::Error);
// Reset flags
m_rxComplete.store(false, std::memory_order_release);
m_rxError.store(false, std::memory_order_release);
m_lastRxSize.store(0, std::memory_order_release);
// Start reception with interrupt
const auto ret = HAL_UART_Receive_IT(m_uartHandle,
m_rxBuffer.data(),
buf.size());
if (ret != HAL_OK)
{
if (ret == HAL_BUSY)
return std::unexpected(ReturnStatus::Busy);
return std::unexpected(ReturnStatus::Error);
}
// Wait for reception to complete
if (!waitForFlag(m_rxComplete, timeout))
{
HAL_UART_AbortReceive(m_uartHandle);
return std::unexpected(ReturnStatus::Timeout);
}
// Check for errors
if (m_rxError.load(std::memory_order_acquire))
return std::unexpected(ReturnStatus::Error);
// Copy data from internal buffer to output buffer
const size_t receivedSize = m_lastRxSize.load(std::memory_order_acquire);
std::copy_n(m_rxBuffer.begin(), receivedSize, buf.begin());
return receivedSize;
}
//--------------------------------------------------------------
/* TX complete callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::txCompleteCallback() const
{
m_lastTxSize.store(m_uartHandle->TxXferSize - m_uartHandle->TxXferCount,
std::memory_order_release);
m_txComplete.store(true, std::memory_order_release);
}
//--------------------------------------------------------------
/* RX complete callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::rxCompleteCallback() const
{
m_lastRxSize.store(m_uartHandle->RxXferSize - m_uartHandle->RxXferCount,
std::memory_order_release);
m_rxComplete.store(true, std::memory_order_release);
}
//--------------------------------------------------------------
/* Error callback */
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline void UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::errorCallback() const
{
if (m_uartHandle->gState == HAL_UART_STATE_BUSY_TX ||
m_uartHandle->gState == HAL_UART_STATE_BUSY_TX_RX)
{
m_txError.store(true, std::memory_order_release);
m_txComplete.store(true, std::memory_order_release);
}
if (m_uartHandle->RxState == HAL_UART_STATE_BUSY_RX ||
m_uartHandle->gState == HAL_UART_STATE_BUSY_TX_RX)
{
m_rxError.store(true, std::memory_order_release);
m_rxComplete.store(true, std::memory_order_release);
}
}
//--------------------------------------------------------------
template<size_t TX_BUFFER_SIZE, size_t RX_BUFFER_SIZE>
inline bool UARTBusIT<TX_BUFFER_SIZE, RX_BUFFER_SIZE>::waitForFlag(const std::atomic<bool> &flag, Timeout timeout)
{
const auto startTime = std::chrono::steady_clock::now();
while (!flag.load(std::memory_order_acquire))
{
const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - startTime);
if (elapsed >= timeout)
return false;
// Small delay to avoid busy waiting consuming too much CPU
for (volatile int i = 0; i < 1000; ++i)
;
}
return true;
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32::comm

View File

@@ -0,0 +1,52 @@
/*
{{copyright}}
*/
/*
{{version}}
*/
/*
{{license}}
*/
#pragma once
#include <array>
#include <cstdint>
#include <optional>
namespace sdi_uToolBox::stm32
{
//--------------------------------------------------------------
class CriticalSection final
{
public:
CriticalSection(); // Constructor
~CriticalSection(); // Destructor
CriticalSection(const CriticalSection &other) = delete; // Copy constructor
CriticalSection(CriticalSection &&other) noexcept = delete; // Move constructor
CriticalSection &operator=(const CriticalSection &other) = delete; // Copy assignment
CriticalSection &operator=(CriticalSection &&other) noexcept = delete; // Move assignment
protected:
uint32_t m_primask;
};
//--------------------------------------------------------------
//--------------------------------------------------------------
/* Constructor */
inline CriticalSection::CriticalSection()
{
m_primask = __get_PRIMASK();
__disable_irq();
}
//--------------------------------------------------------------
/* Destructor */
inline CriticalSection::~CriticalSection()
{
if ((m_primask & 0x01) == 0) // Only if interrupts were enabled
__enable_irq();
}
//--------------------------------------------------------------
} // namespace sdi_uToolBox::stm32

View File

@@ -0,0 +1,20 @@
#pragma once
#include <main.h>
//--------------------------------------------------------------
// Set a real type for FPU usage if available
#if defined(__FPU_PRESENT) && (__FPU_PRESENT == 1U)
# if defined(__FPU_DP) && (__FPU_DP == 1U)
// FPU with double precision support
using Real = double;
# else
// FPU with single precision only
using Real = float;
# endif
#else
// No FPU present. Floating point will be emulated
// (It is better to use a smaller type to be cheaper)
using Real = float;
#endif
//--------------------------------------------------------------