adding freeRtos API from different project

This commit is contained in:
2024-11-16 15:50:59 +01:00
parent 49b3b46ffe
commit deccb3186c
14 changed files with 1061 additions and 0 deletions
+4
View File
@@ -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
}
+87
View File
@@ -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
+98
View File
@@ -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
+184
View File
@@ -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 &param = 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 &param, 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
)
+30
View File
@@ -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());
}
}
+155
View File
@@ -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);
}
+88
View File
@@ -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]);
}
+11
View File
@@ -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);
}