adding freeRtos API from different project
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "src"
|
||||
INCLUDE_DIRS "include"
|
||||
)
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @file fullDemo.cpp
|
||||
* @author Gustice
|
||||
* @brief Coherent demonstration of FreeRTOS-Abstraction in small environment
|
||||
* @copyright Copyright (c) 2024
|
||||
*/
|
||||
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <stdio.h>
|
||||
|
||||
constexpr int StackSize = 1024;
|
||||
constexpr int LED = 2;
|
||||
|
||||
#define TerminalPrint(format, ...) ESP_LOGI("main", format, __VA_ARGS__)
|
||||
|
||||
#include "Mutex.hpp"
|
||||
#include "Queue.hpp"
|
||||
#include "Semaphore.hpp"
|
||||
#include "Task.hpp"
|
||||
#include <string>
|
||||
|
||||
using namespace fos;
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
constexpr int WAIT = 1000;
|
||||
|
||||
/// @brief Arbitrary definition for message type (to be used as queue template parameter)
|
||||
struct TxMsg {
|
||||
TxMsg(uint32_t c, std::string m) : cnt(c), message(m){};
|
||||
uint32_t cnt;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
/// @brief Global semaphore for blink tasks
|
||||
Semaphore blinkSem;
|
||||
|
||||
void setupLedPin();
|
||||
void toggleLedPin();
|
||||
|
||||
/// @brief Parameterless task entry for generation of blink signals
|
||||
/// @note uses global semaphore
|
||||
void TaskBlinkProvider() {
|
||||
TerminalPrint("starting blink provider task");
|
||||
while (true) {
|
||||
Task::delayMs(WAIT); // wait
|
||||
blinkSem.give(); // then signal a blink event
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Parameterless task entry for execution of blink signals
|
||||
/// @note uses global semaphore
|
||||
void TaskBlink() {
|
||||
TerminalPrint("starting blink execution task");
|
||||
setupLedPin();
|
||||
while (true) {
|
||||
blinkSem.take(); // wait for blink event signal
|
||||
toggleLedPin(); // execute blink event
|
||||
Task::delayMs(100); // wait
|
||||
toggleLedPin(); // execute blink event
|
||||
TerminalPrint("Blink tick");
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Parametrized task entry for message provider (generates message object)
|
||||
/// @param queue reference to appropriate queue
|
||||
void MessageProvider(Queue<TxMsg> &queue) {
|
||||
uint32_t cnt{};
|
||||
while (true) {
|
||||
auto e = std::make_unique<TxMsg>(cnt++, "tick");
|
||||
queue.enqueue(std::move(e));
|
||||
Task::delayMs(WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Parametrized task entry for message consumer (sends message over UART)
|
||||
/// @param queue reference to appropriate queue
|
||||
void MessageConsumer(Queue<TxMsg> &queue) {
|
||||
while (true) {
|
||||
auto msg = queue.dequeue(); // Wait with infinit timeout, guaranteed to succeed
|
||||
TerminalPrint("Message: %s %lu", msg->message.c_str(), msg->cnt);
|
||||
}
|
||||
}
|
||||
|
||||
void setupTasks() {
|
||||
/// @note: All definitions as static instances to not be destructed after loosing scope
|
||||
static Queue<TxMsg> txQueue(8);
|
||||
static Task blinkerTask(TaskBlink, "BlinkExec", StackSize);
|
||||
static Task blinkeProviderTask(TaskBlinkProvider, "BlinkFlags", StackSize);
|
||||
|
||||
static TaskT<Queue<TxMsg> &> tickerTask(MessageProvider, txQueue, "Tick", StackSize);
|
||||
static TaskT<Queue<TxMsg> &> senderTask(MessageConsumer, txQueue, "Sender", StackSize);
|
||||
}
|
||||
|
||||
|
||||
extern "C" { // This switch allows the ROS C-implementation to find this main
|
||||
void app_main(void);
|
||||
}
|
||||
|
||||
void app_main() {
|
||||
void setupTasks();
|
||||
|
||||
while (true) {
|
||||
ESP_LOGI("main", "healt tick");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void setupLedPin() {
|
||||
// no Buildin-led on XIAO C3
|
||||
}
|
||||
void toggleLedPin() {
|
||||
// no Buildin-led on XIAO C3
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @file mutex.hpp
|
||||
* @author Gustice
|
||||
* @brief Mutex Wrapper
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
namespace fos {
|
||||
|
||||
/**
|
||||
* @brief Mutex
|
||||
* @details FreeRTOS implementation uses already priority inheritance
|
||||
* and are recursively callable
|
||||
*/
|
||||
class Mutex {
|
||||
private:
|
||||
QueueHandle_t _handle;
|
||||
|
||||
public:
|
||||
/// @brief Tick type (redefinition for convenience)
|
||||
using Tick = TickType_t;
|
||||
/// @brief Definition for endless waittime
|
||||
static constexpr Tick MaxDelay = portMAX_DELAY;
|
||||
|
||||
/// @brief Constructor
|
||||
Mutex() {
|
||||
_handle = xSemaphoreCreateMutex();
|
||||
assert(_handle != 0 && "Semaphore create must finish successfully");
|
||||
}
|
||||
|
||||
~Mutex() {
|
||||
vSemaphoreDelete(_handle);
|
||||
};
|
||||
|
||||
/// @brief Relase mutex
|
||||
void release() {
|
||||
auto ret = xSemaphoreGive(_handle);
|
||||
assert(ret == pdPASS && "Underlying semaphore queue must be initialized correcty");
|
||||
}
|
||||
|
||||
/// @brief Take mutex
|
||||
/// @param timeout Timeout to wait for signal (default = wait forever)
|
||||
/// @return true if successfully claimed (not timeout)
|
||||
bool claim(Tick timeout = MaxDelay) {
|
||||
auto ret = xSemaphoreTake(_handle, timeout);
|
||||
return ret == pdPASS;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mutex Guard. Self releasing mutex for atomic operation
|
||||
*/
|
||||
class MutexGuard {
|
||||
private:
|
||||
Mutex &_mutex;
|
||||
bool _claimed;
|
||||
|
||||
public:
|
||||
/// @brief Constructor
|
||||
/// @param mtx Mutex to claim
|
||||
/// @param timeout Timeout to wait for signal (default = wait forever)
|
||||
MutexGuard(Mutex &mtx, Mutex::Tick timeout = Mutex::MaxDelay) : _mutex(mtx) {
|
||||
_claimed = _mutex.claim(timeout);
|
||||
};
|
||||
|
||||
~MutexGuard() {
|
||||
if (!_claimed) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mutex.release();
|
||||
}
|
||||
|
||||
/// @brief Check if mutex was claimed successfully
|
||||
/// @return true if claimed
|
||||
bool isActive() {
|
||||
return _claimed;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fos
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @file queue.hpp
|
||||
* @author Gustice
|
||||
* @brief Queue Wrapper
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace fos {
|
||||
|
||||
/**
|
||||
* @brief Message Queue
|
||||
*
|
||||
* @tparam T Type for message payload
|
||||
*/
|
||||
template <typename T>
|
||||
class Queue {
|
||||
private:
|
||||
bool _isActive;
|
||||
QueueHandle_t xQueue;
|
||||
|
||||
public:
|
||||
/// @brief Tick type (redefinition for convenience)
|
||||
using Tick = TickType_t;
|
||||
/// @brief Definition for endless waittime
|
||||
static constexpr Tick MaxDelay = portMAX_DELAY;
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param size number of slots in queue
|
||||
Queue(size_t size) {
|
||||
xQueue = xQueueCreate(size, sizeof(T *));
|
||||
assert(xQueue != 0 && "Queue create must finish successfully");
|
||||
_isActive = true;
|
||||
}
|
||||
|
||||
~Queue() {
|
||||
vQueueDelete(xQueue);
|
||||
}
|
||||
|
||||
/// @brief Push new message in queue
|
||||
/// @param message payload
|
||||
/// @return nullptr if successful else give back pointer to queued element if pushing failed
|
||||
std::unique_ptr<T> enqueue(std::unique_ptr<T> message) {
|
||||
if (uxQueueSpacesAvailable(xQueue) == 0) {
|
||||
return message;
|
||||
}
|
||||
T *o = message.release();
|
||||
auto ret = xQueueSend(xQueue, &o, (TickType_t)0);
|
||||
assert(ret == pdPASS && "Underlying semaphore queue must be initialized correcty");
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/// @brief Pop message from queue
|
||||
/// @param timeout timeout to wait for message (default: wait for ever)
|
||||
/// @return pointer to dequeued element
|
||||
std::unique_ptr<T> dequeue(Tick timeout = MaxDelay) {
|
||||
T *pMsg = nullptr;
|
||||
if (!xQueueReceive(xQueue, &pMsg, timeout)) {
|
||||
return {nullptr};
|
||||
}
|
||||
return std::unique_ptr<T>(pMsg);
|
||||
}
|
||||
|
||||
/// @brief Pop latest element from queue and ignore all older elements
|
||||
/// @param timeout timeout to wait for message (default: wait for ever)
|
||||
/// @return pointer to dequeued element
|
||||
std::unique_ptr<T> dequeueLatest(Tick timeout = MaxDelay) {
|
||||
T *pMsg = nullptr;
|
||||
if (!xQueueReceive(xQueue, &pMsg, timeout)) {
|
||||
return {nullptr};
|
||||
}
|
||||
|
||||
while (true == xQueueReceive(xQueue, &pMsg, (TickType_t)0)) {
|
||||
}
|
||||
return std::unique_ptr<T>(pMsg);
|
||||
}
|
||||
|
||||
/// @brief terminate message queue
|
||||
void signalClose() {
|
||||
_isActive = false;
|
||||
}
|
||||
|
||||
/// @brief Check if message queue is active
|
||||
/// @return true if active
|
||||
bool isActive() {
|
||||
return _isActive;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fos
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @file semaphore.hpp
|
||||
* @author Gustice
|
||||
* @brief Semaphore Wrapper
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
namespace fos {
|
||||
|
||||
/**
|
||||
* @brief Semaphore
|
||||
*/
|
||||
class Semaphore {
|
||||
private:
|
||||
enum Type {
|
||||
Binary, ///< Binary semaphore, can only be true or false, no matter how often given
|
||||
Counting ///< Counting semaphore, keeps track of number of give and takes
|
||||
};
|
||||
|
||||
const Type SemType;
|
||||
QueueHandle_t _handle;
|
||||
|
||||
public:
|
||||
/// @brief Tick type (redefinition for convenience)
|
||||
using Tick = TickType_t;
|
||||
|
||||
/// @brief Definition for endless waittime
|
||||
static constexpr Tick MaxDelay = portMAX_DELAY;
|
||||
|
||||
/// @brief Constructor for binary semaphore
|
||||
Semaphore() : SemType(Semaphore::Binary) {
|
||||
_handle = xSemaphoreCreateBinary();
|
||||
assert(_handle != 0 && "Semaphore create must finish successfully");
|
||||
}
|
||||
|
||||
/// @brief Constructor for conting semaphore
|
||||
/// @param maxCount Counting limit
|
||||
/// @param init Initial count
|
||||
Semaphore(uint32_t maxCount, uint32_t init = 0) : SemType(Semaphore::Counting) {
|
||||
_handle = xSemaphoreCreateCounting(maxCount, init);
|
||||
assert(_handle != 0 && "Semaphore create must finish successfully");
|
||||
}
|
||||
|
||||
~Semaphore() {
|
||||
vSemaphoreDelete(_handle);
|
||||
};
|
||||
|
||||
/// @brief Give signal
|
||||
/// @note Binary semaphore can only be effectively given once
|
||||
void give() {
|
||||
// checking for error would lead to false positives in case of multiple gives ...
|
||||
xSemaphoreGive(_handle);
|
||||
}
|
||||
|
||||
/// @brief Consume signal
|
||||
/// @param timeout Timeout to wait for signal (default = wait forever)
|
||||
/// @return true if successfully consumed (not timeout)
|
||||
bool take(Tick timeout = MaxDelay) {
|
||||
auto ret = xSemaphoreTake(_handle, timeout);
|
||||
return ret == pdPASS;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fos
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* @file task.hpp
|
||||
* @author Gustice
|
||||
* @brief Task Wrapper
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <cassert>
|
||||
#include <string_view>
|
||||
#include <Semaphore.hpp>
|
||||
#define DEFAULT_STACKSIZE 0x1000uL
|
||||
|
||||
namespace fos {
|
||||
/**
|
||||
* @brief Base class for tasks
|
||||
*/
|
||||
class TaskBase {
|
||||
protected:
|
||||
TaskHandle_t _handle;
|
||||
|
||||
static void disposeFrameConditionally() {
|
||||
if constexpr (INCLUDE_vTaskDelete != 0) {
|
||||
vTaskDelete(NULL);
|
||||
// NOTE: This only compiles if symbol INCLUDE_vTaskDelete is defined
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore _finished;
|
||||
|
||||
public:
|
||||
/// @brief Priority type (redefinition for convenience)
|
||||
using Priority = unsigned short;
|
||||
/// @brief Tick type (redefinition for convenience)
|
||||
using Tick = TickType_t;
|
||||
/// @brief Definition for endless waittime
|
||||
static constexpr Tick MaxDelay = portMAX_DELAY;
|
||||
|
||||
/// @brief Maximum delay
|
||||
static constexpr int MaximumDelay = portMAX_DELAY;
|
||||
|
||||
/// @brief Default Stack size
|
||||
static constexpr size_t DefaultStackSize = DEFAULT_STACKSIZE;
|
||||
|
||||
/// @brief Lowest priority for new tasks
|
||||
static constexpr Priority MIN_PRIORITY = tskIDLE_PRIORITY + 1;
|
||||
|
||||
/// @brief Highest priority for new tasks
|
||||
static constexpr Priority MAX_PRIORITY = configMAX_PRIORITIES;
|
||||
|
||||
/// @brief Kill a task
|
||||
/// @note It can lead to undefined behaviour when one task deletes another task on a
|
||||
/// multi-core system. Hence a task should clear itself.
|
||||
void kill() { // NOTE: this leads to undefined behavior if interferes with Destructor
|
||||
vTaskDelete(_handle);
|
||||
}
|
||||
|
||||
/// @brief Join threads
|
||||
/// @param timeout timeout to wait for join else kill
|
||||
/// @return joined successfully
|
||||
bool join(Tick timeout = MaxDelay) {
|
||||
auto success = _finished.take(timeout);
|
||||
if(not success) {
|
||||
kill();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/// @brief Get task state
|
||||
/// @return FreeRTOS Task state
|
||||
eTaskState getState() {
|
||||
return eTaskGetState(_handle);
|
||||
}
|
||||
|
||||
/// @brief Get task handle
|
||||
/// @return FreeRTOS Task handle
|
||||
TaskHandle_t getHandle() {
|
||||
return _handle;
|
||||
}
|
||||
|
||||
/// @brief Delay function
|
||||
/// @param msToDelay Milliseconds to delay
|
||||
/// @note The minimal resolution for the delay is tick based
|
||||
static void delayMs(unsigned int msToDelay) {
|
||||
// pdMS_TO_TICKS(_timeout)
|
||||
vTaskDelay(msToDelay / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
/// @brief Delay function
|
||||
/// @param ticksToDelay Ticks to delay
|
||||
static void delayTicks(unsigned int ticksToDelay) {
|
||||
vTaskDelay(ticksToDelay);
|
||||
}
|
||||
|
||||
virtual ~TaskBase() {
|
||||
kill();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Simple task without task parameter
|
||||
*/
|
||||
class Task : public TaskBase {
|
||||
private:
|
||||
/// @brief Signature for task-implementation-callback
|
||||
using TaskSignature = void (*)();
|
||||
|
||||
static void TaskFrame(void *_this) {
|
||||
auto &task = *reinterpret_cast<Task *>(_this);
|
||||
task._call();
|
||||
// Tasks shall not return, however if this happens dispose task
|
||||
disposeFrameConditionally();
|
||||
task._finished.give();
|
||||
}
|
||||
|
||||
TaskSignature _call;
|
||||
|
||||
public:
|
||||
/// @brief Constructor for task without passed argument
|
||||
/// @param call Function pointer to implementation
|
||||
/// @param name Name of task
|
||||
/// @param stack Stacksize if not default
|
||||
/// @param prio Priority if not default
|
||||
Task(TaskSignature call, const char *name, size_t stack = DefaultStackSize,
|
||||
Priority prio = MIN_PRIORITY)
|
||||
: _call{call} {
|
||||
auto ret = xTaskCreate(TaskFrame, name, stack, this, MIN_PRIORITY, &_handle);
|
||||
assert(pdPASS == ret && "Task create must finish successfully");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Task with task parameter
|
||||
* @tparam T Type of task parameter on call
|
||||
*/
|
||||
template <typename T>
|
||||
class TaskT : public TaskBase {
|
||||
private:
|
||||
/// @brief Signature for task-implementation-callback
|
||||
using TaskSignature = void (*)(T);
|
||||
|
||||
template <typename TS>
|
||||
struct Param {
|
||||
TaskSignature call;
|
||||
const TS &arg;
|
||||
};
|
||||
Param<T> _param;
|
||||
|
||||
static void TaskFrame(void *_this) {
|
||||
auto &task = *reinterpret_cast<TaskT *>(_this);
|
||||
auto ¶m = task._param;
|
||||
param.call(param.arg);
|
||||
// Tasks shall not return, however if this happens dispose task
|
||||
disposeFrameConditionally();
|
||||
task._finished.give();
|
||||
}
|
||||
|
||||
public:
|
||||
/// @brief Constructor for task with passed argument
|
||||
/// @param call Function pointer to implementation
|
||||
/// @param param Parameter ot pass task implementation
|
||||
/// @param name Name of task
|
||||
/// @param stack Stacksize if not default
|
||||
/// @param prio Priority if not default
|
||||
TaskT(TaskSignature call, const T ¶m, const char *name, size_t stack = DefaultStackSize,
|
||||
Priority prio = MIN_PRIORITY)
|
||||
: _param{call, param} {
|
||||
auto ret = xTaskCreate(TaskFrame, name, stack, this, MIN_PRIORITY, &_handle);
|
||||
assert(pdPASS == ret && "Task create must finish successfully");
|
||||
}
|
||||
};
|
||||
|
||||
class Timing {
|
||||
public:
|
||||
static uint32_t getTickCount() {
|
||||
return xTaskGetTickCount();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fos
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @file freeRtosApi.cpp
|
||||
* @author Gustice
|
||||
* @brief FreeRtos wrapper
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*/
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES cmock freeRtosApi esp_timer
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <stdio.h>
|
||||
constexpr int StackSize = 1024;
|
||||
|
||||
#include <unity.h>
|
||||
|
||||
class TimeTest {
|
||||
private:
|
||||
bool running{};
|
||||
TickType_t _begin;
|
||||
|
||||
public:
|
||||
TimeTest() {
|
||||
start();
|
||||
}
|
||||
|
||||
void start() {
|
||||
_begin = xTaskGetTickCount();
|
||||
running = true;
|
||||
}
|
||||
|
||||
|
||||
TickType_t getRunTime() {
|
||||
return xTaskGetTickCount() - _begin;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "test.hpp"
|
||||
|
||||
#include <Mutex.hpp>
|
||||
#include <Semaphore.hpp>
|
||||
#include <Task.hpp>
|
||||
|
||||
using namespace fos;
|
||||
|
||||
constexpr int SHORT_DELAY = 10;
|
||||
|
||||
TEST_CASE("Test mutexes", "[freeRtos][mutex]") {
|
||||
Mutex eut;
|
||||
// first take fails
|
||||
TEST_ASSERT_TRUE(eut.claim(0));
|
||||
eut.release();
|
||||
|
||||
TEST_ASSERT_TRUE(eut.claim(0));
|
||||
TEST_ASSERT_FALSE(eut.claim(0));
|
||||
|
||||
eut.release();
|
||||
// eut.release(); // not a good idea
|
||||
TEST_ASSERT_TRUE(eut.claim(0));
|
||||
// second take after give fails
|
||||
TEST_ASSERT_FALSE(eut.claim(0));
|
||||
}
|
||||
|
||||
static Semaphore starter1;
|
||||
static void testClaimer(Mutex &m) {
|
||||
m.claim();
|
||||
starter1.give();
|
||||
vTaskDelay(SHORT_DELAY);
|
||||
m.release();
|
||||
vTaskDelay(SHORT_DELAY);
|
||||
m.claim();
|
||||
starter1.give();
|
||||
while (true) {
|
||||
vTaskDelay(SHORT_DELAY);
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Test mutex timing", "[freeRtos][mutex]") {
|
||||
static Mutex eut;
|
||||
|
||||
TaskT<Mutex &> claimer(testClaimer, eut, "signal1");
|
||||
starter1.take(100);
|
||||
{ // All ok
|
||||
TimeTest timer;
|
||||
TEST_ASSERT_TRUE(eut.claim(100));
|
||||
TEST_ASSERT_INT_WITHIN(1, SHORT_DELAY, timer.getRunTime());
|
||||
eut.release();
|
||||
}
|
||||
starter1.take(100);
|
||||
{ // Timeout
|
||||
TimeTest timer;
|
||||
TEST_ASSERT_FALSE(eut.claim(20));
|
||||
TEST_ASSERT_INT_WITHIN(1, 20, timer.getRunTime());
|
||||
}
|
||||
|
||||
{ // NoWeit timeout
|
||||
Mutex passiveSem;
|
||||
passiveSem.claim(0);
|
||||
TimeTest timer;
|
||||
TEST_ASSERT_FALSE(passiveSem.claim(0));
|
||||
TEST_ASSERT_INT_WITHIN(1, 0, timer.getRunTime());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test mutex guard", "[freeRtos][mutex]") {
|
||||
Mutex mtx;
|
||||
{
|
||||
MutexGuard eut(mtx, 100);
|
||||
TEST_ASSERT_TRUE(eut.isActive());
|
||||
// already taken
|
||||
TEST_ASSERT_FALSE(mtx.claim(0));
|
||||
}
|
||||
// now free to go
|
||||
TEST_ASSERT_TRUE(mtx.claim(0));
|
||||
{ // Timeout
|
||||
TimeTest timer;
|
||||
MutexGuard eut(mtx, SHORT_DELAY);
|
||||
TEST_ASSERT_FALSE(eut.isActive());
|
||||
TEST_ASSERT_INT_WITHIN(1, SHORT_DELAY, timer.getRunTime());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
#include "test.hpp"
|
||||
|
||||
#include <Queue.hpp>
|
||||
#include <Task.hpp>
|
||||
#include <string>
|
||||
|
||||
using namespace fos;
|
||||
|
||||
constexpr int SHORT_DELAY = 10;
|
||||
|
||||
struct MessageFrame {
|
||||
MessageFrame(int v, std::string msg) : value(v), message(msg) {}
|
||||
int value;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
TEST_CASE("Test enqueueing then dequeueing", "[freeRtos][queue]") {
|
||||
Queue<MessageFrame> eut(10);
|
||||
|
||||
auto e = std::make_unique<MessageFrame>(1, "one");
|
||||
eut.enqueue(std::move(e));
|
||||
|
||||
auto r = eut.dequeue(100);
|
||||
TEST_ASSERT_NOT_NULL(r.get());
|
||||
TEST_ASSERT_EQUAL(1, r->value);
|
||||
TEST_ASSERT_TRUE(r->message == "one");
|
||||
}
|
||||
|
||||
TEST_CASE("Test enqueueing then dequeue multiple", "[freeRtos][queue]") {
|
||||
Queue<MessageFrame> eut(10);
|
||||
|
||||
auto e = std::make_unique<MessageFrame>(1, "one");
|
||||
eut.enqueue(std::move(e));
|
||||
e = std::make_unique<MessageFrame>(2, "two");
|
||||
eut.enqueue(std::move(e));
|
||||
e = std::make_unique<MessageFrame>(3, "three");
|
||||
eut.enqueue(std::move(e));
|
||||
|
||||
auto r = eut.dequeue(100);
|
||||
TEST_ASSERT_NOT_NULL(r.get());
|
||||
TEST_ASSERT_EQUAL(1, r->value);
|
||||
TEST_ASSERT_TRUE(r->message == "one");
|
||||
r = eut.dequeue(100);
|
||||
TEST_ASSERT_NOT_NULL(r.get());
|
||||
TEST_ASSERT_EQUAL(2, r->value);
|
||||
TEST_ASSERT_TRUE(r->message == "two");
|
||||
r = eut.dequeue(100);
|
||||
TEST_ASSERT_NOT_NULL(r.get());
|
||||
TEST_ASSERT_EQUAL(3, r->value);
|
||||
TEST_ASSERT_TRUE(r->message == "three");
|
||||
}
|
||||
|
||||
TEST_CASE("Test enqueue cyclically", "[freeRtos][queue]") {
|
||||
constexpr int Size = 10;
|
||||
Queue<MessageFrame> eut(Size);
|
||||
for (size_t i = 0; i < Size * 2; i++) {
|
||||
auto e = std::make_unique<MessageFrame>(i, "Some message");
|
||||
eut.enqueue(std::move(e));
|
||||
|
||||
auto r = eut.dequeue(100);
|
||||
TEST_ASSERT_NOT_NULL(r.get());
|
||||
TEST_ASSERT_EQUAL(i, r->value);
|
||||
}
|
||||
TEST_ASSERT_TRUE(true);
|
||||
}
|
||||
|
||||
TEST_CASE("Test enqueueing till overflow", "[freeRtos][queue]") {
|
||||
Queue<MessageFrame> eut(3);
|
||||
|
||||
auto e = std::make_unique<MessageFrame>(1, "one");
|
||||
auto r = eut.enqueue(std::move(e));
|
||||
TEST_ASSERT_EQUAL(nullptr, r.get());
|
||||
|
||||
e = std::make_unique<MessageFrame>(2, "two");
|
||||
r = eut.enqueue(std::move(e));
|
||||
TEST_ASSERT_EQUAL(nullptr, r.get());
|
||||
|
||||
e = std::make_unique<MessageFrame>(3, "three");
|
||||
r = eut.enqueue(std::move(e));
|
||||
TEST_ASSERT_EQUAL(nullptr, r.get());
|
||||
|
||||
e = std::make_unique<MessageFrame>(4, "four");
|
||||
auto pE = e.get();
|
||||
r = eut.enqueue(std::move(e));
|
||||
TEST_ASSERT_NOT_EQUAL(nullptr, r.get());
|
||||
// also return old element ...
|
||||
TEST_ASSERT_EQUAL(pE, r.get());
|
||||
}
|
||||
|
||||
TEST_CASE("Test enqueueing then dequeue multiple", "[freeRtos][queue]") {
|
||||
Queue<MessageFrame> eut(10);
|
||||
|
||||
auto e = std::make_unique<MessageFrame>(1, "one");
|
||||
eut.enqueue(std::move(e));
|
||||
e = std::make_unique<MessageFrame>(2, "two");
|
||||
eut.enqueue(std::move(e));
|
||||
|
||||
auto r = eut.dequeue(100);
|
||||
TEST_ASSERT_NOT_NULL(r.get());
|
||||
TEST_ASSERT_EQUAL(1, r->value);
|
||||
r = eut.dequeue(100);
|
||||
TEST_ASSERT_NOT_NULL(r.get());
|
||||
TEST_ASSERT_EQUAL(2, r->value);
|
||||
r = eut.dequeue(0); // no waiting
|
||||
TEST_ASSERT_EQUAL(nullptr, r.get());
|
||||
}
|
||||
|
||||
static void queue1Gen(Queue<MessageFrame> &q) {
|
||||
vTaskDelay(SHORT_DELAY);
|
||||
auto e = std::make_unique<MessageFrame>(0, "msg");
|
||||
q.enqueue(std::move(e));
|
||||
while (true) {
|
||||
vTaskDelay(SHORT_DELAY);
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Test timing of queue", "[freeRtos][queue]") {
|
||||
static Queue<MessageFrame> eut(8);
|
||||
TaskT<Queue<MessageFrame> &> signaler(queue1Gen, eut, "queue1Gen");
|
||||
{ // All ok
|
||||
TimeTest timer;
|
||||
auto r = eut.dequeue(100);
|
||||
TEST_ASSERT_NOT_NULL(r.get());
|
||||
TEST_ASSERT_INT_WITHIN(1, SHORT_DELAY, timer.getRunTime());
|
||||
}
|
||||
|
||||
{ // Timeout
|
||||
TimeTest timer;
|
||||
auto r = eut.dequeue(20);
|
||||
TEST_ASSERT_NULL(r.get());
|
||||
TEST_ASSERT_INT_WITHIN(1, 20, timer.getRunTime());
|
||||
}
|
||||
}
|
||||
|
||||
static void senderTask(Queue<MessageFrame> &q) {
|
||||
size_t i = 0;
|
||||
while (q.isActive()) {
|
||||
auto e = std::make_unique<MessageFrame>(i++, "Some message");
|
||||
q.enqueue(std::move(e));
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test enqueueing series of message", "[freeRtos][queue]") {
|
||||
constexpr int Size = 10;
|
||||
Queue<MessageFrame> eut(Size);
|
||||
TaskT<Queue<MessageFrame> &> sender(senderTask, eut, "sender");
|
||||
for (size_t i = 0; i < Size * 2; i++) {
|
||||
auto r = eut.dequeue(100);
|
||||
TEST_ASSERT_NOT_NULL(r.get());
|
||||
TEST_ASSERT_EQUAL(i, r->value);
|
||||
}
|
||||
eut.signalClose();
|
||||
TEST_ASSERT_TRUE(true);
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "test.hpp"
|
||||
|
||||
#include <Semaphore.hpp>
|
||||
#include <Task.hpp>
|
||||
|
||||
using namespace fos;
|
||||
|
||||
constexpr int SHORT_DELAY = 10;
|
||||
|
||||
TEST_CASE("Test binary semaphore", "[freeRtos][sem]") {
|
||||
Semaphore eut;
|
||||
// first take fails
|
||||
TEST_ASSERT_FALSE(eut.take(0));
|
||||
eut.give();
|
||||
// first take after give fails
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
TEST_ASSERT_FALSE(eut.take(0));
|
||||
|
||||
eut.give();
|
||||
eut.give();
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
// second take after give fails
|
||||
TEST_ASSERT_FALSE(eut.take(0));
|
||||
}
|
||||
|
||||
TEST_CASE("Test counting semaphore", "[freeRtos][sem]") {
|
||||
Semaphore eut(2, 0);
|
||||
// first take fails
|
||||
TEST_ASSERT_FALSE(eut.take(0));
|
||||
eut.give();
|
||||
// first take after give fails
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
TEST_ASSERT_FALSE(eut.take(0));
|
||||
|
||||
eut.give();
|
||||
eut.give();
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
// third take after give fails
|
||||
TEST_ASSERT_FALSE(eut.take(0));
|
||||
|
||||
eut.give();
|
||||
eut.give();
|
||||
eut.give();
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
// still third take after give fails
|
||||
TEST_ASSERT_FALSE(eut.take(0));
|
||||
}
|
||||
|
||||
TEST_CASE("Test preset counting semaphore", "[freeRtos][sem]") {
|
||||
Semaphore eut(2, 2);
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
// third take fails
|
||||
TEST_ASSERT_FALSE(eut.take(0));
|
||||
|
||||
eut.give();
|
||||
// first take after give fails
|
||||
TEST_ASSERT_TRUE(eut.take(0));
|
||||
TEST_ASSERT_FALSE(eut.take(0));
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
static void test1Signal(Semaphore &s) {
|
||||
vTaskDelay(SHORT_DELAY);
|
||||
s.give();
|
||||
while (true) {
|
||||
vTaskDelay(SHORT_DELAY);
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Test timing binary semaphore", "[freeRtos][sem]") {
|
||||
static Semaphore eut;
|
||||
|
||||
TaskT<Semaphore &> signaler(test1Signal, eut, "signal1");
|
||||
{ // All ok
|
||||
TimeTest timer;
|
||||
TEST_ASSERT_TRUE(eut.take(100));
|
||||
TEST_ASSERT_INT_WITHIN(1, SHORT_DELAY, timer.getRunTime());
|
||||
}
|
||||
|
||||
{ // Timeout
|
||||
TimeTest timer;
|
||||
TEST_ASSERT_FALSE(eut.take(20));
|
||||
TEST_ASSERT_INT_WITHIN(1, 20, timer.getRunTime());
|
||||
}
|
||||
|
||||
{ // NoWait timeout
|
||||
Semaphore passiveSem;
|
||||
TimeTest timer;
|
||||
TEST_ASSERT_FALSE(passiveSem.take(0));
|
||||
TEST_ASSERT_INT_WITHIN(1, 0, timer.getRunTime());
|
||||
}
|
||||
}
|
||||
|
||||
static int syncCount = 0;
|
||||
static void producerTask(Semaphore &s) {
|
||||
while (true) {
|
||||
s.give();
|
||||
vTaskDelay(SHORT_DELAY);
|
||||
};
|
||||
}
|
||||
static void consumerTask(Semaphore &s) {
|
||||
while (true) {
|
||||
s.take();
|
||||
syncCount++;
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Test if tasks signaling with semaphore", "[freeRtos][sem]") {
|
||||
static Semaphore eut;
|
||||
|
||||
TaskT<Semaphore &> producer(producerTask, eut, "producer");
|
||||
TaskT<Semaphore &> consumer(consumerTask, eut, "consumer");
|
||||
vTaskDelay(5 * SHORT_DELAY);
|
||||
TEST_ASSERT_INT_WITHIN(1, 5, syncCount);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "test.hpp"
|
||||
|
||||
#include <Task.hpp>
|
||||
#include <array>
|
||||
|
||||
using namespace fos;
|
||||
|
||||
TEST_CASE("Test Delay", "[freeRtos][task]") {
|
||||
TickType_t start = xTaskGetTickCount();
|
||||
Task::delayMs(5 * portTICK_PERIOD_MS);
|
||||
TickType_t end = xTaskGetTickCount();
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(5, (end - start));
|
||||
TEST_ASSERT_LESS_OR_EQUAL(6, (end - start));
|
||||
}
|
||||
|
||||
static bool taskWasCalled = false;
|
||||
static void callableTask() {
|
||||
taskWasCalled = true;
|
||||
while (true) {
|
||||
vTaskDelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test creation and deletion", "[freeRtos][task]") {
|
||||
static Task task(callableTask, "task1");
|
||||
Task::delayMs(50);
|
||||
TEST_ASSERT_TRUE(taskWasCalled);
|
||||
task.kill();
|
||||
}
|
||||
|
||||
static void callableTask2() {
|
||||
taskWasCalled = true;
|
||||
while (true) {
|
||||
vTaskDelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test if task can be killed", "[freeRtos][task]") {
|
||||
static Task task(callableTask2, "task2");
|
||||
auto handle = task.getHandle();
|
||||
configASSERT(handle);
|
||||
TEST_ASSERT_TRUE(eTaskState::eReady == task.getState() ||
|
||||
eTaskState::eBlocked == task.getState());
|
||||
Task::delayMs(50);
|
||||
TEST_ASSERT_TRUE(taskWasCalled);
|
||||
task.kill();
|
||||
TEST_ASSERT_EQUAL(eTaskState::eDeleted, eTaskGetState(handle));
|
||||
}
|
||||
|
||||
static std::array<int, 2> values;
|
||||
|
||||
static void oneOfMultiple(int idx) {
|
||||
static std::array<int, 2> c{1, -1};
|
||||
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
values.at(idx) += c.at(idx);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
vTaskDelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test if tasks do not interfere", "[freeRtos][task]") {
|
||||
// Both tasks are run by starting the same static function body
|
||||
int param1 = 0;
|
||||
TaskT<int> task1(oneOfMultiple, param1, "pTask1");
|
||||
int param2 = 1;
|
||||
TaskT<int> task2(oneOfMultiple, param2, "pTask2");
|
||||
|
||||
Task::delayMs(50);
|
||||
|
||||
TEST_ASSERT_EQUAL(10, values[0]);
|
||||
TEST_ASSERT_EQUAL(-10, values[1]);
|
||||
}
|
||||
|
||||
TEST_CASE("Test if tasks can be joined correctly", "[freeRtos][task]") {
|
||||
// Both tasks are run by starting the same static function body
|
||||
int param1 = 0;
|
||||
Task task1(oneOfMultiple, param1, "pTask1");
|
||||
int param2 = 1;
|
||||
TaskT<int> task2(oneOfMultiple, param2, "pTask2");
|
||||
|
||||
Task::delayMs(50);
|
||||
|
||||
TEST_ASSERT_EQUAL(10, values[0]);
|
||||
TEST_ASSERT_EQUAL(-10, values[1]);
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "ethernet_init.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_eth.h"
|
||||
#include "Task.hpp"
|
||||
|
||||
static const char *TAG = "Watch";
|
||||
static esp_eth_handle_t s_eth_handle = NULL;
|
||||
@@ -107,6 +108,15 @@ static void initialize_ethernet(void)
|
||||
ESP_ERROR_CHECK(esp_eth_start(s_eth_handle));
|
||||
}
|
||||
|
||||
void LightingTask() {
|
||||
ESP_LOGI(TAG, "Start Lighting");
|
||||
|
||||
while (true) {
|
||||
ESP_LOGI("Light", "Tick");
|
||||
fos::Task::delayMs(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
@@ -117,4 +127,5 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(ret);
|
||||
// ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
initialize_ethernet();
|
||||
static fos::Task lightingTask(LightingTask, "Lights", 0x4000uL);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user