Skip to content

Commit

Permalink
Remove Arduino dependencies, streamline code, bump version to 0.9.5
Browse files Browse the repository at this point in the history
  • Loading branch information
Floessie committed Dec 1, 2024
1 parent a4278d9 commit 0c9cf15
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 29 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.15)

add_library(frt INTERFACE)

target_include_directories(frt INTERFACE src)
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2018-2020 Flössie <[email protected]>
Copyright (c) 2018-2024 Flössie <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
# frt - Flössie's ready (FreeRTOS) threading

frt is an object-oriented wrapper around FreeRTOS tasks, mutexes, semaphores, and queues. It provides the basic tools for a clean multithreading approach based on the [Arduino_FreeRTOS_Library](https://github.com/feilipu/Arduino_FreeRTOS_Library) with focus on static allocation, so you know your SRAM demands at compile time.
`frt` is an object-oriented wrapper around FreeRTOS tasks, mutexes, semaphores, and queues. It provides the basic tools for a clean multithreading approach based on the [FreeRTOS kernel](https://github.com/FreeRTOS/FreeRTOS-Kernel) with focus on static allocation.

This will compile just fine with the stock [Arduino_FreeRTOS_Library](https://github.com/feilipu/Arduino_FreeRTOS_Library), but if you want the advantages of static allocation you are welcome to try my [`minimal-static`](https://github.com/Floessie/Arduino_FreeRTOS_Library/tree/minimal-static) branch with frt.
The wrapper was originally geared towards the [Arduino_FreeRTOS_Library](https://github.com/feilipu/Arduino_FreeRTOS_Library), but can now be used universally with FreeRTOS (see [below](#cmake-integration)).

## Implementation

Just take a look at [`frt.h`](https://github.com/Floessie/frt/blob/master/src/frt.h). It contains the whole wrapper in about 500 lines of C++ code.
Just take a look at [`frt.h`](https://github.com/Floessie/frt/blob/master/src/frt.h). It contains the whole wrapper in about 550 lines of C++ code.

## Examples
## Arduino examples

* [`Blink_AnalogRead.ino`](https://github.com/Floessie/frt/blob/master/examples/Blink_AnalogRead/Blink_AnalogRead.ino): Like the classic [Arduino_FreeRTOS_Library example](https://github.com/feilipu/Arduino_FreeRTOS_Library/blob/master/examples/Blink_AnalogRead/Blink_AnalogRead.ino) this one blinks the builtin LED in one task and prints the value of `A0` in another task. The `loop()` is a bit more sophisticated, as it stops one task after five seconds and prints some statistics.
* [`Queue.ino`](https://github.com/Floessie/frt/blob/master/examples/Queue/Queue.ino): Shows two tasks communicating via a queue at full speed. There's a monitoring task and also a mutex involved. This example invites you to play with priorities and optimize the data flow for lower latencies.
* [`QueueISR.ino`](https://github.com/Floessie/frt/blob/master/examples/QueueISR/QueueISR.ino): Asynchronous ADC via ISR and data transfer to task with a queue. And there's also a monitoring task for fun.
* [`CriticalSection.ino`](https://github.com/Floessie/frt/blob/master/examples/CriticalSection/CriticalSection.ino): Asynchronous ADC via ISR and data transfer to task using *direct to task notification* and a critical section.

## CMake integration

To use `frt` in your FreeRTOS project (independent of Arduino) add it as a `git` submodule and extend your main `CMakeLists.txt` with:

```cmake
add_subdirectory(frt) # … or wherever you placed it
target_link_libraries(${PROJECT_NAME} … frt)
```

## API

The whole API resides in the `frt` namespace. That doesn't mean your classes have to be in namespace `frt`, but that all classes of `frt` have to be prefixed with `frt::` when using them. See the code snippets below and the examples above.
Expand Down Expand Up @@ -82,6 +92,7 @@ Mutexes in FreeRTOS can't be used from ISRs. See `frt::Task::beginCriticalSectio
A `frt::Mutex` has a dead simple interface:
* `lock()`: Locks the mutex.
* `unlock()`: Unlocks the mutex.
* `tryLock()`: Tries to lock the mutex and returns `false` if that failed. There is also a `try_lock()` function with the same semantics that can be used with [`std::scoped_lock<>`](https://en.cppreference.com/w/cpp/thread/scoped_lock).

### Semaphore

Expand Down Expand Up @@ -139,8 +150,8 @@ Here's the interface of `frt::Queue`:
## Remarks about the API

Maybe you miss some functions from the API. If so, there might be several reasons why they are missing:
* Values that are specified by you or your Arduino_FreeRTOS_Library configuration aren't exposed because you already know them.
* A wrapper for [`vTaskDelayUntil()`](https://www.freertos.org/vtaskdelayuntil.html) isn't available, as this would mean exposing the *tick* while `frt` tries its best to hide it. You can use `millis()` to determine if you missed a deadline or if there's enough time left for a `frt::Task::msleep()` as long as timer 0 wasn't stopped in the meantime.
* Values that are specified by you or your FreeRTOS configuration aren't exposed because you already know them.
* A wrapper for [`vTaskDelayUntil()`](https://www.freertos.org/vtaskdelayuntil.html) isn't available, as this would mean exposing the *tick* while `frt` tries its best to hide it. On Arduino you can use `millis()` to determine if you missed a deadline or if there's enough time left for a `frt::Task::msleep()` as long as timer 0 wasn't stopped in the meantime.
* The FreeRTOS function is too special or the problem can be solved by other means.
* I simply didn't deem the function to be important enough.

6 changes: 3 additions & 3 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name=frt
version=0.9.2
version=0.9.5
author=Flössie <[email protected]>
maintainer=Flössie <[email protected]>
sentence=Lightweight, easy-to-use wrapper around the Arduino_FreeRTOS_Library.
paragraph=frt is an object-oriented wrapper around FreeRTOS tasks, mutexes, semaphores, and queues. It provides the basic tools for a clean multithreading approach based on the Arduino_FreeRTOS_Library with focus on static allocation, so you know your SRAM demands at compile time.
paragraph=frt is an object-oriented wrapper around FreeRTOS tasks, mutexes, semaphores, and queues. It provides the basic tools for a clean multithreading approach based on the FreeRTOS kernel with focus on static allocation.
category=Timing
url=https://github.com/Floessie/frt
architectures=avr
architectures=*
includes=frt.h
depends=FreeRTOS
80 changes: 61 additions & 19 deletions src/frt.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2020 Flössie <[email protected]>
* Copyright (c) 2018-2024 Flössie <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
Expand All @@ -23,8 +23,18 @@

#pragma once

#include <Arduino.h>
#include <Arduino_FreeRTOS.h>
#ifdef __has_include
#if __has_include(<FreeRTOS.h>)
#include <FreeRTOS.h>
#elif __has_include(<Arduino_FreeRTOS.h>)
#include <Arduino_FreeRTOS.h>
#else
#error "FreeRTOS header not found."
#endif
#else
#include <Arduino_FreeRTOS.h>
#endif

#include <queue.h>
#include <semphr.h>

Expand All @@ -48,9 +58,18 @@ namespace frt
#endif
}

template<typename T>
constexpr const T& maximum(const T& a, const T& b)
{
return
a < b
? b
: a;
}

}

template<typename T, unsigned int STACK_SIZE = configMINIMAL_STACK_SIZE * sizeof(StackType_t)>
template<typename DERIVED, unsigned int STACK_SIZE = configMINIMAL_STACK_SIZE * sizeof(StackType_t)>
class Task
{
public:
Expand Down Expand Up @@ -85,6 +104,7 @@ namespace frt
stack,
&state
);

return handle;
#else
return
Expand Down Expand Up @@ -120,7 +140,11 @@ namespace frt

unsigned int getUsedStackSize() const
{
#if INCLUDE_uxTaskGetStackHighWaterMark2 == 1
return STACK_SIZE - uxTaskGetStackHighWaterMark2(handle) * sizeof(StackType_t);
#else
return STACK_SIZE - uxTaskGetStackHighWaterMark(handle) * sizeof(StackType_t);
#endif
}

void post()
Expand All @@ -130,7 +154,7 @@ namespace frt

void preparePostFromInterrupt()
{
higher_priority_task_woken = 0;
higher_priority_task_woken = pdFALSE;
}

void postFromInterrupt()
Expand All @@ -155,7 +179,7 @@ namespace frt
{
const TickType_t ticks = msecs / portTICK_PERIOD_MS;

vTaskDelay(max(1U, ticks));
vTaskDelay(detail::maximum(1U, ticks));
}

void msleep(unsigned int msecs, unsigned int& remainder)
Expand All @@ -164,7 +188,7 @@ namespace frt
const TickType_t ticks = msecs / portTICK_PERIOD_MS;
remainder = msecs % portTICK_PERIOD_MS * static_cast<bool>(ticks);

vTaskDelay(max(1U, ticks));
vTaskDelay(detail::maximum(1U, ticks));
}

void wait()
Expand All @@ -176,7 +200,7 @@ namespace frt
{
const TickType_t ticks = msecs / portTICK_PERIOD_MS;

return ulTaskNotifyTake(pdTRUE, max(1U, ticks));
return ulTaskNotifyTake(pdTRUE, detail::maximum(1U, ticks));
}

bool wait(unsigned int msecs, unsigned int& remainder)
Expand All @@ -185,7 +209,7 @@ namespace frt
const TickType_t ticks = msecs / portTICK_PERIOD_MS;
remainder = msecs % portTICK_PERIOD_MS * static_cast<bool>(ticks);

if (ulTaskNotifyTake(pdTRUE, max(1U, ticks))) {
if (ulTaskNotifyTake(pdTRUE, detail::maximum(1U, ticks))) {
remainder = 0;
return true;
}
Expand All @@ -211,16 +235,21 @@ namespace frt
}

taskENTER_CRITICAL();

do_stop = true;

while (running) {
taskEXIT_CRITICAL();

if (!from_idle_task) {
vTaskDelay(1);
} else {
taskYIELD();
}

taskENTER_CRITICAL();
}

taskEXIT_CRITICAL();

return true;
Expand All @@ -237,7 +266,7 @@ namespace frt
do_stop = self->do_stop;
taskEXIT_CRITICAL();

while (!do_stop && static_cast<T*>(self)->run()) {
while (!do_stop && static_cast<DERIVED*>(self)->run()) {
taskENTER_CRITICAL();
do_stop = self->do_stop;
taskEXIT_CRITICAL();
Expand Down Expand Up @@ -296,6 +325,16 @@ namespace frt
xSemaphoreGive(handle);
}

bool tryLock()
{
return xSemaphoreTake(handle, 0) == pdTRUE;
}

bool try_lock()
{
return tryLock();
}

private:
SemaphoreHandle_t handle;
#if configSUPPORT_STATIC_ALLOCATION > 0
Expand Down Expand Up @@ -343,7 +382,7 @@ namespace frt
{
const TickType_t ticks = msecs / portTICK_PERIOD_MS;

return xSemaphoreTake(handle, max(1U, ticks)) == pdTRUE;
return xSemaphoreTake(handle, detail::maximum(1U, ticks)) == pdTRUE;
}

bool wait(unsigned int msecs, unsigned int& remainder)
Expand All @@ -352,8 +391,9 @@ namespace frt
const TickType_t ticks = msecs / portTICK_PERIOD_MS;
remainder = msecs % portTICK_PERIOD_MS * static_cast<bool>(ticks);

if (xSemaphoreTake(handle, max(1U, ticks)) == pdTRUE) {
if (xSemaphoreTake(handle, detail::maximum(1U, ticks)) == pdTRUE) {
remainder = 0;

return true;
}

Expand All @@ -367,7 +407,7 @@ namespace frt

void preparePostFromInterrupt()
{
higher_priority_task_woken = 0;
higher_priority_task_woken = pdFALSE;
}

void postFromInterrupt()
Expand Down Expand Up @@ -427,7 +467,7 @@ namespace frt
{
const TickType_t ticks = msecs / portTICK_PERIOD_MS;

return xQueueSend(handle, &item, max(1U, ticks)) == pdTRUE;
return xQueueSend(handle, &item, detail::maximum(1U, ticks)) == pdTRUE;
}

bool push(const T& item, unsigned int msecs, unsigned int& remainder)
Expand All @@ -436,8 +476,9 @@ namespace frt
const TickType_t ticks = msecs / portTICK_PERIOD_MS;
remainder = msecs % portTICK_PERIOD_MS * static_cast<bool>(ticks);

if (xQueueSend(handle, &item, max(1U, ticks)) == pdTRUE) {
if (xQueueSend(handle, &item, detail::maximum(1U, ticks)) == pdTRUE) {
remainder = 0;

return true;
}

Expand All @@ -446,7 +487,7 @@ namespace frt

void preparePushFromInterrupt()
{
higher_priority_task_woken_from_push = 0;
higher_priority_task_woken_from_push = pdFALSE;
}

bool pushFromInterrupt(const T& item)
Expand All @@ -470,7 +511,7 @@ namespace frt
{
const TickType_t ticks = msecs / portTICK_PERIOD_MS;

return xQueueReceive(handle, &item, max(1U, ticks)) == pdTRUE;
return xQueueReceive(handle, &item, detail::maximum(1U, ticks)) == pdTRUE;
}

bool pop(T& item, unsigned int msecs, unsigned int& remainder)
Expand All @@ -479,8 +520,9 @@ namespace frt
const TickType_t ticks = msecs / portTICK_PERIOD_MS;
remainder = msecs % portTICK_PERIOD_MS * static_cast<bool>(ticks);

if (xQueueReceive(handle, &item, max(1U, ticks)) == pdTRUE) {
if (xQueueReceive(handle, &item, detail::maximum(1U, ticks)) == pdTRUE) {
remainder = 0;

return true;
}

Expand All @@ -489,7 +531,7 @@ namespace frt

void preparePopFromInterrupt()
{
higher_priority_task_woken_from_pop = 0;
higher_priority_task_woken_from_pop = pdFALSE;
}

bool popFromInterrupt(const T& item)
Expand Down

0 comments on commit 0c9cf15

Please sign in to comment.