diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 562729f0..f93e5386 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,5 +21,7 @@ jobs: run: cmake --build build --target build-tests - name: "Run TactilityCore Tests" run: build/Tests/TactilityCore/TactilityCoreTests --exit + - name: "Run TactilityFreertos Tests" + run: build/Tests/TactilityFreertos/TactilityFreertosTests --exit - name: "Run TactilityHeadless Tests" run: build/Tests/Tactility/TactilityTests --exit diff --git a/CMakeLists.txt b/CMakeLists.txt index 36f271ad..f9df0104 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) "Tactility" "TactilityC" "TactilityCore" + "TactilityFreeRtos" "Libraries/esp_lvgl_port" "Libraries/elf_loader" "Libraries/lvgl" @@ -74,6 +75,7 @@ project(Tactility) if (NOT DEFINED ENV{ESP_IDF_VERSION}) add_subdirectory(Tactility) add_subdirectory(TactilityCore) + add_subdirectory(TactilityFreeRtos) add_subdirectory(Devices/simulator) endif () diff --git a/Devices/simulator/Source/LvglTask.cpp b/Devices/simulator/Source/LvglTask.cpp index daa2f8a1..8946df34 100644 --- a/Devices/simulator/Source/LvglTask.cpp +++ b/Devices/simulator/Source/LvglTask.cpp @@ -1,13 +1,14 @@ #include "LvglTask.h" -#include -#include -#include #include +#include +#include +#include +#include #include -#define TAG "lvgl_task" +constexpr auto TAG = "lvgl_task"; // Mutex for LVGL drawing static tt::RecursiveMutex lvgl_mutex; diff --git a/Devices/simulator/Source/Main.cpp b/Devices/simulator/Source/Main.cpp index f367e145..bebf8326 100644 --- a/Devices/simulator/Source/Main.cpp +++ b/Devices/simulator/Source/Main.cpp @@ -1,6 +1,6 @@ #include "Main.h" -#include #include +#include #include "FreeRTOS.h" #include "task.h" diff --git a/Firmware/CMakeLists.txt b/Firmware/CMakeLists.txt index ca300ff1..eb5d9ff9 100644 --- a/Firmware/CMakeLists.txt +++ b/Firmware/CMakeLists.txt @@ -19,6 +19,7 @@ else () target_link_libraries(FirmwareSim PRIVATE Tactility PRIVATE TactilityCore + PRIVATE TactilityFreeRtos PRIVATE Simulator PRIVATE SDL2::SDL2-static SDL2-static ) diff --git a/LICENSE.md b/LICENSE.md index bf273b45..9c697f9c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,15 +1,20 @@ -# Tactility +# Tactility: Main License -The Tactility project is available under the [GNU General Public License v3](Documentation/license-tactility.md). +The Tactility project code is available under the [GNU General Public License v3](Documentation/license-tactility.md). Distributions and forks must adhere to the license terms. The Tactility logo copyrights are owned by Ken Van Hoeylandt. Firmwares built from [the original repository](https://github.com/ByteWelder/Tactility) can be redistributed with the Tactility logo. For other usages, [contact me](https://kenvanhoeylandt.net). -# TactilityC & TactilitySDK +Third party projects that were included in Tactility retain their licenses. -TactilityC (source & binaries) and the TactilitySDK distribution files are available via the [Apache License Version 2.0](Documentation/license-tactilitysdk.md). +# Tactility: Secondary License + +The following projects are also available under [Apache License Version 2.0](Documentation/license-tactilitysdk.md): +- TactilityC +- TactilityFreertos +- TactilitySDK (source and binaries) # Other licenses & copyrights diff --git a/Tactility/CMakeLists.txt b/Tactility/CMakeLists.txt index a1e6e9bb..b4895b72 100644 --- a/Tactility/CMakeLists.txt +++ b/Tactility/CMakeLists.txt @@ -8,6 +8,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) list(APPEND REQUIRES_LIST TactilityCore + TactilityFreeRtos lvgl driver elf_loader @@ -71,6 +72,7 @@ else() target_link_libraries(Tactility PUBLIC cJSON + PUBLIC TactilityFreeRtos PUBLIC TactilityCore PUBLIC freertos_kernel PUBLIC lvgl diff --git a/Tactility/Include/Tactility/Tactility.h b/Tactility/Include/Tactility/Tactility.h index d3168bee..bd229f9e 100644 --- a/Tactility/Include/Tactility/Tactility.h +++ b/Tactility/Include/Tactility/Tactility.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include diff --git a/Tactility/Include/Tactility/hal/gps/GpsDevice.h b/Tactility/Include/Tactility/hal/gps/GpsDevice.h index d56a95b0..67366587 100644 --- a/Tactility/Include/Tactility/hal/gps/GpsDevice.h +++ b/Tactility/Include/Tactility/hal/gps/GpsDevice.h @@ -4,8 +4,8 @@ #include "GpsConfiguration.h" #include "Satellites.h" -#include #include +#include #include #include diff --git a/Tactility/Include/Tactility/hal/gps/Satellites.h b/Tactility/Include/Tactility/hal/gps/Satellites.h index f8442c31..cda420e4 100644 --- a/Tactility/Include/Tactility/hal/gps/Satellites.h +++ b/Tactility/Include/Tactility/hal/gps/Satellites.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/Tactility/Include/Tactility/hal/i2c/I2c.h b/Tactility/Include/Tactility/hal/i2c/I2c.h index f8e773fd..a31c51b8 100644 --- a/Tactility/Include/Tactility/hal/i2c/I2c.h +++ b/Tactility/Include/Tactility/hal/i2c/I2c.h @@ -3,7 +3,7 @@ #include "./I2cCompat.h" #include "Tactility/Lock.h" -#include +#include #include #include diff --git a/Tactility/Include/Tactility/hal/sdcard/SdCardDevice.h b/Tactility/Include/Tactility/hal/sdcard/SdCardDevice.h index 845f2da3..f93082f3 100644 --- a/Tactility/Include/Tactility/hal/sdcard/SdCardDevice.h +++ b/Tactility/Include/Tactility/hal/sdcard/SdCardDevice.h @@ -3,6 +3,7 @@ #include "../Device.h" #include +#include namespace tt::hal::sdcard { @@ -51,7 +52,7 @@ public: */ virtual bool unmount() = 0; - virtual State getState(TickType_t timeout = portMAX_DELAY) const = 0; + virtual State getState(TickType_t timeout = kernel::MAX_TICKS) const = 0; /** @return empty string when not mounted or the mount path if mounted */ virtual std::string getMountPath() const = 0; @@ -63,7 +64,7 @@ public: virtual MountBehaviour getMountBehaviour() const { return mountBehaviour; } /** @return true if the SD card was mounted, returns false when it was not or when a timeout happened. */ - bool isMounted(TickType_t timeout = portMAX_DELAY) const { return getState(timeout) == State::Mounted; } + bool isMounted(TickType_t timeout = kernel::MAX_TICKS) const { return getState(timeout) == State::Mounted; } }; /** Return the SdCard device if the path is within the SdCard mounted path (path std::string::starts_with() check)*/ diff --git a/Tactility/Include/Tactility/hal/spi/Spi.h b/Tactility/Include/Tactility/hal/spi/Spi.h index e349eea1..4ca52f09 100644 --- a/Tactility/Include/Tactility/hal/spi/Spi.h +++ b/Tactility/Include/Tactility/hal/spi/Spi.h @@ -3,7 +3,7 @@ #include "SpiCompat.h" #include -#include +#include namespace tt::hal::spi { diff --git a/Tactility/Include/Tactility/hal/uart/Uart.h b/Tactility/Include/Tactility/hal/uart/Uart.h index 6ea54e7b..8ff8c5e1 100644 --- a/Tactility/Include/Tactility/hal/uart/Uart.h +++ b/Tactility/Include/Tactility/hal/uart/Uart.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "../gpio/Gpio.h" #include "Tactility/Lock.h" diff --git a/Tactility/Include/Tactility/service/gps/GpsService.h b/Tactility/Include/Tactility/service/gps/GpsService.h index 6ea211a4..b8ae8c1a 100644 --- a/Tactility/Include/Tactility/service/gps/GpsService.h +++ b/Tactility/Include/Tactility/service/gps/GpsService.h @@ -1,12 +1,12 @@ #pragma once -#include "Tactility/Mutex.h" -#include "Tactility/RecursiveMutex.h" -#include "Tactility/PubSub.h" -#include "Tactility/hal/gps/GpsDevice.h" -#include "Tactility/service/Service.h" -#include "Tactility/service/ServiceContext.h" -#include "Tactility/service/gps/GpsState.h" +#include +#include +#include +#include +#include +#include +#include namespace tt::service::gps { diff --git a/Tactility/Include/Tactility/service/loader/Loader.h b/Tactility/Include/Tactility/service/loader/Loader.h index 9693c136..fd95aebb 100644 --- a/Tactility/Include/Tactility/service/loader/Loader.h +++ b/Tactility/Include/Tactility/service/loader/Loader.h @@ -1,11 +1,11 @@ #pragma once -#include -#include -#include #include +#include #include #include +#include +#include #include #include diff --git a/Tactility/Include/Tactility/service/wifi/Wifi.h b/Tactility/Include/Tactility/service/wifi/Wifi.h index f80d3350..86a9a61e 100644 --- a/Tactility/Include/Tactility/service/wifi/Wifi.h +++ b/Tactility/Include/Tactility/service/wifi/Wifi.h @@ -2,6 +2,7 @@ #include +#include #include #include diff --git a/Tactility/Private/Tactility/app/AppInstance.h b/Tactility/Private/Tactility/app/AppInstance.h index c3416d12..fc55a731 100644 --- a/Tactility/Private/Tactility/app/AppInstance.h +++ b/Tactility/Private/Tactility/app/AppInstance.h @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include diff --git a/Tactility/Private/Tactility/app/i2cscanner/I2cScannerPrivate.h b/Tactility/Private/Tactility/app/i2cscanner/I2cScannerPrivate.h index a6183586..d0e411cf 100644 --- a/Tactility/Private/Tactility/app/i2cscanner/I2cScannerPrivate.h +++ b/Tactility/Private/Tactility/app/i2cscanner/I2cScannerPrivate.h @@ -4,7 +4,7 @@ namespace tt::app::i2cscanner { -#define TAG "i2cscanner" +static constexpr auto TAG = "i2cscanner"; enum ScanState { ScanStateInitial, diff --git a/Tactility/Private/Tactility/service/gui/GuiService.h b/Tactility/Private/Tactility/service/gui/GuiService.h index 8cdccfa1..908091dd 100644 --- a/Tactility/Private/Tactility/service/gui/GuiService.h +++ b/Tactility/Private/Tactility/service/gui/GuiService.h @@ -1,9 +1,9 @@ #pragma once -#include #include -#include #include +#include +#include #include #include @@ -21,6 +21,7 @@ class GuiService final : public Service { // Thread and lock Thread* thread = nullptr; + EventGroup threadFlags; RecursiveMutex mutex; PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr; @@ -49,7 +50,7 @@ class GuiService final : public Service { } void unlock() const { - tt_check(mutex.unlock()); + mutex.unlock(); } void showApp(std::shared_ptr app); diff --git a/Tactility/Private/Tactility/service/memorychecker/MemoryCheckerService.h b/Tactility/Private/Tactility/service/memorychecker/MemoryCheckerService.h index a8fef7a0..5f467fee 100644 --- a/Tactility/Private/Tactility/service/memorychecker/MemoryCheckerService.h +++ b/Tactility/Private/Tactility/service/memorychecker/MemoryCheckerService.h @@ -2,8 +2,8 @@ #include "Tactility/service/Service.h" -#include #include +#include namespace tt::service::memorychecker { @@ -14,7 +14,7 @@ namespace tt::service::memorychecker { class MemoryCheckerService final : public Service { Mutex mutex; - Timer timer = Timer(Timer::Type::Periodic, [this] { onTimerUpdate(); }); + Timer timer = Timer(Timer::Type::Periodic, pdMS_TO_TICKS(1000), [this] { onTimerUpdate(); }); // LVGL Statusbar icon int8_t statusbarIconId = -1; diff --git a/Tactility/Private/Tactility/service/screenshot/ScreenshotTask.h b/Tactility/Private/Tactility/service/screenshot/ScreenshotTask.h index f2cce507..49a44e72 100644 --- a/Tactility/Private/Tactility/service/screenshot/ScreenshotTask.h +++ b/Tactility/Private/Tactility/service/screenshot/ScreenshotTask.h @@ -1,4 +1,4 @@ -#include "Tactility/TactilityConfig.h" +#include #if TT_FEATURE_SCREENSHOT_ENABLED diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 38e34603..5708bbd5 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -5,15 +5,15 @@ #include #include +#include +#include #include #include -#include #include #include #include #include #include -#include #include #include #include diff --git a/Tactility/Source/app/AppManifestParsing.cpp b/Tactility/Source/app/AppManifestParsing.cpp index c5855e6f..7b4ccb23 100644 --- a/Tactility/Source/app/AppManifestParsing.cpp +++ b/Tactility/Source/app/AppManifestParsing.cpp @@ -1,5 +1,6 @@ #include +#include #include namespace tt::app { diff --git a/Tactility/Source/app/development/Development.cpp b/Tactility/Source/app/development/Development.cpp index 4ee54064..6ca32ca5 100644 --- a/Tactility/Source/app/development/Development.cpp +++ b/Tactility/Source/app/development/Development.cpp @@ -1,5 +1,7 @@ #ifdef ESP_PLATFORM +#include +#include #include #include #include @@ -9,8 +11,6 @@ #include #include #include -#include -#include #include #include @@ -27,7 +27,7 @@ class DevelopmentApp final : public App { lv_obj_t* statusLabel = nullptr; std::shared_ptr service; - Timer timer = Timer(Timer::Type::Periodic, [this] { + Timer timer = Timer(Timer::Type::Periodic, pdMS_TO_TICKS(1000), [this] { auto lock = lvgl::getSyncLock()->asScopedLock(); // TODO: There's a crash when this is called when the app is being destroyed if (lock.lock(lvgl::defaultLockTime) && lvgl::isStarted()) { @@ -148,7 +148,7 @@ public: updateViewState(); - timer.start(1000); + timer.start(); } void onHide(AppContext& appContext) override { diff --git a/Tactility/Source/app/files/State.cpp b/Tactility/Source/app/files/State.cpp index a4b7b6bc..115d61c2 100644 --- a/Tactility/Source/app/files/State.cpp +++ b/Tactility/Source/app/files/State.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include diff --git a/Tactility/Source/app/files/View.cpp b/Tactility/Source/app/files/View.cpp index 1387882f..7bd90705 100644 --- a/Tactility/Source/app/files/View.cpp +++ b/Tactility/Source/app/files/View.cpp @@ -1,22 +1,22 @@ #include #include +#include +#include #include #include #include #include #include +#include +#include #include #include - -#include -#include -#include #include +#include #include #include -#include #ifdef ESP_PLATFORM #include diff --git a/Tactility/Source/app/fileselection/State.cpp b/Tactility/Source/app/fileselection/State.cpp index 52f40756..cc3c2b89 100644 --- a/Tactility/Source/app/fileselection/State.cpp +++ b/Tactility/Source/app/fileselection/State.cpp @@ -4,7 +4,7 @@ #include "Tactility/hal/sdcard/SdCardDevice.h" #include #include -#include +#include #include #include diff --git a/Tactility/Source/app/fileselection/View.cpp b/Tactility/Source/app/fileselection/View.cpp index f4235aab..136f72ec 100644 --- a/Tactility/Source/app/fileselection/View.cpp +++ b/Tactility/Source/app/fileselection/View.cpp @@ -1,18 +1,19 @@ -#include "Tactility/app/fileselection/View.h" +#include -#include "Tactility/app/alertdialog/AlertDialog.h" -#include "Tactility/lvgl/Toolbar.h" -#include "Tactility/lvgl/LvglSync.h" +#include +#include +#include #include -#include "Tactility/file/File.h" +#include #include +#include #include #include #ifdef ESP_PLATFORM -#include "Tactility/service/loader/Loader.h" +#include #endif namespace tt::app::fileselection { diff --git a/Tactility/Source/app/gpssettings/GpsSettings.cpp b/Tactility/Source/app/gpssettings/GpsSettings.cpp index aadeff91..54bc89b2 100644 --- a/Tactility/Source/app/gpssettings/GpsSettings.cpp +++ b/Tactility/Source/app/gpssettings/GpsSettings.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -7,7 +8,6 @@ #include #include #include -#include #include #include @@ -62,7 +62,7 @@ class GpsSettingsApp final : public App { } void startReceivingUpdates() { - timer->start(kernel::secondsToTicks(1)); + timer->start(); updateViews(); } @@ -268,7 +268,7 @@ class GpsSettingsApp final : public App { public: GpsSettingsApp() { - timer = std::make_unique(Timer::Type::Periodic, [this] { + timer = std::make_unique(Timer::Type::Periodic, kernel::secondsToTicks(1), [this] { updateViews(); }); service = service::gps::findGpsService(); diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index 9e437316..fea6fba7 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -8,10 +8,10 @@ #include #include +#include #include #include #include -#include #include @@ -278,10 +278,10 @@ void I2cScannerApp::startScanning() { lv_obj_clean(scanListWidget); scanState = ScanStateScanning; - scanTimer = std::make_unique(Timer::Type::Once, [this]{ + scanTimer = std::make_unique(Timer::Type::Once, 10, [this]{ onScanTimer(); }); - scanTimer->start(10); + scanTimer->start(); mutex.unlock(); } else { TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "startScanning"); diff --git a/Tactility/Source/app/power/Power.cpp b/Tactility/Source/app/power/Power.cpp index ea4d3eee..11e18640 100644 --- a/Tactility/Source/app/power/Power.cpp +++ b/Tactility/Source/app/power/Power.cpp @@ -1,12 +1,12 @@ -#include "Tactility/app/AppContext.h" -#include "Tactility/lvgl/LvglSync.h" -#include "Tactility/lvgl/Style.h" -#include "Tactility/lvgl/Toolbar.h" -#include "Tactility/service/loader/Loader.h" +#include +#include +#include +#include +#include -#include "Tactility/hal/power/PowerDevice.h" -#include +#include #include +#include #include #include @@ -31,7 +31,7 @@ std::shared_ptr _Nullable optApp() { class PowerApp : public App { - Timer update_timer = Timer(Timer::Type::Periodic, []() { onTimer(); }); + Timer update_timer = Timer(Timer::Type::Periodic, kernel::millisToTicks(1000),[]() { onTimer(); }); std::shared_ptr power; @@ -180,7 +180,7 @@ public: updateUi(); - update_timer.start(kernel::millisToTicks(1000)); + update_timer.start(); } void onHide(TT_UNUSED AppContext& app) override { diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index e005e368..346a0025 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -71,7 +72,7 @@ static void onModeSetCallback(TT_UNUSED lv_event_t* event) { } ScreenshotApp::ScreenshotApp() { - updateTimer = std::make_unique(Timer::Type::Periodic, [this]() { + updateTimer = std::make_unique(Timer::Type::Periodic, 500 / portTICK_PERIOD_MS, [this] { onTimerTick(); }); } @@ -274,7 +275,7 @@ void ScreenshotApp::onShow(AppContext& appContext, lv_obj_t* parent) { updateScreenshotMode(); if (!updateTimer->isRunning()) { - updateTimer->start(500 / portTICK_PERIOD_MS); + updateTimer->start(); } } diff --git a/Tactility/Source/app/timezone/TimeZone.cpp b/Tactility/Source/app/timezone/TimeZone.cpp index c1c597dc..7de026c8 100644 --- a/Tactility/Source/app/timezone/TimeZone.cpp +++ b/Tactility/Source/app/timezone/TimeZone.cpp @@ -7,9 +7,9 @@ #include #include +#include #include #include -#include #include #include @@ -82,7 +82,7 @@ class TimeZoneApp final : public App { updateTimer->stop(); } - updateTimer->start(500 / portTICK_PERIOD_MS); + updateTimer->start(); mutex.unlock(); } @@ -220,7 +220,7 @@ public: } void onCreate(AppContext& app) override { - updateTimer = std::make_unique(Timer::Type::Once, [this] { + updateTimer = std::make_unique(Timer::Type::Once, 500 / portTICK_PERIOD_MS, [this] { updateList(); }); } diff --git a/Tactility/Source/hal/Device.cpp b/Tactility/Source/hal/Device.cpp index 66836ea4..7d4f02f9 100644 --- a/Tactility/Source/hal/Device.cpp +++ b/Tactility/Source/hal/Device.cpp @@ -1,5 +1,6 @@ -#include "Tactility/hal/Device.h" +#include +#include #include #include @@ -9,7 +10,7 @@ std::vector> devices; RecursiveMutex mutex; static Device::Id nextId = 0; -#define TAG "devices" +constexpr auto TAG = "devices"; Device::Device() : id(nextId++) {} diff --git a/Tactility/Source/hal/gps/GpsDevice.cpp b/Tactility/Source/hal/gps/GpsDevice.cpp index e6818cfc..8abfd574 100644 --- a/Tactility/Source/hal/gps/GpsDevice.cpp +++ b/Tactility/Source/hal/gps/GpsDevice.cpp @@ -1,7 +1,9 @@ -#include "Tactility/hal/gps/GpsDevice.h" -#include "Tactility/hal/gps/GpsInit.h" -#include "Tactility/hal/gps/Probe.h" -#include "Tactility/hal/uart/Uart.h" +#include +#include +#include +#include +#include + #include #include diff --git a/Tactility/Source/hal/gps/GpsInit.cpp b/Tactility/Source/hal/gps/GpsInit.cpp index 352c7a56..de5dc06d 100644 --- a/Tactility/Source/hal/gps/GpsInit.cpp +++ b/Tactility/Source/hal/gps/GpsInit.cpp @@ -1,12 +1,16 @@ -#include "Tactility/hal/gps/Cas.h" -#include "Tactility/hal/gps/GpsDevice.h" -#include "Tactility/hal/gps/Ublox.h" +#include +#include +#include +#include +#include +#include + #include -#define TAG "gps" - namespace tt::hal::gps { +constexpr auto TAG = "gps"; + bool initMtk(uart::Uart& uart); bool initMtkL76b(uart::Uart& uart); bool initMtkPa1616s(uart::Uart& uart); diff --git a/Tactility/Source/hal/gps/Satellites.cpp b/Tactility/Source/hal/gps/Satellites.cpp index 1b321176..1570b54c 100644 --- a/Tactility/Source/hal/gps/Satellites.cpp +++ b/Tactility/Source/hal/gps/Satellites.cpp @@ -1,12 +1,14 @@ -#include "Tactility/hal/gps/Satellites.h" +#include +#include +#include #include -#define TAG "satellites" - namespace tt::hal::gps { -constexpr inline bool hasTimeElapsed(TickType_t now, TickType_t timeInThePast, TickType_t expireTimeInTicks) { +constexpr auto TAG = "Satellites"; + +constexpr bool hasTimeElapsed(TickType_t now, TickType_t timeInThePast, TickType_t expireTimeInTicks) { return (TickType_t)(now - timeInThePast) >= expireTimeInTicks; } @@ -45,7 +47,7 @@ SatelliteStorage::SatelliteRecord* SatelliteStorage::findRecordToRecycle() { lock.lock(); int candidate_index = -1; - auto candidate_age = portMAX_DELAY; + auto candidate_age = kernel::MAX_TICKS; TickType_t expire_duration = kernel::secondsToTicks(recycleTimeSeconds); TickType_t now = kernel::getTicks(); for (int i = 0; i < records.size(); ++i) { diff --git a/Tactility/Source/hal/gps/Ublox.cpp b/Tactility/Source/hal/gps/Ublox.cpp index 84e85578..22491fd0 100644 --- a/Tactility/Source/hal/gps/Ublox.cpp +++ b/Tactility/Source/hal/gps/Ublox.cpp @@ -1,12 +1,15 @@ -#include "Tactility/hal/gps/Ublox.h" -#include "Tactility/hal/gps/UbloxMessages.h" -#include "Tactility/hal/uart/Uart.h" +#include +#include +#include +#include +#include + #include -#define TAG "ublox" - namespace tt::hal::gps::ublox { +constexpr auto TAG = "ublox"; + bool initUblox6(uart::Uart& uart); bool initUblox789(uart::Uart& uart, GpsModel model); bool initUblox10(uart::Uart& uart); diff --git a/Tactility/Source/hal/i2c/I2c.cpp b/Tactility/Source/hal/i2c/I2c.cpp index 43e43b52..e417bdf5 100644 --- a/Tactility/Source/hal/i2c/I2c.cpp +++ b/Tactility/Source/hal/i2c/I2c.cpp @@ -2,15 +2,12 @@ #include #include - -#ifdef ESP_PLATFORM -#include -#endif // ESP_PLATFORM - -#define TAG "i2c" +#include namespace tt::hal::i2c { +constexpr auto TAG = "i2c"; + struct Data { Mutex mutex; bool isConfigured = false; diff --git a/Tactility/Source/hal/spi/Spi.cpp b/Tactility/Source/hal/spi/Spi.cpp index a5138a15..e1b3e3ea 100644 --- a/Tactility/Source/hal/spi/Spi.cpp +++ b/Tactility/Source/hal/spi/Spi.cpp @@ -1,5 +1,6 @@ -#include "Tactility/hal/spi/Spi.h" +#include +#include #include namespace tt::hal::spi { diff --git a/Tactility/Source/hal/uart/UartEsp.cpp b/Tactility/Source/hal/uart/UartEsp.cpp index e558d2c2..77cb11d3 100644 --- a/Tactility/Source/hal/uart/UartEsp.cpp +++ b/Tactility/Source/hal/uart/UartEsp.cpp @@ -1,17 +1,18 @@ #ifdef ESP_PLATFORM -#include "Tactility/hal/uart/UartEsp.h" +#include #include +#include #include -#include #include - -#define TAG "uart" +#include namespace tt::hal::uart { +constexpr auto TAG = "uart"; + bool UartEsp::start() { TT_LOG_I(TAG, "[%s] Starting", configuration.name.c_str()); diff --git a/Tactility/Source/hal/uart/UartPosix.cpp b/Tactility/Source/hal/uart/UartPosix.cpp index 78af4109..28ce68c4 100644 --- a/Tactility/Source/hal/uart/UartPosix.cpp +++ b/Tactility/Source/hal/uart/UartPosix.cpp @@ -1,8 +1,8 @@ #ifndef ESP_PLATFORM -#include "Tactility/hal/uart/UartPosix.h" -#include "Tactility/hal/uart/Uart.h" - +#include +#include +#include #include #include @@ -10,10 +10,10 @@ #include #include -#define TAG "uart" - namespace tt::hal::uart { +constexpr auto TAG = "uart"; + bool UartPosix::start() { auto lock = mutex.asScopedLock(); lock.lock(); diff --git a/Tactility/Source/kernel/SystemEvents.cpp b/Tactility/Source/kernel/SystemEvents.cpp index ad04be49..e326bc7a 100644 --- a/Tactility/Source/kernel/SystemEvents.cpp +++ b/Tactility/Source/kernel/SystemEvents.cpp @@ -1,6 +1,9 @@ -#include "Tactility/kernel/SystemEvents.h" +#include +#include +#include #include + #include namespace tt::kernel { @@ -56,7 +59,7 @@ static const char* getEventName(SystemEvent event) { void publishSystemEvent(SystemEvent event) { TT_LOG_I(TAG, "%s", getEventName(event)); - if (mutex.lock(portMAX_DELAY)) { + if (mutex.lock(kernel::MAX_TICKS)) { for (auto& subscription : subscriptions) { if (subscription.event == event) { subscription.handler(event); @@ -68,7 +71,7 @@ void publishSystemEvent(SystemEvent event) { } SystemEventSubscription subscribeSystemEvent(SystemEvent event, OnSystemEvent handler) { - if (mutex.lock(portMAX_DELAY)) { + if (mutex.lock(kernel::MAX_TICKS)) { auto id = ++subscriptionCounter; subscriptions.push_back({ @@ -85,7 +88,7 @@ SystemEventSubscription subscribeSystemEvent(SystemEvent event, OnSystemEvent ha } void unsubscribeSystemEvent(SystemEventSubscription subscription) { - if (mutex.lock(portMAX_DELAY)) { + if (mutex.lock(kernel::MAX_TICKS)) { std::erase_if(subscriptions, [subscription](auto& item) { return (item.id == subscription); }); diff --git a/Tactility/Source/lvgl/EspLvglPort.cpp b/Tactility/Source/lvgl/EspLvglPort.cpp index f4ce55e6..b1510627 100644 --- a/Tactility/Source/lvgl/EspLvglPort.cpp +++ b/Tactility/Source/lvgl/EspLvglPort.cpp @@ -1,9 +1,12 @@ #ifdef ESP_PLATFORM -#include -#include +#include #include +#include #include +#include + +#include // LVGL // The minimum task stack seems to be about 3500, but that crashes the wifi app in some scenarios diff --git a/Tactility/Source/lvgl/LvglSync.cpp b/Tactility/Source/lvgl/LvglSync.cpp index d4646ead..45c3ab48 100644 --- a/Tactility/Source/lvgl/LvglSync.cpp +++ b/Tactility/Source/lvgl/LvglSync.cpp @@ -22,14 +22,14 @@ void syncSet(LvglLock lock, LvglUnlock unlock) { auto old_unlock = unlock_singleton; // Ensure the old lock is not engaged when changing locks - old_lock((uint32_t)portMAX_DELAY); + old_lock((uint32_t)kernel::MAX_TICKS); lock_singleton = lock; unlock_singleton = unlock; old_unlock(); } bool lock(TickType_t timeout) { - return lock_singleton(pdMS_TO_TICKS(timeout == 0 ? portMAX_DELAY : timeout)); + return lock_singleton(pdMS_TO_TICKS(timeout == 0 ? kernel::MAX_TICKS : timeout)); } void unlock() { @@ -44,9 +44,8 @@ public: return lvgl::lock(timeoutTicks); } - bool unlock() const override { + void unlock() const override { lvgl::unlock(); - return true; } }; diff --git a/Tactility/Source/lvgl/Statusbar.cpp b/Tactility/Source/lvgl/Statusbar.cpp index bc918562..b4f94f48 100644 --- a/Tactility/Source/lvgl/Statusbar.cpp +++ b/Tactility/Source/lvgl/Statusbar.cpp @@ -3,14 +3,14 @@ #include #include +#include +#include +#include #include +#include #include #include -#include -#include -#include #include -#include #include @@ -30,7 +30,7 @@ struct StatusbarData { RecursiveMutex mutex; std::shared_ptr> pubsub = std::make_shared>(); StatusbarIcon icons[STATUSBAR_ICON_LIMIT] = {}; - Timer* time_update_timer = new Timer(Timer::Type::Once, [] { onUpdateTime(); }); + Timer* time_update_timer = new Timer(Timer::Type::Once, 200 / portTICK_PERIOD_MS, [] { onUpdateTime(); }); uint8_t time_hours = 0; uint8_t time_minutes = 0; bool time_set = false; @@ -73,12 +73,12 @@ static void onUpdateTime() { statusbar_data.time_set = true; // Reschedule - statusbar_data.time_update_timer->start(getNextUpdateTime()); + statusbar_data.time_update_timer->reset(getNextUpdateTime()); // Notify widget statusbar_data.pubsub->publish(nullptr); } else { - statusbar_data.time_update_timer->start(pdMS_TO_TICKS(60000U)); + statusbar_data.time_update_timer->reset(pdMS_TO_TICKS(60000U)); } statusbar_data.mutex.unlock(); @@ -113,9 +113,7 @@ static void statusbar_pubsub_event(Statusbar* statusbar) { static void onTimeChanged(TT_UNUSED kernel::SystemEvent event) { if (statusbar_data.mutex.lock()) { - statusbar_data.time_update_timer->stop(); - statusbar_data.time_update_timer->start(5); - + statusbar_data.time_update_timer->reset(5); statusbar_data.mutex.unlock(); } } @@ -131,7 +129,7 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) }); if (!statusbar_data.time_update_timer->isRunning()) { - statusbar_data.time_update_timer->start(200 / portTICK_PERIOD_MS); + statusbar_data.time_update_timer->start(); statusbar_data.systemEventSubscription = kernel::subscribeSystemEvent( kernel::SystemEvent::Time, onTimeChanged @@ -177,7 +175,7 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) { obj_set_style_bg_invisible(left_spacer); lv_obj_set_flex_grow(left_spacer, 1); - statusbar_data.mutex.lock(portMAX_DELAY); + statusbar_data.mutex.lock(kernel::MAX_TICKS); for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { auto* image = lv_image_create(obj); lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE); diff --git a/Tactility/Source/network/Http.cpp b/Tactility/Source/network/Http.cpp index 5681025b..6b5eb6d6 100644 --- a/Tactility/Source/network/Http.cpp +++ b/Tactility/Source/network/Http.cpp @@ -31,6 +31,7 @@ void download( auto certificate_length = strlen(reinterpret_cast(certificate.get())) + 1; + // TODO: Fix for missing initializer warnings auto config = std::make_unique(esp_http_client_config_t { .url = url.c_str(), .auth_type = HTTP_AUTH_TYPE_NONE, diff --git a/Tactility/Source/network/HttpServer.cpp b/Tactility/Source/network/HttpServer.cpp index ae47f1ea..95be72c1 100644 --- a/Tactility/Source/network/HttpServer.cpp +++ b/Tactility/Source/network/HttpServer.cpp @@ -1,6 +1,8 @@ #ifdef ESP_PLATFORM #include + +#include #include namespace tt::network { diff --git a/Tactility/Source/service/ServiceRegistration.cpp b/Tactility/Source/service/ServiceRegistration.cpp index 46fc6571..b89afe9e 100644 --- a/Tactility/Source/service/ServiceRegistration.cpp +++ b/Tactility/Source/service/ServiceRegistration.cpp @@ -1,10 +1,10 @@ #include +#include +#include #include #include -#include - #include namespace tt::service { diff --git a/Tactility/Source/service/espnow/EspNowWifi.cpp b/Tactility/Source/service/espnow/EspNowWifi.cpp index 46f6ba12..b7f3dc82 100644 --- a/Tactility/Source/service/espnow/EspNowWifi.cpp +++ b/Tactility/Source/service/espnow/EspNowWifi.cpp @@ -4,9 +4,10 @@ #ifdef CONFIG_ESP_WIFI_ENABLED -#include "Tactility/service/espnow/EspNow.h" -#include "Tactility/service/wifi/Wifi.h" +#include #include +#include +#include #include #include diff --git a/Tactility/Source/service/gui/GuiService.cpp b/Tactility/Source/service/gui/GuiService.cpp index 1fa5d171..963f826b 100644 --- a/Tactility/Source/service/gui/GuiService.cpp +++ b/Tactility/Source/service/gui/GuiService.cpp @@ -25,27 +25,30 @@ void GuiService::onLoaderEvent(LoaderService::Event event) { } int32_t GuiService::guiMain() { + auto service = findServiceById(manifest.id); + while (true) { - uint32_t flags = Thread::awaitFlags(GUI_THREAD_FLAG_ALL, EventFlag::WaitAny, portMAX_DELAY); - - // When service not started or starting -> exit - State service_state = getState(manifest.id); - if (service_state != State::Started && service_state != State::Starting) { - break; - } - - // Process and dispatch draw call - if (flags & GUI_THREAD_FLAG_DRAW) { - Thread::clearFlags(GUI_THREAD_FLAG_DRAW); - auto service = findService(); - if (service != nullptr) { - service->redraw(); + uint32_t flags = 0; + if (service->threadFlags.wait(GUI_THREAD_FLAG_ALL, false, true, portMAX_DELAY, &flags)) { + // When service not started or starting -> exit + State service_state = getState(manifest.id); + if (service_state != State::Started && service_state != State::Starting) { + break; } - } - if (flags & GUI_THREAD_FLAG_EXIT) { - Thread::clearFlags(GUI_THREAD_FLAG_EXIT); - break; + // Process and dispatch draw call + if (flags & GUI_THREAD_FLAG_DRAW) { + service->threadFlags.clear(GUI_THREAD_FLAG_DRAW); + auto service = findService(); + if (service != nullptr) { + service->redraw(); + } + } + + if (flags & GUI_THREAD_FLAG_EXIT) { + service->threadFlags.clear(GUI_THREAD_FLAG_EXIT); + break; + } } } @@ -177,8 +180,8 @@ void GuiService::onStop(TT_UNUSED ServiceContext& service) { appToRender = nullptr; isStarted = false; - ThreadId thread_id = thread->getId(); - Thread::setFlags(thread_id, GUI_THREAD_FLAG_EXIT); + auto task_handle = thread->getTaskHandle(); + threadFlags.set(GUI_THREAD_FLAG_EXIT); thread->join(); delete thread; @@ -190,8 +193,8 @@ void GuiService::onStop(TT_UNUSED ServiceContext& service) { } void GuiService::requestDraw() { - ThreadId thread_id = thread->getId(); - Thread::setFlags(thread_id, GUI_THREAD_FLAG_DRAW); + auto task_handle = thread->getTaskHandle(); + threadFlags.set(GUI_THREAD_FLAG_DRAW); } void GuiService::showApp(std::shared_ptr app) { diff --git a/Tactility/Source/service/memorychecker/MemoryCheckerService.cpp b/Tactility/Source/service/memorychecker/MemoryCheckerService.cpp index 75d7897e..7bf7c5c2 100644 --- a/Tactility/Source/service/memorychecker/MemoryCheckerService.cpp +++ b/Tactility/Source/service/memorychecker/MemoryCheckerService.cpp @@ -8,7 +8,6 @@ namespace tt::service::memorychecker { constexpr const char* TAG = "MemoryChecker"; -constexpr TickType_t TIMER_UPDATE_INTERVAL = 1000U / portTICK_PERIOD_MS; // Total memory (in bytes) that should be free before warnings occur constexpr auto TOTAL_FREE_THRESHOLD = 10'000; @@ -58,8 +57,8 @@ bool MemoryCheckerService::onStart(ServiceContext& service) { statusbarIconId = lvgl::statusbar_icon_add(icon_path, false); lvgl::statusbar_icon_set_visibility(statusbarIconId, false); - timer.setThreadPriority(Thread::Priority::Lower); - timer.start(TIMER_UPDATE_INTERVAL); + timer.setCallbackPriority(Thread::Priority::Lower); + timer.start(); return true; } diff --git a/Tactility/Source/service/screenshot/Screenshot.cpp b/Tactility/Source/service/screenshot/Screenshot.cpp index 18668d3a..14d0782b 100644 --- a/Tactility/Source/service/screenshot/Screenshot.cpp +++ b/Tactility/Source/service/screenshot/Screenshot.cpp @@ -2,11 +2,13 @@ #if TT_FEATURE_SCREENSHOT_ENABLED +#include +#include #include #include -#include #include +#include namespace tt::service::screenshot { diff --git a/Tactility/Source/service/screenshot/ScreenshotTask.cpp b/Tactility/Source/service/screenshot/ScreenshotTask.cpp index 51cf728d..2c32f997 100644 --- a/Tactility/Source/service/screenshot/ScreenshotTask.cpp +++ b/Tactility/Source/service/screenshot/ScreenshotTask.cpp @@ -159,7 +159,7 @@ void ScreenshotTask::stop() { if (thread != nullptr) { if (mutex.lock(50 / portTICK_PERIOD_MS)) { interrupted = true; - tt_check(mutex.unlock()); + mutex.unlock(); } thread->join(); @@ -167,7 +167,7 @@ void ScreenshotTask::stop() { if (mutex.lock(50 / portTICK_PERIOD_MS)) { delete thread; thread = nullptr; - tt_check(mutex.unlock()); + mutex.unlock(); } } } diff --git a/Tactility/Source/service/sdcard/Sdcard.cpp b/Tactility/Source/service/sdcard/Sdcard.cpp index 850f21a6..4668daa9 100644 --- a/Tactility/Source/service/sdcard/Sdcard.cpp +++ b/Tactility/Source/service/sdcard/Sdcard.cpp @@ -1,9 +1,9 @@ #include #include +#include #include #include -#include #include namespace tt::service::sdcard { @@ -60,12 +60,12 @@ public: } auto service = findServiceById(manifest.id); - updateTimer = std::make_unique(Timer::Type::Periodic, [service]() { + updateTimer = std::make_unique(Timer::Type::Periodic, 1000, [service] { service->update(); }); // We want to try and scan more often in case of startup or scan lock failure - updateTimer->start(1000); + updateTimer->start(); return true; } diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index 58ffabf5..1cd89faa 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -1,16 +1,16 @@ #include +#include +#include #include #include #include #include -#include -#include #include #include #include +#include #include -#include namespace tt::service::statusbar { @@ -265,14 +265,14 @@ public: assert(service); service->update(); - updateTimer = std::make_unique(Timer::Type::Periodic, [service] { + updateTimer = std::make_unique(Timer::Type::Periodic, pdMS_TO_TICKS(1000), [service] { service->update(); }); - updateTimer->setThreadPriority(Thread::Priority::Lower); + updateTimer->setCallbackPriority(Thread::Priority::Lower); // We want to try and scan more often in case of startup or scan lock failure - updateTimer->start(1000); + updateTimer->start(); return true; } diff --git a/Tactility/Source/service/wifi/Wifi.cpp b/Tactility/Source/service/wifi/Wifi.cpp index 9c3d044c..2650cf35 100644 --- a/Tactility/Source/service/wifi/Wifi.cpp +++ b/Tactility/Source/service/wifi/Wifi.cpp @@ -1,5 +1,6 @@ -#include "Tactility/service/wifi/Wifi.h" +#include +#include #include #include diff --git a/Tactility/Source/service/wifi/WifiEsp.cpp b/Tactility/Source/service/wifi/WifiEsp.cpp index ab0dcf6c..8fb5bdf8 100644 --- a/Tactility/Source/service/wifi/WifiEsp.cpp +++ b/Tactility/Source/service/wifi/WifiEsp.cpp @@ -6,15 +6,15 @@ #include -#include +#include +#include +#include #include #include -#include #include +#include #include #include -#include -#include #include #include @@ -66,10 +66,10 @@ public: /** @brief Maximum amount of records to scan (value > 0) */ uint16_t scan_list_limit = TT_WIFI_SCAN_RECORD_LIMIT; /** @brief when we last requested a scan. Loops around every 50 days. */ - TickType_t last_scan_time = portMAX_DELAY; + TickType_t last_scan_time = kernel::MAX_TICKS; esp_event_handler_instance_t event_handler_any_id = nullptr; esp_event_handler_instance_t event_handler_got_ip = nullptr; - EventFlag connection_wait_flags; + EventGroup connection_wait_flags; settings::WifiApSettings connection_target; bool pause_auto_connect = false; // Pause when manually disconnecting until manually connecting again bool connection_target_remember = false; // Whether to store the connection_target on successful connection or not @@ -792,32 +792,34 @@ static void dispatchConnect(std::shared_ptr wifi) { /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) * or connection failed for the maximum number of re-tries (WIFI_FAIL_BIT). * The bits are set by wifi_event_handler() */ - uint32_t bits = wifi_singleton->connection_wait_flags.wait(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT); - TT_LOG_I(TAG, "Waiting for EventFlag by event_handler()"); + uint32_t bits; + if (wifi_singleton->connection_wait_flags.wait(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT, false, true, kernel::MAX_TICKS, &bits)) { + TT_LOG_I(TAG, "Waiting for EventGroup by event_handler()"); - if (bits & WIFI_CONNECTED_BIT) { - wifi->setSecureConnection(config.sta.password[0] != 0x00U); - wifi->setRadioState(RadioState::ConnectionActive); - publish_event(wifi, WifiEvent::ConnectionSuccess); - TT_LOG_I(TAG, "Connected to %s", wifi->connection_target.ssid.c_str()); - if (wifi->connection_target_remember) { - if (!settings::save(wifi->connection_target)) { - TT_LOG_E(TAG, "Failed to store credentials"); - } else { - TT_LOG_I(TAG, "Stored credentials"); + if (bits & WIFI_CONNECTED_BIT) { + wifi->setSecureConnection(config.sta.password[0] != 0x00U); + wifi->setRadioState(RadioState::ConnectionActive); + publish_event(wifi, WifiEvent::ConnectionSuccess); + TT_LOG_I(TAG, "Connected to %s", wifi->connection_target.ssid.c_str()); + if (wifi->connection_target_remember) { + if (!settings::save(wifi->connection_target)) { + TT_LOG_E(TAG, "Failed to store credentials"); + } else { + TT_LOG_I(TAG, "Stored credentials"); + } } + } else if (bits & WIFI_FAIL_BIT) { + wifi->setRadioState(RadioState::On); + publish_event(wifi, WifiEvent::ConnectionFailed); + TT_LOG_I(TAG, "Failed to connect to %s", wifi->connection_target.ssid.c_str()); + } else { + wifi->setRadioState(RadioState::On); + publish_event(wifi, WifiEvent::ConnectionFailed); + TT_LOG_E(TAG, "UNEXPECTED EVENT"); } - } else if (bits & WIFI_FAIL_BIT) { - wifi->setRadioState(RadioState::On); - publish_event(wifi, WifiEvent::ConnectionFailed); - TT_LOG_I(TAG, "Failed to connect to %s", wifi->connection_target.ssid.c_str()); - } else { - wifi->setRadioState(RadioState::On); - publish_event(wifi, WifiEvent::ConnectionFailed); - TT_LOG_E(TAG, "UNEXPECTED EVENT"); - } - wifi_singleton->connection_wait_flags.clear(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT); + wifi_singleton->connection_wait_flags.clear(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT); + } } static void dispatchDisconnectButKeepActive(std::shared_ptr wifi) { @@ -920,9 +922,10 @@ public: bootSplashInit(); }); - wifi_singleton->autoConnectTimer = std::make_unique(Timer::Type::Periodic, []() { onAutoConnectTimer(); }); + auto timer_interval = std::min(2000, AUTO_SCAN_INTERVAL); + wifi_singleton->autoConnectTimer = std::make_unique(Timer::Type::Periodic, timer_interval, [] { onAutoConnectTimer(); }); // We want to try and scan more often in case of startup or scan lock failure - wifi_singleton->autoConnectTimer->start(std::min(2000, AUTO_SCAN_INTERVAL)); + wifi_singleton->autoConnectTimer->start(); if (settings::shouldEnableOnBoot()) { TT_LOG_I(TAG, "Auto-enabling due to setting"); diff --git a/Tactility/Source/service/wifi/WifiMock.cpp b/Tactility/Source/service/wifi/WifiMock.cpp index d559936a..716b2ba8 100644 --- a/Tactility/Source/service/wifi/WifiMock.cpp +++ b/Tactility/Source/service/wifi/WifiMock.cpp @@ -2,9 +2,9 @@ #include +#include #include #include -#include #include #include #include @@ -81,26 +81,31 @@ std::vector getScanResults() { records.push_back((ApRecord) { .ssid = "Home Wifi", .rssi = -30, + .channel = 0, .auth_mode = WIFI_AUTH_WPA2_PSK }); records.push_back((ApRecord) { .ssid = "No place like 127.0.0.1", .rssi = -67, + .channel = 0, .auth_mode = WIFI_AUTH_WPA2_PSK }); records.push_back((ApRecord) { .ssid = "Pretty fly for a Wi-Fi", .rssi = -70, + .channel = 0, .auth_mode = WIFI_AUTH_WPA2_PSK }); records.push_back((ApRecord) { .ssid = "An AP with a really, really long name", .rssi = -80, + .channel = 0, .auth_mode = WIFI_AUTH_WPA2_PSK }); records.push_back((ApRecord) { .ssid = "Bad Reception", .rssi = -90, + .channel = 0, .auth_mode = WIFI_AUTH_OPEN }); diff --git a/TactilityC/Include/tt_hal_i2c.h b/TactilityC/Include/tt_hal_i2c.h index 2c52683a..c12d5f52 100644 --- a/TactilityC/Include/tt_hal_i2c.h +++ b/TactilityC/Include/tt_hal_i2c.h @@ -103,9 +103,8 @@ bool tt_hal_i2c_lock(i2c_port_t port, TickType_t timeout); /** * Used to unlock an I2C port. * This is useful for creating thread-safe I2C calls while calling ESP-IDF directly of third party I2C APIs. - * @param[in] port the I2C port to unlock */ -bool tt_hal_i2c_unlock(i2c_port_t port); +void tt_hal_i2c_unlock(i2c_port_t port); #ifdef __cplusplus } diff --git a/TactilityC/Include/tt_kernel.h b/TactilityC/Include/tt_kernel.h index 1b48cf11..b0003301 100644 --- a/TactilityC/Include/tt_kernel.h +++ b/TactilityC/Include/tt_kernel.h @@ -1,8 +1,6 @@ #pragma once #include -#include - #ifdef __cplusplus extern "C" { #endif @@ -11,43 +9,6 @@ typedef unsigned long TickType; #define TT_MAX_TICKS ((TickType)(~(TickType)0)) -/** - * Stall the current task for the specified amount of time. - * @param milliseconds the time in milliseconds to stall. - */ -void tt_kernel_delay_millis(uint32_t milliseconds); - -/** - * Stall the current task for the specified amount of time. - * @param milliseconds the time in microsends to stall. - */ -void tt_kernel_delay_micros(uint32_t microSeconds); - -/** - * Stall the current task for the specified amount of time. - * @param milliseconds the time in ticks to stall. - */ -void tt_kernel_delay_ticks(TickType ticks); - -/** @return the number of ticks since the device was started */ -TickType tt_kernel_get_ticks(); - -/** Convert milliseconds to ticks */ -TickType tt_kernel_millis_to_ticks(uint32_t milliSeconds); - -/** Stall the current task until the specified timestamp - * @return false if for some reason the delay was broken off - */ -bool tt_kernel_delay_until_tick(TickType tick); - -/** @return the tick frequency of the kernel (commonly 1000 Hz when running FreeRTOS) */ -uint32_t tt_kernel_get_tick_frequency(); - -/** @return the number of milliseconds that have passed since the device was started */ -uint32_t tt_kernel_get_millis(); - -unsigned long tt_kernel_get_micros(); - #ifdef __cplusplus } #endif diff --git a/TactilityC/Include/tt_lock.h b/TactilityC/Include/tt_lock.h index 38a2203b..71d844f3 100644 --- a/TactilityC/Include/tt_lock.h +++ b/TactilityC/Include/tt_lock.h @@ -41,9 +41,8 @@ bool tt_lock_acquire(LockHandle handle, TickType timeout); /** * Attempt to unlock the lock. * @param[in] handle the handle that represents the mutex instance - * @return true when the lock was unlocked */ -bool tt_lock_release(LockHandle handle); +void tt_lock_release(LockHandle handle); /** Free the memory for this lock * This does not auto-release the lock. diff --git a/TactilityC/Include/tt_message_queue.h b/TactilityC/Include/tt_message_queue.h index a64ed980..eacad7cf 100644 --- a/TactilityC/Include/tt_message_queue.h +++ b/TactilityC/Include/tt_message_queue.h @@ -41,23 +41,13 @@ bool tt_message_queue_put(MessageQueueHandle handle, const void* message, TickTy */ bool tt_message_queue_get(MessageQueueHandle handle, void* message, TickType_t timeout); -/** @return the total amount of messages that this queue can hold */ -uint32_t tt_message_queue_get_capacity(MessageQueueHandle handle); - -/** @return the size of a single message in the queue */ -uint32_t tt_message_queue_get_message_size(MessageQueueHandle handle); - /** @return the current amount of items in the queue */ uint32_t tt_message_queue_get_count(MessageQueueHandle handle); -/** @return the remaining capacity in the queue */ -uint32_t tt_message_queue_get_space(MessageQueueHandle handle); - /** * Remove all items from the queue (if any) - * @return true on failure */ -bool tt_message_queue_reset(MessageQueueHandle handle); +void tt_message_queue_reset(MessageQueueHandle handle); #ifdef __cplusplus } diff --git a/TactilityC/Include/tt_thread.h b/TactilityC/Include/tt_thread.h index 0f8da9a4..3e7c5c93 100644 --- a/TactilityC/Include/tt_thread.h +++ b/TactilityC/Include/tt_thread.h @@ -27,7 +27,7 @@ typedef enum { } ThreadState; /** The identifier that represents the thread */ -typedef TaskHandle_t ThreadId; +typedef TaskHandle_t TaskHandle; /** ThreadCallback Your callback to run in new thread * @warning never use osThreadExit in Thread @@ -132,16 +132,16 @@ void tt_thread_start(ThreadHandle handle); /** * Wait (block) for the thread to finish. * @param[in] handle the thread instance handle - * @warning make sure you manually interrupt any logic in your thread (e.g. by an EventFlag or boolean+Mutex) + * @warning make sure you manually interrupt any logic in your thread (e.g. by an EventGroup or boolean+Mutex) */ bool tt_thread_join(ThreadHandle handle, TickType_t timeout); /** - * Get thread id + * Get thread task handle * @param[in] handle the thread instance handle - * @return the ThreadId of a thread + * @return the task handle of a thread * */ -ThreadId tt_thread_get_id(ThreadHandle handle); +TaskHandle tt_thread_get_task_handle(ThreadHandle handle); /** * Get the return code of a thread diff --git a/TactilityC/Include/tt_timer.h b/TactilityC/Include/tt_timer.h index 7738b82f..2e375077 100644 --- a/TactilityC/Include/tt_timer.h +++ b/TactilityC/Include/tt_timer.h @@ -1,7 +1,7 @@ #pragma once +#include "tt_kernel.h" #include "tt_thread.h" -#include #include #include @@ -15,8 +15,8 @@ typedef void* TimerHandle; /** The behaviour of the timer */ typedef enum { - TimerTypeOnce = 0, ///< One-shot timer. - TimerTypePeriodic = 1 ///< Repeating timer. + TimerTypeOnce = 0, // Timer triggers once after time has passed + TimerTypePeriodic = 1 // Timer triggers repeatedly after time has passed } TimerType; typedef void (*TimerCallback)(void* context); @@ -27,7 +27,7 @@ typedef void (*TimerPendingCallback)(void* context, uint32_t arg); * @param[in] callback the callback to call when the timer expires * @param[in] callbackContext the data to pass to the callback */ -TimerHandle tt_timer_alloc(TimerType type, TimerCallback callback, void* callbackContext); +TimerHandle tt_timer_alloc(TimerType type, TickType ticks, TimerCallback callback, void* callbackContext); /** Free up the memory of a timer instance */ void tt_timer_free(TimerHandle handle); @@ -35,18 +35,24 @@ void tt_timer_free(TimerHandle handle); /** * Start the timer * @param[in] handle the timer instance handle - * @parma[in] interval the interval of the timer * @return true when the timer was successfully started */ -bool tt_timer_start(TimerHandle handle, TickType_t interval); +bool tt_timer_start(TimerHandle handle); /** * Restart an already started timer * @param[in] handle the timer instance handle - * @parma[in] interval the interval of the timer + * @param[in] interval the timer new interval * @return true when the timer was successfully restarted */ -bool tt_timer_restart(TimerHandle handle, TickType_t interval); +bool tt_timer_reset_with_interval(TimerHandle handle, TickType interval); + + /** + * Restart an already started timer + * @param[in] handle the timer instance handle + * @return true when the timer was successfully restarted + */ +bool tt_timer_reset(TimerHandle handle); /** * Stop a started timer @@ -63,11 +69,11 @@ bool tt_timer_stop(TimerHandle handle); bool tt_timer_is_running(TimerHandle handle); /** - * Get the expire time of a timer + * Get the expiry time of a timer * @param[in] handle the timer instance handle * @return the absolute timestamp at which the timer will expire */ -uint32_t tt_timer_get_expire_time(TimerHandle handle); +uint32_t tt_timer_get_expiry_time(TimerHandle handle); /** * Set the pending callback for a timer diff --git a/TactilityC/Source/symbols/freertos.cpp b/TactilityC/Source/symbols/freertos.cpp index b4a8ee01..b494b1cc 100644 --- a/TactilityC/Source/symbols/freertos.cpp +++ b/TactilityC/Source/symbols/freertos.cpp @@ -3,7 +3,7 @@ #include -#include +#include #include #include diff --git a/TactilityC/Source/tt_app.cpp b/TactilityC/Source/tt_app.cpp index e66d8aad..0c400477 100644 --- a/TactilityC/Source/tt_app.cpp +++ b/TactilityC/Source/tt_app.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include diff --git a/TactilityC/Source/tt_hal_i2c.cpp b/TactilityC/Source/tt_hal_i2c.cpp index b26c9979..ea80824f 100644 --- a/TactilityC/Source/tt_hal_i2c.cpp +++ b/TactilityC/Source/tt_hal_i2c.cpp @@ -43,8 +43,8 @@ bool tt_hal_i2c_lock(i2c_port_t port, TickType_t timeout) { return tt::hal::i2c::getLock(port).lock(timeout); } -bool tt_hal_i2c_unlock(i2c_port_t port) { - return tt::hal::i2c::getLock(port).unlock(); +void tt_hal_i2c_unlock(i2c_port_t port) { + tt::hal::i2c::getLock(port).unlock(); } } \ No newline at end of file diff --git a/TactilityC/Source/tt_init.cpp b/TactilityC/Source/tt_init.cpp index 28f5956b..57cbf216 100644 --- a/TactilityC/Source/tt_init.cpp +++ b/TactilityC/Source/tt_init.cpp @@ -12,7 +12,6 @@ #include "tt_hal_i2c.h" #include "tt_hal_touch.h" #include "tt_hal_uart.h" -#include "tt_kernel.h" #include #include "tt_lvgl.h" #include "tt_lvgl_keyboard.h" @@ -261,15 +260,6 @@ const esp_elfsym main_symbols[] { ESP_ELFSYM_EXPORT(tt_hal_uart_set_baud_rate), ESP_ELFSYM_EXPORT(tt_hal_uart_get_baud_rate), ESP_ELFSYM_EXPORT(tt_hal_uart_flush_input), - ESP_ELFSYM_EXPORT(tt_kernel_delay_millis), - ESP_ELFSYM_EXPORT(tt_kernel_delay_micros), - ESP_ELFSYM_EXPORT(tt_kernel_delay_ticks), - ESP_ELFSYM_EXPORT(tt_kernel_get_ticks), - ESP_ELFSYM_EXPORT(tt_kernel_millis_to_ticks), - ESP_ELFSYM_EXPORT(tt_kernel_delay_until_tick), - ESP_ELFSYM_EXPORT(tt_kernel_get_tick_frequency), - ESP_ELFSYM_EXPORT(tt_kernel_get_millis), - ESP_ELFSYM_EXPORT(tt_kernel_get_micros), ESP_ELFSYM_EXPORT(tt_lvgl_is_started), ESP_ELFSYM_EXPORT(tt_lvgl_lock), ESP_ELFSYM_EXPORT(tt_lvgl_unlock), @@ -295,8 +285,6 @@ const esp_elfsym main_symbols[] { ESP_ELFSYM_EXPORT(tt_message_queue_free), ESP_ELFSYM_EXPORT(tt_message_queue_put), ESP_ELFSYM_EXPORT(tt_message_queue_get), - ESP_ELFSYM_EXPORT(tt_message_queue_get_capacity), - ESP_ELFSYM_EXPORT(tt_message_queue_get_message_size), ESP_ELFSYM_EXPORT(tt_message_queue_get_count), ESP_ELFSYM_EXPORT(tt_message_queue_reset), ESP_ELFSYM_EXPORT(tt_preferences_alloc), @@ -324,15 +312,16 @@ const esp_elfsym main_symbols[] { ESP_ELFSYM_EXPORT(tt_thread_get_state), ESP_ELFSYM_EXPORT(tt_thread_start), ESP_ELFSYM_EXPORT(tt_thread_join), - ESP_ELFSYM_EXPORT(tt_thread_get_id), + ESP_ELFSYM_EXPORT(tt_thread_get_task_handle), ESP_ELFSYM_EXPORT(tt_thread_get_return_code), ESP_ELFSYM_EXPORT(tt_timer_alloc), ESP_ELFSYM_EXPORT(tt_timer_free), ESP_ELFSYM_EXPORT(tt_timer_start), - ESP_ELFSYM_EXPORT(tt_timer_restart), + ESP_ELFSYM_EXPORT(tt_timer_reset), + ESP_ELFSYM_EXPORT(tt_timer_reset_with_interval), ESP_ELFSYM_EXPORT(tt_timer_stop), ESP_ELFSYM_EXPORT(tt_timer_is_running), - ESP_ELFSYM_EXPORT(tt_timer_get_expire_time), + ESP_ELFSYM_EXPORT(tt_timer_get_expiry_time), ESP_ELFSYM_EXPORT(tt_timer_set_pending_callback), ESP_ELFSYM_EXPORT(tt_timer_set_thread_priority), ESP_ELFSYM_EXPORT(tt_timezone_set), diff --git a/TactilityC/Source/tt_kernel.cpp b/TactilityC/Source/tt_kernel.cpp deleted file mode 100644 index 46c1b632..00000000 --- a/TactilityC/Source/tt_kernel.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "tt_kernel.h" -#include - -extern "C" { - -void tt_kernel_delay_millis(uint32_t milliseconds) { - tt::kernel::delayMillis(milliseconds); -} - -void tt_kernel_delay_micros(uint32_t microSeconds) { - tt::kernel::delayMicros(microSeconds); -} - -void tt_kernel_delay_ticks(TickType ticks) { - tt::kernel::delayTicks((TickType_t)ticks); -} - -TickType tt_kernel_get_ticks() { - return tt::kernel::getTicks(); -} - -TickType tt_kernel_millis_to_ticks(uint32_t milliSeconds) { - return tt::kernel::millisToTicks(milliSeconds); -} - -bool tt_kernel_delay_until_tick(TickType tick) { - return tt::kernel::delayUntilTick(tick); -} - -uint32_t tt_kernel_get_tick_frequency() { - return tt::kernel::getTickFrequency(); -} - -uint32_t tt_kernel_get_millis() { - return tt::kernel::getMillis(); -} - -unsigned long tt_kernel_get_micros() { - return tt::kernel::getMicros(); -} - -} \ No newline at end of file diff --git a/TactilityC/Source/tt_lock.cpp b/TactilityC/Source/tt_lock.cpp index 25ad8e5f..de185a92 100644 --- a/TactilityC/Source/tt_lock.cpp +++ b/TactilityC/Source/tt_lock.cpp @@ -32,7 +32,7 @@ bool tt_lock_acquire(LockHandle handle, TickType timeout) { return HANDLE_AS_LOCK(handle)->lock(timeout); } -bool tt_lock_release(LockHandle handle) { +void tt_lock_release(LockHandle handle) { return HANDLE_AS_LOCK(handle)->unlock(); } diff --git a/TactilityC/Source/tt_message_queue.cpp b/TactilityC/Source/tt_message_queue.cpp index c0474d7a..fc6d4cb8 100644 --- a/TactilityC/Source/tt_message_queue.cpp +++ b/TactilityC/Source/tt_message_queue.cpp @@ -21,23 +21,11 @@ bool tt_message_queue_get(MessageQueueHandle handle, void* message, TickType_t t return HANDLE_TO_MESSAGE_QUEUE(handle)->get(message, timeout); } -uint32_t tt_message_queue_get_capacity(MessageQueueHandle handle) { - return HANDLE_TO_MESSAGE_QUEUE(handle)->getCapacity(); -} - -uint32_t tt_message_queue_get_message_size(MessageQueueHandle handle) { - return HANDLE_TO_MESSAGE_QUEUE(handle)->getMessageSize(); -} - uint32_t tt_message_queue_get_count(MessageQueueHandle handle) { return HANDLE_TO_MESSAGE_QUEUE(handle)->getCount(); } -uint32_t tt_message_queue_get_space(MessageQueueHandle handle) { - return HANDLE_TO_MESSAGE_QUEUE(handle)->getSpace(); -} - -bool tt_message_queue_reset(MessageQueueHandle handle) { +void tt_message_queue_reset(MessageQueueHandle handle) { return HANDLE_TO_MESSAGE_QUEUE(handle)->reset(); } diff --git a/TactilityC/Source/tt_thread.cpp b/TactilityC/Source/tt_thread.cpp index 26c93a2e..fa256470 100644 --- a/TactilityC/Source/tt_thread.cpp +++ b/TactilityC/Source/tt_thread.cpp @@ -66,8 +66,8 @@ bool tt_thread_join(ThreadHandle handle, TickType_t timeout) { return HANDLE_AS_THREAD(handle)->join(timeout); } -ThreadId tt_thread_get_id(ThreadHandle handle) { - return HANDLE_AS_THREAD(handle)->getId(); +TaskHandle tt_thread_get_task_handle(ThreadHandle handle) { + return HANDLE_AS_THREAD(handle)->getTaskHandle(); } int32_t tt_thread_get_return_code(ThreadHandle handle) { diff --git a/TactilityC/Source/tt_timer.cpp b/TactilityC/Source/tt_timer.cpp index 24fffe65..5b35c282 100644 --- a/TactilityC/Source/tt_timer.cpp +++ b/TactilityC/Source/tt_timer.cpp @@ -9,9 +9,9 @@ struct TimerWrapper { extern "C" { -TimerHandle tt_timer_alloc(TimerType type, TimerCallback callback, void* callbackContext) { +TimerHandle tt_timer_alloc(TimerType type, TickType ticks, TimerCallback callback, void* callbackContext) { auto wrapper = new TimerWrapper; - wrapper->timer = std::make_unique(static_cast(type), [callback, callbackContext](){ callback(callbackContext); }); + wrapper->timer = std::make_unique(static_cast(type), ticks, [callback, callbackContext](){ callback(callbackContext); }); return wrapper; } @@ -21,12 +21,16 @@ void tt_timer_free(TimerHandle handle) { delete wrapper; } -bool tt_timer_start(TimerHandle handle, TickType_t intervalTicks) { - return HANDLE_TO_WRAPPER(handle)->timer->start(intervalTicks); +bool tt_timer_start(TimerHandle handle) { + return HANDLE_TO_WRAPPER(handle)->timer->start(); } -bool tt_timer_restart(TimerHandle handle, TickType_t intervalTicks) { - return HANDLE_TO_WRAPPER(handle)->timer->restart(intervalTicks); +bool tt_timer_reset(TimerHandle handle) { + return HANDLE_TO_WRAPPER(handle)->timer->reset(); +} + +bool tt_timer_reset_with_interval(TimerHandle handle, TickType interval) { + return HANDLE_TO_WRAPPER(handle)->timer->reset(interval); } bool tt_timer_stop(TimerHandle handle) { @@ -37,8 +41,8 @@ bool tt_timer_is_running(TimerHandle handle) { return HANDLE_TO_WRAPPER(handle)->timer->isRunning(); } -uint32_t tt_timer_get_expire_time(TimerHandle handle) { - return HANDLE_TO_WRAPPER(handle)->timer->getExpireTime(); +uint32_t tt_timer_get_expiry_time(TimerHandle handle) { + return HANDLE_TO_WRAPPER(handle)->timer->getExpiryTime(); } bool tt_timer_set_pending_callback(TimerHandle handle, TimerPendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeoutTicks) { @@ -46,12 +50,12 @@ bool tt_timer_set_pending_callback(TimerHandle handle, TimerPendingCallback call callback, callbackContext, callbackArg, - (TickType_t)timeoutTicks + timeoutTicks ); } void tt_timer_set_thread_priority(TimerHandle handle, ThreadPriority priority) { - HANDLE_TO_WRAPPER(handle)->timer->setThreadPriority(static_cast(priority)); + HANDLE_TO_WRAPPER(handle)->timer->setCallbackPriority(static_cast(priority)); } } diff --git a/TactilityCore/CMakeLists.txt b/TactilityCore/CMakeLists.txt index a55e7dab..b57398a1 100644 --- a/TactilityCore/CMakeLists.txt +++ b/TactilityCore/CMakeLists.txt @@ -9,7 +9,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Include/" - REQUIRES mbedtls nvs_flash esp_rom esp_timer + REQUIRES TactilityFreeRtos mbedtls nvs_flash esp_rom ) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -32,7 +32,7 @@ else() add_definitions(-D_Nonnull=) target_link_libraries(TactilityCore + PUBLIC TactilityFreeRtos PUBLIC mbedtls - PUBLIC freertos_kernel ) endif() \ No newline at end of file diff --git a/TactilityCore/Include/Tactility/CpuAffinity.h b/TactilityCore/Include/Tactility/CpuAffinity.h index 1408a3d5..a8d55742 100644 --- a/TactilityCore/Include/Tactility/CpuAffinity.h +++ b/TactilityCore/Include/Tactility/CpuAffinity.h @@ -1,6 +1,6 @@ #pragma once -#include "RtosCompat.h" +#include namespace tt { diff --git a/TactilityCore/Include/Tactility/Dispatcher.h b/TactilityCore/Include/Tactility/Dispatcher.h deleted file mode 100644 index 114eb639..00000000 --- a/TactilityCore/Include/Tactility/Dispatcher.h +++ /dev/null @@ -1,56 +0,0 @@ -/** -* @file Dispatcher.h -* -* Dispatcher is a thread-safe code execution queue. -*/ -#pragma once - -#include "MessageQueue.h" -#include "Mutex.h" -#include "EventFlag.h" - -#include -#include - -namespace tt { - -/** - * A thread-safe way to defer code execution. - * Generally, one task would dispatch the execution, - * while the other thread consumes and executes the work. - */ -class Dispatcher final { - -public: - - typedef std::function Function; - -private: - - Mutex mutex; - std::queue queue = {}; - EventFlag eventFlag; - -public: - - explicit Dispatcher() = default; - ~Dispatcher(); - - /** - * Queue a function to be consumed elsewhere. - * @param[in] function the function to execute elsewhere - * @param[in] timeout lock acquisition timeout - * @return true if dispatching was successful (timeout not reached) - */ - bool dispatch(Function function, TickType_t timeout = portMAX_DELAY); - - /** - * Consume 1 or more dispatched function (if any) until the queue is empty. - * @warning The timeout is only the wait time before consuming the message! It is not a limit to the total execution time when calling this method. - * @param[in] timeout the ticks to wait for a message - * @return the amount of messages that were consumed - */ - uint32_t consume(TickType_t timeout = portMAX_DELAY); -}; - -} // namespace diff --git a/TactilityCore/Include/Tactility/DispatcherThread.h b/TactilityCore/Include/Tactility/DispatcherThread.h deleted file mode 100644 index 2e0f1557..00000000 --- a/TactilityCore/Include/Tactility/DispatcherThread.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "Dispatcher.h" - -namespace tt { - -/** Starts a Thread to process dispatched messages */ -class DispatcherThread final { - - Dispatcher dispatcher; - std::unique_ptr thread; - bool interruptThread = true; - - int32_t threadMain(); - -public: - - explicit DispatcherThread(const std::string& threadName, size_t threadStackSize = 4096); - ~DispatcherThread(); - - /** - * Dispatch a message. - */ - bool dispatch(Dispatcher::Function function, TickType_t timeout = portMAX_DELAY); - - /** Start the thread (blocking). */ - void start(); - - /** Stop the thread (blocking). */ - void stop(); - - /** @return true of the thread is started */ - bool isStarted() const { return thread != nullptr && !interruptThread; } -}; - -} \ No newline at end of file diff --git a/TactilityCore/Include/Tactility/EventFlag.h b/TactilityCore/Include/Tactility/EventFlag.h deleted file mode 100644 index 91319f43..00000000 --- a/TactilityCore/Include/Tactility/EventFlag.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "RtosCompatEventGroups.h" -#include - -namespace tt { - -/** - * Wrapper for FreeRTOS xEventGroup. - */ -class EventFlag final { - - struct EventGroupHandleDeleter { - void operator()(EventGroupHandle_t handleToDelete) { - vEventGroupDelete(handleToDelete); - } - }; - - std::unique_ptr, EventGroupHandleDeleter> handle; - -public: - - EventFlag(); - ~EventFlag(); - - enum Flag { - WaitAny = 0x00000000U, ///< Wait for any flag (default). - WaitAll = 0x00000001U, ///< Wait for all flags. - NoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for. - - Error = 0x80000000U, ///< Error indicator. - ErrorUnknown = 0xFFFFFFFFU, ///< TtStatusError (-1). - ErrorTimeout = 0xFFFFFFFEU, ///< TtStatusErrorTimeout (-2). - ErrorResource = 0xFFFFFFFDU, ///< TtStatusErrorResource (-3). - ErrorParameter = 0xFFFFFFFCU, ///< TtStatusErrorParameter (-4). - ErrorISR = 0xFFFFFFFAU, ///< TtStatusErrorISR (-6). - }; - - /** Set the bitmask for 1 or more flags that we might be waiting for */ - uint32_t set(uint32_t flags) const; - - /** Clear the specified flags */ - uint32_t clear(uint32_t flags) const; - - /** Get the currently set flags */ - uint32_t get() const; - - /** Await for flags to be set - * @param[in] flags the bitmask of the flags that we want to wait for - * @param[in] options the trigger behaviour: WaitAny, WaitAll, NoClear (NoClear can be combined with either WaitAny or WaitAll) - * @param[in] timeoutTicks the maximum amount of ticks to wait - */ - uint32_t wait( - uint32_t flags, - uint32_t options = WaitAny, - uint32_t timeoutTicks = (uint32_t)portMAX_DELAY - ) const; -}; - -} // namespace diff --git a/TactilityCore/Include/Tactility/MessageQueue.h b/TactilityCore/Include/Tactility/MessageQueue.h deleted file mode 100644 index f781ef51..00000000 --- a/TactilityCore/Include/Tactility/MessageQueue.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @file MessageQueue.h - * - * MessageQueue is a wrapper for FreeRTOS xQueue functionality. - * There is no additional thread-safety on top of the xQueue functionality, - * so make sure you create a lock if needed. - */ -#pragma once - -#include - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" -#else -#include "FreeRTOS.h" -#include "queue.h" -#endif - -namespace tt { - -/** - * Message Queue implementation. - * Calls can be done from ISR/IRQ mode unless otherwise specified. - */ -class MessageQueue { - - struct QueueHandleDeleter { - void operator()(QueueHandle_t handleToDelete) { - vQueueDelete(handleToDelete); - } - }; - - std::unique_ptr, QueueHandleDeleter> handle; - -public: - /** Allocate message queue - * @param[in] capacity Maximum messages in queue - * @param[in] messageSize The size in bytes of a single message - */ - MessageQueue(uint32_t capacity, uint32_t messageSize); - - ~MessageQueue(); - - /** Post a message to the queue. - * The message is queued by copy, not by reference. - * @param[in] message A pointer to a message. The message will be copied into a buffer. - * @param[in] timeout - * @return success result - */ - bool put(const void* message, TickType_t timeout); - - /** Get message from queue - * @param[out] message A pointer to an already allocated message object - * @param[in] timeout - * @return success result - */ - bool get(void* message, TickType_t timeout); - - /** - * @return The maximum amount of messages that can be in the queue at any given time. - */ - uint32_t getCapacity() const; - - /** - * @return The size of a single message in bytes - */ - uint32_t getMessageSize() const; - - /** - * @return How many messages are currently in the queue. - */ - uint32_t getCount() const; - - /** - * @return How many messages can be added to the queue before the put() method starts blocking. - */ - uint32_t getSpace() const; - - /** Reset queue (cannot be called in ISR/IRQ mode) - * @return success result - */ - bool reset(); -}; - -} // namespace diff --git a/TactilityCore/Include/Tactility/RtosCompat.h b/TactilityCore/Include/Tactility/RtosCompat.h deleted file mode 100644 index 8399d08b..00000000 --- a/TactilityCore/Include/Tactility/RtosCompat.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -/** - * Compatibility includes for FreeRTOS. - * Custom FreeRTOS from ESP-IDF prefixes paths with "freertos/", - * but this isn't the normal behaviour for the regular FreeRTOS project. - */ - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#else -#include "FreeRTOS.h" -#endif diff --git a/TactilityCore/Include/Tactility/RtosCompatEventGroups.h b/TactilityCore/Include/Tactility/RtosCompatEventGroups.h deleted file mode 100644 index 52329d20..00000000 --- a/TactilityCore/Include/Tactility/RtosCompatEventGroups.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -/** - * See explanation in RtosCompat.h - */ - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" -#else -#include "FreeRTOS.h" -#include "event_groups.h" -#endif - diff --git a/TactilityCore/Include/Tactility/RtosCompatSemaphore.h b/TactilityCore/Include/Tactility/RtosCompatSemaphore.h deleted file mode 100644 index 06fc4223..00000000 --- a/TactilityCore/Include/Tactility/RtosCompatSemaphore.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -/** - * See explanation in RtosCompat.h - */ - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#else -#include "FreeRTOS.h" -#include "semphr.h" -#endif - diff --git a/TactilityCore/Include/Tactility/RtosCompatTask.h b/TactilityCore/Include/Tactility/RtosCompatTask.h deleted file mode 100644 index 509e1d60..00000000 --- a/TactilityCore/Include/Tactility/RtosCompatTask.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -/** - * See explanation in RtosCompat.h - */ - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#else -#include "FreeRTOS.h" -#include "task.h" -#endif - diff --git a/TactilityCore/Include/Tactility/RtosCompatTimers.h b/TactilityCore/Include/Tactility/RtosCompatTimers.h deleted file mode 100644 index 77b9963a..00000000 --- a/TactilityCore/Include/Tactility/RtosCompatTimers.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -/** - * See explanation in RtosCompat.h - */ - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/timers.h" -#else -#include "FreeRTOS.h" -#include "timers.h" -#endif diff --git a/TactilityCore/Include/Tactility/Semaphore.h b/TactilityCore/Include/Tactility/Semaphore.h deleted file mode 100644 index 756363ca..00000000 --- a/TactilityCore/Include/Tactility/Semaphore.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include "Lock.h" -#include "kernel/Kernel.h" -#include -#include - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#else -#include "FreeRTOS.h" -#include "semphr.h" -#endif - -namespace tt { - -/** - * Wrapper for xSemaphoreCreateBinary (max count == 1) and xSemaphoreCreateCounting (max count > 1) - * Can be used from IRQ/ISR mode, but cannot be created/destroyed from such a context. - */ -class Semaphore final : public Lock { - - struct SemaphoreHandleDeleter { - void operator()(QueueHandle_t handleToDelete) { - assert(!kernel::isIsr()); - vSemaphoreDelete(handleToDelete); - } - }; - - std::unique_ptr, SemaphoreHandleDeleter> handle; - -public: - - using Lock::lock; - - /** - * Cannot be called from IRQ/ISR mode. - * @param[in] maxAvailable The maximum count - * @param[in] initialAvailable The initial count - */ - Semaphore(uint32_t maxAvailable, uint32_t initialAvailable); - - /** - * Cannot be called from IRQ/ISR mode. - * @param[in] maxAvailable The maximum count - */ - explicit Semaphore(uint32_t maxAvailable) : Semaphore(maxAvailable, maxAvailable) {}; - - /** Cannot be called from IRQ/ISR mode. */ - ~Semaphore() override; - - Semaphore(Semaphore& other) : handle(std::move(other.handle)) {} - - /** Acquire semaphore */ - bool acquire(TickType_t timeout) const; - - /** Release semaphore */ - bool release() const; - - bool lock(TickType_t timeout) const override { return acquire(timeout); } - - bool unlock() const override { return release(); } - - /** @return return the amount of times this semaphore can be acquired/locked */ - uint32_t getAvailable() const; -}; - -} // namespace diff --git a/TactilityCore/Include/Tactility/StreamBuffer.h b/TactilityCore/Include/Tactility/StreamBuffer.h deleted file mode 100644 index b85e5b8a..00000000 --- a/TactilityCore/Include/Tactility/StreamBuffer.h +++ /dev/null @@ -1,152 +0,0 @@ -#pragma once - -#include - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include "freertos/stream_buffer.h" -#else -#include "FreeRTOS.h" -#include "stream_buffer.h" -#endif - -namespace tt { - -/** - * Stream buffers are used to send a continuous stream of data from one task or - * interrupt to another. Their implementation is light weight, making them - * particularly suited for interrupt to task and core to core communication - * scenarios. - * - * **NOTE**: Stream buffer implementation assumes there is only one task or - * interrupt that will write to the buffer (the writer), and only one task or - * interrupt that will read from the buffer (the reader). - */ -class StreamBuffer final { - - struct StreamBufferHandleDeleter { - void operator()(StreamBufferHandle_t handleToDelete) { - vStreamBufferDelete(handleToDelete); - } - }; - - std::unique_ptr, StreamBufferHandleDeleter> handle; - -public: - - /** - * Stream buffer implementation assumes there is only one task or - * interrupt that will write to the buffer (the writer), and only one task or - * interrupt that will read from the buffer (the reader). - * - * @param[in] size The total number of bytes the stream buffer will be able to hold at any one time. - * @param[in] triggerLevel The number of bytes that must be in the stream buffer - * before a task that is blocked on the stream buffer to wait for data is moved out of the blocked state. - * @return The stream buffer instance. - */ - StreamBuffer(size_t size, size_t triggerLevel); - - ~StreamBuffer() = default; - - /** - * @brief Set trigger level for stream buffer. - * A stream buffer's trigger level is the number of bytes that must be in the - * stream buffer before a task that is blocked on the stream buffer to - * wait for data is moved out of the blocked state. - * - * @param[in] triggerLevel The new trigger level for the stream buffer. - * @return true if trigger level can be be updated (new trigger level was less than or equal to the stream buffer's length). - * @return false if trigger level can't be be updated (new trigger level was greater than the stream buffer's length). - */ - bool setTriggerLevel(size_t triggerLevel) const; - - /** - * @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer. - * Wakes up task waiting for data to become available if called from ISR. - * - * @param[in] data A pointer to the data that is to be copied into the stream buffer. - * @param[in] length The maximum number of bytes to copy from data into the stream buffer. - * @param[in] timeout The maximum amount of time the task should remain in the - * Blocked state to wait for space to become available if the stream buffer is full. - * Will return immediately if timeout is zero. - * Setting timeout to portMAX_DELAY will cause the task to wait indefinitely. - * Ignored if called from ISR. - * @return The number of bytes actually written to the stream buffer. - */ - size_t send( - const void* data, - size_t length, - uint32_t timeout - ) const; - - /** - * @brief Receives bytes from a stream buffer. - * Wakes up task waiting for space to become available if called from ISR. - * - * @param[in] data A pointer to the buffer into which the received bytes will be - * copied. - * @param[in] length The length of the buffer pointed to by the data parameter. - * @param[in] timeout The maximum amount of time the task should remain in the - * Blocked state to wait for data to become available if the stream buffer is empty. - * Will return immediately if timeout is zero. - * Setting timeout to portMAX_DELAY will cause the task to wait indefinitely. - * Ignored if called from ISR. - * @return The number of bytes read from the stream buffer, if any. - */ - size_t receive( - void* data, - size_t length, - uint32_t timeout - ) const; - - /** - * @brief Queries a stream buffer to see how much data it contains, which is equal to - * the number of bytes that can be read from the stream buffer before the stream - * buffer would be empty. - * - * @return The number of bytes that can be read from the stream buffer before - * the stream buffer would be empty. - */ - size_t getAvailableReadBytes() const; - - /** - * @brief Queries a stream buffer to see how much free space it contains, which is - * equal to the amount of data that can be sent to the stream buffer before it - * is full. - * - * @return The number of bytes that can be written to the stream buffer before - * the stream buffer would be full. - */ - size_t getAvailableWriteBytes() const; - - /** - * @brief Queries a stream buffer to see if it is full. - * - * @return true if the stream buffer is full. - * @return false if the stream buffer is not full. - */ - bool isFull() const; - - /** - * @brief Queries a stream buffer to see if it is empty. - * - * @param stream_buffer The stream buffer instance. - * @return true if the stream buffer is empty. - * @return false if the stream buffer is not empty. - */ - bool isEmpty() const; - - /** - * @brief Resets a stream buffer to its initial, empty, state. Any data that was - * in the stream buffer is discarded. A stream buffer can only be reset if there - * are no tasks blocked waiting to either send to or receive from the stream buffer. - * - * @return TtStatusOk if the stream buffer is reset. - * @return TtStatusError if there was a task blocked waiting to send to or read - * from the stream buffer then the stream buffer is not reset. - */ - bool reset() const; -}; - - -} // namespace diff --git a/TactilityCore/Include/Tactility/TactilityCore.h b/TactilityCore/Include/Tactility/TactilityCore.h index 0567df76..d107dc4a 100644 --- a/TactilityCore/Include/Tactility/TactilityCore.h +++ b/TactilityCore/Include/Tactility/TactilityCore.h @@ -2,11 +2,9 @@ #include +#include "Tactility/Thread.h" #include "Check.h" #include "CoreDefines.h" -#include "EventFlag.h" -#include "kernel/Kernel.h" -#include "kernel/critical/Critical.h" #include "Log.h" -#include "Mutex.h" -#include "Thread.h" + +#include diff --git a/TactilityCore/Include/Tactility/TactilityCoreConfig.h b/TactilityCore/Include/Tactility/TactilityCoreConfig.h deleted file mode 100644 index 09729e39..00000000 --- a/TactilityCore/Include/Tactility/TactilityCoreConfig.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#define TT_CONFIG_THREAD_MAX_PRIORITIES 10 \ No newline at end of file diff --git a/TactilityCore/Include/Tactility/Thread.h b/TactilityCore/Include/Tactility/Thread.h deleted file mode 100644 index a8efac51..00000000 --- a/TactilityCore/Include/Tactility/Thread.h +++ /dev/null @@ -1,195 +0,0 @@ -#pragma once - -#include "RtosCompatTask.h" - -#include -#include -#include - -namespace tt { - -typedef TaskHandle_t ThreadId; - -class Thread final { - -public: - - enum class State{ - Stopped, - Starting, - Running, - }; - - /** ThreadPriority */ - enum class Priority : UBaseType_t { - None = 0U, /**< Uninitialized, choose system default */ - Idle = 1U, - Lower = 2U, - Low = 3U, - Normal = 4U, - High = 5U, - Higher = 6U, - Critical = 7U - }; - - - /** ThreadCallback Your callback to run in new thread - * @warning never use osThreadExit in Thread - */ - typedef int32_t (*Callback)(void* context); - typedef std::function MainFunction; - - /** Write to stdout callback - * @param[in] data pointer to data - * @param[in] size data size @warning your handler must consume everything - */ - typedef void (*StdoutWriteCallback)(const char* data, size_t size); - - /** Thread state change callback called upon thread state change - * @param[in] state new thread state - * @param[in] context callback context - */ - typedef void (*StateCallback)(State state, void* context); - -private: - - static void mainBody(void* context); - - TaskHandle_t taskHandle = nullptr; - State state = State::Stopped; - MainFunction mainFunction; - int32_t callbackResult = 0; - StateCallback stateCallback = nullptr; - void* stateCallbackContext = nullptr; - std::string name = {}; - Priority priority = Priority::Normal; - configSTACK_DEPTH_TYPE stackSize = 0; - portBASE_TYPE affinity = -1; - - void setState(Thread::State state); - -public: - - Thread() = default; - - /** Allocate Thread, shortcut version - * @param[in] name the name of the thread - * @param[in] stackSize in bytes - * @param[in] function - * @param[in] affinity Which CPU core to pin this task to, -1 means unpinned (only works on ESP32) - */ - Thread( - std::string name, - configSTACK_DEPTH_TYPE stackSize, - MainFunction function, - portBASE_TYPE affinity = -1 - ); - - ~Thread(); - - /** Set Thread name - * @param[in] name string - */ - void setName(std::string name); - - /** Set Thread stack size - * @param[in] stackSize stack size in bytes - */ - void setStackSize(size_t stackSize); - - /** Set CPU core pinning for this thread. - * @param[in] affinity -1 means not pinned, otherwise it's the core id (e.g. 0 or 1 on ESP32) - */ - void setAffinity(portBASE_TYPE affinity); - - /** Set Thread callback - * @param[in] callback ThreadCallback, called upon thread run - * @param[in] callbackContext what to pass to the callback - */ - [[deprecated("use setMainFunction()")]] - void setCallback(Callback callback, _Nullable void* callbackContext = nullptr); - - /** Set Thread callback - * @param[in] function called upon thread run - */ - void setMainFunction(MainFunction function); - - /** Set Thread priority - * @param[in] priority ThreadPriority value - */ - void setPriority(Priority priority); - - - /** Set Thread state change callback - * @param[in] callback state change callback - * @param[in] callbackContext pointer to context - */ - void setStateCallback(StateCallback callback, _Nullable void* callbackContext = nullptr); - - /** Get Thread state - * @return thread state from ThreadState - */ - State getState() const; - - /** Start Thread */ - void start(); - - /** Join Thread - * @warning make sure you manually interrupt any logic in your thread (e.g. by an EventFlag or boolean+Mutex) - * @param[in] timeout the maximum amount of time to wait - * @param[in] pollInterval the amount of ticks to wait before we check again if the thread is finished - * @return success result - */ - bool join(TickType_t timeout = portMAX_DELAY, TickType_t pollInterval = 10); - - /** Get FreeRTOS ThreadId for Thread instance - * @return ThreadId or nullptr - */ - ThreadId getId() const; - - /** - * @warning crashes when state is not "stopped" - * @return thread return code - */ - int32_t getReturnCode() const; - - /** Suspend thread - * @param[in] threadId thread id - */ - static void suspend(ThreadId threadId); - - /** Resume thread - * @param[in] threadId thread id - */ - static void resume(ThreadId threadId); - - /** Get thread suspended state - * @param[in] threadId thread id - * @return true if thread is suspended - */ - static bool isSuspended(ThreadId threadId); - - /** - * @brief Get thread stack watermark - * @param[in] threadId - * @return uint32_t - */ - static uint32_t getStackSpace(ThreadId threadId); - - /** @return pointer to Thread instance or nullptr if this thread doesn't belong to Tactility */ - static Thread* getCurrent(); - - static uint32_t setFlags(ThreadId threadId, uint32_t flags); - - static uint32_t clearFlags(uint32_t flags); - - static uint32_t getFlags(); - - static uint32_t awaitFlags(uint32_t flags, uint32_t options, uint32_t timeout); -}; - -constexpr auto THREAD_PRIORITY_SERVICE = Thread::Priority::High; -constexpr auto THREAD_PRIORITY_RENDER = Thread::Priority::Higher; -constexpr auto THREAD_PRIORITY_ISR = Thread::Priority::Critical; - -} // namespace diff --git a/TactilityCore/Include/Tactility/Timer.h b/TactilityCore/Include/Tactility/Timer.h deleted file mode 100644 index 2d58bf69..00000000 --- a/TactilityCore/Include/Tactility/Timer.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include "RtosCompatTimers.h" -#include "Thread.h" - -#include -#include - -namespace tt { - -class Timer { - -public: - - typedef std::function Callback; - typedef void (*PendingCallback)(void* context, uint32_t arg); - -private: - - struct TimerHandleDeleter { - void operator()(TimerHandle_t handleToDelete) const { - xTimerDelete(handleToDelete, portMAX_DELAY); - } - }; - - Callback callback; - std::unique_ptr, TimerHandleDeleter> handle; - - static void onCallback(TimerHandle_t hTimer); - -public: - - enum class Type { - Once = 0, ///< One-shot timer. - Periodic = 1 ///< Repeating timer. - }; - - enum class Priority{ - Normal, /**< Lower then other threads */ - Elevated, /**< Same as other threads */ - }; - - /** - * @param[in] type The timer type - * @param[in] callback The callback function - */ - Timer(Type type, Callback callback); - - ~Timer(); - - /** Start timer - * @warning This is asynchronous call, real operation will happen as soon as timer service process this request. - * @param[in] interval The interval in ticks - * @return success result - */ - bool start(TickType_t interval); - - /** Restart timer with previous timeout value - * @warning This is asynchronous call, real operation will happen as soon as timer service process this request. - * @param[in] interval The interval in ticks - * @return success result - */ - bool restart(TickType_t interval); - - /** Stop timer - * @warning This is asynchronous call, real operation will happen as soon as timer service process this request. - * @return success result - */ - bool stop(); - - /** Is timer running - * @warning This cal may and will return obsolete timer state if timer commands are still in the queue. Please read FreeRTOS timer documentation first. - * @return true when running - */ - bool isRunning(); - - /** Get timer expire time - * @return expire tick - */ - TickType_t getExpireTime(); - - /** - * Calls xTimerPendFunctionCall internally. - * @param[in] callback the function to call - * @param[in] callbackContext the first function argument - * @param[in] callbackArg the second function argument - * @param[in] timeout the function timeout (must set to 0 in ISR mode) - * @return true on success - */ - bool setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout); - - /** Set Timer thread priority - * @param[in] priority The priority - */ - void setThreadPriority(Thread::Priority priority); -}; - -} // namespace diff --git a/TactilityCore/Include/Tactility/file/File.h b/TactilityCore/Include/Tactility/file/File.h index 5e6b2054..fa102d99 100644 --- a/TactilityCore/Include/Tactility/file/File.h +++ b/TactilityCore/Include/Tactility/file/File.h @@ -4,7 +4,8 @@ */ #pragma once -#include "Tactility/TactilityCore.h" +#include +#include #include #include diff --git a/TactilityCore/Include/Tactility/kernel/Kernel.h b/TactilityCore/Include/Tactility/kernel/Kernel.h deleted file mode 100644 index 3f3488b8..00000000 --- a/TactilityCore/Include/Tactility/kernel/Kernel.h +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once - -#ifdef ESP_PLATFORM -#include "freertos/FreeRTOS.h" -#include -#else -#include "FreeRTOS.h" -#include -#endif - -namespace tt::kernel { - -/** Recognized platform types */ -typedef enum { - PlatformEsp, - PlatformSimulator -} Platform; - -/** Return true when called from an Interrupt Service Routine (~IRQ mode) */ -#ifdef ESP_PLATFORM -constexpr bool isIsr() { return (xPortInIsrContext() == pdTRUE); } -#else -constexpr bool isIsr() { return false; } -#endif - -/** Check if kernel is running - * @return true if the FreeRTOS kernel is running, false otherwise - */ -bool isRunning(); - -/** Lock kernel, pause process scheduling - * @warning don't call from ISR context - * @return true on success - */ -bool lock(); - -/** Unlock kernel, resume process scheduling - * @warning don't call from ISR context - * @return true on success - */ -bool unlock(); - -/** Restore kernel lock state - * @warning don't call from ISR context - * @param[in] lock The lock state - * @return true on success - */ -bool restoreLock(bool lock); - -/** Get kernel systick frequency - * @return systick counts per second - */ -uint32_t getTickFrequency(); - -TickType_t getTicks(); - -constexpr size_t getMillis() { return getTicks() / portTICK_PERIOD_MS; } - -constexpr long int getMicros() { -#ifdef ESP_PLATFORM - return static_cast(esp_timer_get_time()); -#else - timeval tv; - gettimeofday(&tv, nullptr); - return 1000000 * tv.tv_sec + tv.tv_usec; -#endif -} - -/** Delay execution - * @warning don't call from ISR context - * Also keep in mind delay is aliased to scheduler timer intervals. - * @param[in] ticks The ticks count to pause - */ -void delayTicks(TickType_t ticks); - -/** Delay until tick - * @warning don't call from ISR context - * @param[in] ticks The tick until which kerel should delay task execution - * @return true on success - */ -bool delayUntilTick(TickType_t tick); - -constexpr TickType_t secondsToTicks(uint32_t seconds) { - return static_cast(seconds) * 1000U / portTICK_PERIOD_MS; -} - -constexpr TickType_t minutesToTicks(uint32_t minutes) { - return secondsToTicks(minutes * 60U); -} - -/** Convert milliseconds to ticks - * - * @param[in] milliSeconds time in milliseconds - * @return time in ticks - */ -TickType_t millisToTicks(uint32_t milliSeconds); - -/** Delay in milliseconds - * This method uses kernel ticks on the inside, which causes delay to be aliased to scheduler timer intervals. - * Real wait time will be between X+ milliseconds. - * Special value: 0, will cause task yield. - * Also if used when kernel is not running will fall back to delayMicros() - * @warning don't call from ISR context - * @param[in] milliSeconds milliseconds to wait - */ -void delayMillis(uint32_t milliSeconds); - -/** Delay in microseconds - * Implemented using Cortex DWT counter. Blocking and non aliased. - * @param[in] microSeconds microseconds to wait - */ -void delayMicros(uint32_t microSeconds); - -/** @return the platform that Tactility currently is running on. */ -Platform getPlatform(); - -} // namespace diff --git a/TactilityCore/Include/Tactility/kernel/Platform.h b/TactilityCore/Include/Tactility/kernel/Platform.h new file mode 100644 index 00000000..2b94628c --- /dev/null +++ b/TactilityCore/Include/Tactility/kernel/Platform.h @@ -0,0 +1,14 @@ +#pragma once + +namespace tt::kernel { + +/** Recognized platform types */ +typedef enum { + PlatformEsp, + PlatformSimulator +} Platform; + +/** @return the platform that Tactility currently is running on. */ +Platform getPlatform(); + +} // namespace diff --git a/TactilityCore/Source/Check.cpp b/TactilityCore/Source/Check.cpp index 4c97d459..beb0c2aa 100644 --- a/TactilityCore/Source/Check.cpp +++ b/TactilityCore/Source/Check.cpp @@ -1,9 +1,9 @@ -#include "Tactility/Check.h" +#include -#include "Tactility/Log.h" -#include "Tactility/RtosCompatTask.h" +#include +#include -#define TAG "kernel" +constexpr auto TAG = "kernel"; static void logMemoryInfo() { #ifdef ESP_PLATFORM diff --git a/TactilityCore/Source/Dispatcher.cpp b/TactilityCore/Source/Dispatcher.cpp deleted file mode 100644 index 0836602e..00000000 --- a/TactilityCore/Source/Dispatcher.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "Tactility/Dispatcher.h" - -#include "Tactility/Check.h" -#include "Tactility/kernel/Kernel.h" - -namespace tt { - -#define TAG "dispatcher" -#define BACKPRESSURE_WARNING_COUNT ((EventBits_t)100) -#define WAIT_FLAG ((EventBits_t)1U) - -Dispatcher::~Dispatcher() { - // Wait for Mutex usage - mutex.lock(); - mutex.unlock(); -} - -bool Dispatcher::dispatch(Function function, TickType_t timeout) { - // Mutate - if (mutex.lock(timeout)) { - queue.push(std::move(function)); - if (queue.size() == BACKPRESSURE_WARNING_COUNT) { - TT_LOG_W(TAG, "Backpressure: You're not consuming fast enough (100 queued)"); - } - tt_check(mutex.unlock()); - // Signal - eventFlag.set(WAIT_FLAG); - return true; - } else { - TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED); - return false; - } -} - -uint32_t Dispatcher::consume(TickType_t timeout) { - // Wait for signal - uint32_t result = eventFlag.wait(WAIT_FLAG, EventFlag::WaitAny, timeout); - if (result & EventFlag::Error) { - return 0; - } - - eventFlag.clear(WAIT_FLAG); - - // Mutate - bool processing = true; - uint32_t consumed = 0; - do { - if (mutex.lock(10)) { - if (!queue.empty()) { - auto function = queue.front(); - queue.pop(); - consumed++; - processing = !queue.empty(); - // Don't keep lock as callback might be slow - mutex.unlock(); - function(); - } else { - processing = false; - mutex.unlock(); - } - } else { - TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED); - } - - } while (processing); - - return consumed; -} - -} // namespace diff --git a/TactilityCore/Source/DispatcherThread.cpp b/TactilityCore/Source/DispatcherThread.cpp deleted file mode 100644 index 320023cb..00000000 --- a/TactilityCore/Source/DispatcherThread.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "Tactility/DispatcherThread.h" - -namespace tt { - -DispatcherThread::DispatcherThread(const std::string& threadName, size_t threadStackSize) { - thread = std::make_unique( - threadName, - threadStackSize, - [this] { - return threadMain(); - } - ); -} - -DispatcherThread::~DispatcherThread() { - if (thread->getState() != Thread::State::Stopped) { - stop(); - } -} - -int32_t DispatcherThread::threadMain() { - do { - /** - * If this value is too high (e.g. 1 second) then the dispatcher destroys too slowly when the simulator exits. - * This causes the problems with other services doing an update (e.g. Statusbar) and calling into destroyed mutex in the global scope. - */ - dispatcher.consume(100 / portTICK_PERIOD_MS); - } while (!interruptThread); - - return 0; -} - -bool DispatcherThread::dispatch(Dispatcher::Function function, TickType_t timeout) { - return dispatcher.dispatch(function, timeout); -} - -void DispatcherThread::start() { - interruptThread = false; - thread->start(); -} - -void DispatcherThread::stop() { - interruptThread = true; - thread->join(); -} - -} \ No newline at end of file diff --git a/TactilityCore/Source/EventFlag.cpp b/TactilityCore/Source/EventFlag.cpp deleted file mode 100644 index 5b9793c2..00000000 --- a/TactilityCore/Source/EventFlag.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "Tactility/EventFlag.h" - -#include "Tactility/Check.h" -#include "Tactility/kernel/Kernel.h" - -#define TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U -#define TT_EVENT_FLAG_INVALID_BITS (~((1UL << TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U)) - -namespace tt { - -EventFlag::EventFlag() : - handle(xEventGroupCreate()) -{ - assert(!kernel::isIsr()); - tt_check(handle); -} - -EventFlag::~EventFlag() { - assert(!kernel::isIsr()); -} - -uint32_t EventFlag::set(uint32_t flags) const { - assert(handle); - assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U); - - uint32_t rflags; - BaseType_t yield; - - if (kernel::isIsr()) { - yield = pdFALSE; - if (xEventGroupSetBitsFromISR(handle.get(), (EventBits_t)flags, &yield) == pdFAIL) { - rflags = (uint32_t)ErrorResource; - } else { - rflags = flags; - portYIELD_FROM_ISR(yield); - } - } else { - rflags = xEventGroupSetBits(handle.get(), (EventBits_t)flags); - } - - /* Return event flags after setting */ - return rflags; -} - -uint32_t EventFlag::clear(uint32_t flags) const { - assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U); - - uint32_t rflags; - - if (kernel::isIsr()) { - rflags = xEventGroupGetBitsFromISR(handle.get()); - - if (xEventGroupClearBitsFromISR(handle.get(), (EventBits_t)flags) == pdFAIL) { - rflags = (uint32_t)ErrorResource; - } else { - /* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */ - /* Yield is required here otherwise clear operation might not execute in the right order. */ - /* See https://github.com/FreeRTOS/FreeRTOS-Kernel/issues/93 for more info. */ - portYIELD_FROM_ISR(pdTRUE); - } - } else { - rflags = xEventGroupClearBits(handle.get(), (EventBits_t)flags); - } - - /* Return event flags before clearing */ - return rflags; -} - -uint32_t EventFlag::get() const { - uint32_t rflags; - - if (kernel::isIsr()) { - rflags = xEventGroupGetBitsFromISR(handle.get()); - } else { - rflags = xEventGroupGetBits(handle.get()); - } - - /* Return current event flags */ - return (rflags); -} - -uint32_t EventFlag::wait( - uint32_t flags, - uint32_t options, - uint32_t timeoutTicksw -) const { - assert(!kernel::isIsr()); - assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U); - - BaseType_t wait_all; - BaseType_t exit_clear; - uint32_t rflags; - - if (options & WaitAll) { - wait_all = pdTRUE; - } else { - wait_all = pdFALSE; - } - - if (options & NoClear) { - exit_clear = pdFALSE; - } else { - exit_clear = pdTRUE; - } - - rflags = xEventGroupWaitBits( - handle.get(), - (EventBits_t)flags, - exit_clear, - wait_all, - (TickType_t)timeoutTicksw - ); - - if (options & WaitAll) { - if ((flags & rflags) != flags) { - if (timeoutTicksw > 0U) { - rflags = (uint32_t)ErrorTimeout; - } else { - rflags = (uint32_t)ErrorResource; - } - } - } else { - if ((flags & rflags) == 0U) { - if (timeoutTicksw > 0U) { - rflags = (uint32_t)ErrorTimeout; - } else { - rflags = (uint32_t)ErrorResource; - } - } - } - - return rflags; -} - -} // namespace diff --git a/TactilityCore/Source/Lock.cpp b/TactilityCore/Source/Lock.cpp deleted file mode 100644 index 0bce6712..00000000 --- a/TactilityCore/Source/Lock.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "Tactility/Lock.h" - -namespace tt { - -ScopedLock Lock::asScopedLock() const { - return ScopedLock(*this); -} - -} diff --git a/TactilityCore/Source/MessageQueue.cpp b/TactilityCore/Source/MessageQueue.cpp deleted file mode 100644 index eb1630cf..00000000 --- a/TactilityCore/Source/MessageQueue.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "Tactility/MessageQueue.h" -#include "Tactility/Check.h" -#include "Tactility/kernel/Kernel.h" - -namespace tt { - -static inline QueueHandle_t createQueue(uint32_t capacity, uint32_t messageSize) { - assert(!kernel::isIsr() && (capacity > 0U) && (messageSize > 0U)); - return xQueueCreate(capacity, messageSize); -} - -MessageQueue::MessageQueue(uint32_t capacity, uint32_t messageSize) : handle(createQueue(capacity, messageSize)) { - tt_check(handle != nullptr); -} - -MessageQueue::~MessageQueue() { - assert(!kernel::isIsr()); -} - -bool MessageQueue::put(const void* message, TickType_t timeout) { - bool result = true; - BaseType_t yield; - - if (kernel::isIsr()) { - if ((handle == nullptr) || (message == nullptr) || (timeout != 0U)) { - result = false; - } else { - yield = pdFALSE; - - if (xQueueSendToBackFromISR(handle.get(), message, &yield) != pdTRUE) { - result = false; - } else { - portYIELD_FROM_ISR(yield); - } - } - } else if ((handle == nullptr) || (message == nullptr)) { - result = false; - } else if (xQueueSendToBack(handle.get(), message, (TickType_t)timeout) != pdPASS) { - result = false; - } - - return result; -} - -bool MessageQueue::get(void* msg_ptr, TickType_t timeout) { - bool result = true; - BaseType_t yield; - - if (kernel::isIsr()) { - if ((handle == nullptr) || (msg_ptr == nullptr) || (timeout != 0U)) { - result = false; - } else { - yield = pdFALSE; - - if (xQueueReceiveFromISR(handle.get(), msg_ptr, &yield) != pdPASS) { - result = false; - } else { - portYIELD_FROM_ISR(yield); - } - } - } else { - if ((handle == nullptr) || (msg_ptr == nullptr)) { - result = false; - } else if (xQueueReceive(handle.get(), msg_ptr, (TickType_t)timeout) != pdPASS) { - result = false; - } - } - - return result; -} - -uint32_t MessageQueue::getCapacity() const { - auto* mq = (StaticQueue_t*)(handle.get()); - if (mq == nullptr) { - return 0U; - } else { - return mq->uxDummy4[1]; - } -} - -uint32_t MessageQueue::getMessageSize() const { - auto* mq = (StaticQueue_t*)(handle.get()); - if (mq == nullptr) { - return 0U; - } else { - return mq->uxDummy4[2]; - } -} - -uint32_t MessageQueue::getCount() const { - UBaseType_t count; - - if (handle == nullptr) { - count = 0U; - } else if (kernel::isIsr()) { - count = uxQueueMessagesWaitingFromISR(handle.get()); - } else { - count = uxQueueMessagesWaiting(handle.get()); - } - - /* Return number of queued messages */ - return (uint32_t)count; -} - -uint32_t MessageQueue::getSpace() const { - auto* mq = (StaticQueue_t*)(handle.get()); - uint32_t space; - uint32_t isrm; - - if (mq == nullptr) { - space = 0U; - } else if (kernel::isIsr()) { - isrm = taskENTER_CRITICAL_FROM_ISR(); - - /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ - space = mq->uxDummy4[1] - mq->uxDummy4[0]; - - taskEXIT_CRITICAL_FROM_ISR(isrm); - } else { - space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)mq); - } - - return space; -} - -bool MessageQueue::reset() { - tt_check(!kernel::isIsr()); - if (handle == nullptr) { - return false; - } else { - xQueueReset(handle.get()); - return true; - } -} - -} // namespace diff --git a/TactilityCore/Source/Semaphore.cpp b/TactilityCore/Source/Semaphore.cpp deleted file mode 100644 index c9da839f..00000000 --- a/TactilityCore/Source/Semaphore.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "Tactility/Semaphore.h" -#include "Tactility/Check.h" -#include "Tactility/CoreDefines.h" - -namespace tt { - -static inline QueueHandle_t createHandle(uint32_t maxCount, uint32_t initialCount) { - assert((maxCount > 0U) && (initialCount <= maxCount)); - - if (maxCount == 1U) { - auto handle = xSemaphoreCreateBinary(); - if ((handle != nullptr) && (initialCount != 0U)) { - if (xSemaphoreGive(handle) != pdPASS) { - vSemaphoreDelete(handle); - handle = nullptr; - } - } - return handle; - } else { - return xSemaphoreCreateCounting(maxCount, initialCount); - } -} - -Semaphore::Semaphore(uint32_t maxAvailable, uint32_t initialAvailable) : handle(createHandle(maxAvailable, initialAvailable)) { - assert(!kernel::isIsr()); - tt_check(handle != nullptr); -} - -Semaphore::~Semaphore() { - assert(!kernel::isIsr()); -} - -bool Semaphore::acquire(TickType_t timeout) const { - if (kernel::isIsr()) { - if (timeout != 0U) { - return false; - } else { - BaseType_t yield = pdFALSE; - - if (xSemaphoreTakeFromISR(handle.get(), &yield) != pdPASS) { - return false; - } else { - portYIELD_FROM_ISR(yield); - return true; - } - } - } else { - return xSemaphoreTake(handle.get(), timeout) == pdPASS; - } -} - -bool Semaphore::release() const { - if (kernel::isIsr()) { - BaseType_t yield = pdFALSE; - if (xSemaphoreGiveFromISR(handle.get(), &yield) != pdTRUE) { - return false; - } else { - portYIELD_FROM_ISR(yield); - return true; - } - } else { - return xSemaphoreGive(handle.get()) == pdPASS; - } -} - -uint32_t Semaphore::getAvailable() const { - if (kernel::isIsr()) { - // TODO: uxSemaphoreGetCountFromISR is not supported on esp-idf 5.1.2 - perhaps later on? -#ifdef uxSemaphoreGetCountFromISR - return uxSemaphoreGetCountFromISR(handle.get()); -#else - return uxQueueMessagesWaitingFromISR(handle.get()); -#endif - } else { - return uxSemaphoreGetCount(handle.get()); - } -} - -} // namespace diff --git a/TactilityCore/Source/StreamBuffer.cpp b/TactilityCore/Source/StreamBuffer.cpp deleted file mode 100644 index 0c1372af..00000000 --- a/TactilityCore/Source/StreamBuffer.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "Tactility/StreamBuffer.h" - -#include "Tactility/Check.h" -#include "Tactility/kernel/Kernel.h" - -namespace tt { - -static StreamBufferHandle_t createStreamBuffer(size_t size, size_t triggerLevel) { - assert(size != 0); - return xStreamBufferCreate(size, triggerLevel); -} - -StreamBuffer::StreamBuffer(size_t size, size_t triggerLevel) : handle(createStreamBuffer(size, triggerLevel)) { - tt_check(handle); -}; - -bool StreamBuffer::setTriggerLevel(size_t triggerLevel) const { - return xStreamBufferSetTriggerLevel(handle.get(), triggerLevel) == pdTRUE; -}; - -size_t StreamBuffer::send( - const void* data, - size_t length, - uint32_t timeout -) const { - if (kernel::isIsr()) { - BaseType_t yield; - size_t result = xStreamBufferSendFromISR(handle.get(), data, length, &yield); - portYIELD_FROM_ISR(yield); - return result; - } else { - return xStreamBufferSend(handle.get(), data, length, timeout); - } -}; - -size_t StreamBuffer::receive( - void* data, - size_t length, - uint32_t timeout -) const { - if (kernel::isIsr()) { - BaseType_t yield; - size_t result = xStreamBufferReceiveFromISR(handle.get(), data, length, &yield); - portYIELD_FROM_ISR(yield); - return result; - } else { - return xStreamBufferReceive(handle.get(), data, length, timeout); - } -} - -size_t StreamBuffer::getAvailableReadBytes() const { - return xStreamBufferBytesAvailable(handle.get()); -}; - -size_t StreamBuffer::getAvailableWriteBytes() const { - return xStreamBufferSpacesAvailable(handle.get()); -}; - -bool StreamBuffer::isFull() const { - return xStreamBufferIsFull(handle.get()) == pdTRUE; -}; - -bool StreamBuffer::isEmpty() const { - return xStreamBufferIsEmpty(handle.get()) == pdTRUE; -}; - -bool StreamBuffer::reset() const { - return xStreamBufferReset(handle.get()) == pdPASS; -} - -} // namespace diff --git a/TactilityCore/Source/Thread.cpp b/TactilityCore/Source/Thread.cpp deleted file mode 100644 index 73f3d13d..00000000 --- a/TactilityCore/Source/Thread.cpp +++ /dev/null @@ -1,386 +0,0 @@ -#include "Tactility/Thread.h" - -#include "Tactility/Check.h" -#include "Tactility/CoreDefines.h" -#include "Tactility/EventFlag.h" -#include "Tactility/kernel/Kernel.h" -#include "Tactility/Log.h" -#include "Tactility/TactilityCoreConfig.h" - -#include - -namespace tt { - -#define TAG "Thread" - -#define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers - -// Limits -#define MAX_BITS_TASK_NOTIFY 31U -#define MAX_BITS_EVENT_GROUPS 24U - -#define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U)) -#define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U)) - -static_assert(static_cast(Thread::Priority::Critical) <= TT_CONFIG_THREAD_MAX_PRIORITIES, "highest thread priority is higher than max priority"); -static_assert(TT_CONFIG_THREAD_MAX_PRIORITIES <= configMAX_PRIORITIES, "highest tactility priority is higher than max FreeRTOS priority"); - -void Thread::setState(Thread::State newState) { - state = newState; - if (stateCallback) { - stateCallback(state, stateCallbackContext); - } -} - -static_assert(configSUPPORT_DYNAMIC_ALLOCATION == 1); - -/** Catch threads that are trying to exit wrong way */ -__attribute__((__noreturn__)) void threadCatch() { //-V1082 - // If you're here it means you're probably doing something wrong with critical sections or with scheduler state - asm volatile("nop"); - tt_crash(); - __builtin_unreachable(); -} - -void Thread::mainBody(void* context) { - assert(context != nullptr); - auto* thread = static_cast(context); - - // Store thread data instance to thread local storage - assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr); - vTaskSetThreadLocalStoragePointer(nullptr, 0, thread); - - TT_LOG_I(TAG, "Starting %s", thread->name.c_str()); - assert(thread->state == Thread::State::Starting); - thread->setState(Thread::State::Running); - thread->callbackResult = thread->mainFunction(); - assert(thread->state == Thread::State::Running); - - thread->setState(Thread::State::Stopped); - TT_LOG_I(TAG, "Stopped %s", thread->name.c_str()); - - vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr); - thread->taskHandle = nullptr; - - vTaskDelete(nullptr); - threadCatch(); -} - -Thread::Thread( - std::string name, - configSTACK_DEPTH_TYPE stackSize, - MainFunction function, - portBASE_TYPE affinity -) : - mainFunction(function), - name(std::move(name)), - stackSize(stackSize), - affinity(affinity) -{} - -Thread::~Thread() { - // Ensure that use join before free - assert(state == State::Stopped); - assert(taskHandle == nullptr); -} - -void Thread::setName(std::string newName) { - assert(state == State::Stopped); - name = std::move(newName); -} - -void Thread::setStackSize(size_t newStackSize) { - assert(state == State::Stopped); - assert(stackSize % 4 == 0); - stackSize = newStackSize; -} - -void Thread::setAffinity(portBASE_TYPE newAffinity) { - assert(state == State::Stopped); - affinity = newAffinity; -} - -void Thread::setCallback(Callback callback, _Nullable void* callbackContext) { - assert(state == State::Stopped); - mainFunction = [callback, callbackContext] { - return callback(callbackContext); - }; -} - -void Thread::setMainFunction(MainFunction function) { - assert(state == State::Stopped); - mainFunction = function; -} - -void Thread::setPriority(Priority newPriority) { - assert(state == State::Stopped); - priority = newPriority; -} - - -void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) { - assert(state == State::Stopped); - stateCallback = callback; - stateCallbackContext = callbackContext; -} - -Thread::State Thread::getState() const { - return state; -} - -void Thread::start() { - assert(mainFunction); - assert(state == State::Stopped); - assert(stackSize > 0U && stackSize < (UINT16_MAX * sizeof(StackType_t))); - - setState(State::Starting); - - uint32_t stack_depth = stackSize / sizeof(StackType_t); - - BaseType_t result; - if (affinity != -1) { -#ifdef ESP_PLATFORM - result = xTaskCreatePinnedToCore( - mainBody, - name.c_str(), - stack_depth, - this, - static_cast(priority), - &taskHandle, - affinity - ); -#else - TT_LOG_W(TAG, "Pinned tasks are not supported by current FreeRTOS platform - creating regular one"); - result = xTaskCreate( - mainBody, - name.c_str(), - stack_depth, - this, - static_cast(priority), - &taskHandle - ); -#endif - } else { - result = xTaskCreate( - mainBody, - name.c_str(), - stack_depth, - this, - static_cast(priority), - &taskHandle - ); - } - - tt_check(result == pdPASS); - tt_check(state == State::Stopped || taskHandle); -} - -bool Thread::join(TickType_t timeout, TickType_t pollInterval) { - tt_check(getCurrent() != this); - - // !!! IMPORTANT NOTICE !!! - // - // If your thread exited, but your app stuck here: some other thread uses - // all cpu time, which delays kernel from releasing task handle - TickType_t start_ticks = kernel::getTicks(); - while (taskHandle) { - kernel::delayTicks(pollInterval); - if ((kernel::getTicks() - start_ticks) > timeout) { - return false; - } - } - - return true; -} - -ThreadId Thread::getId() const { - return taskHandle; -} - -int32_t Thread::getReturnCode() const { - assert(state == State::Stopped); - return callbackResult; -} - -Thread* Thread::getCurrent() { - return static_cast(pvTaskGetThreadLocalStoragePointer(nullptr, 0)); -} - -uint32_t Thread::setFlags(ThreadId threadId, uint32_t flags) { - auto hTask = (TaskHandle_t)threadId; - uint32_t rflags; - BaseType_t yield; - - if ((hTask == nullptr) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) { - rflags = (uint32_t)EventFlag::ErrorParameter; - } else { - rflags = (uint32_t)EventFlag::Error; - - if (kernel::isIsr()) { - yield = pdFALSE; - - (void)xTaskNotifyIndexedFromISR(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits, &yield); - (void)xTaskNotifyAndQueryIndexedFromISR( - hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags, nullptr - ); - - portYIELD_FROM_ISR(yield); - } else { - (void)xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits); - (void)xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags); - } - } - /* Return flags after setting */ - return (rflags); -} - -uint32_t Thread::clearFlags(uint32_t flags) { - TaskHandle_t hTask; - uint32_t rflags, cflags; - - if (kernel::isIsr()) { - rflags = (uint32_t)EventFlag::ErrorISR; - } else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { - rflags = (uint32_t)EventFlag::ErrorParameter; - } else { - hTask = xTaskGetCurrentTaskHandle(); - - if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &cflags) == - pdPASS) { - rflags = cflags; - cflags &= ~flags; - - if (xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) != - pdPASS) { - rflags = (uint32_t)EventFlag::Error; - } - } else { - rflags = (uint32_t)EventFlag::Error; - } - } - - /* Return flags before clearing */ - return (rflags); -} - -uint32_t Thread::getFlags() { - TaskHandle_t hTask; - uint32_t rflags; - - if (kernel::isIsr()) { - rflags = (uint32_t)EventFlag::ErrorISR; - } else { - hTask = xTaskGetCurrentTaskHandle(); - - if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) != - pdPASS) { - rflags = (uint32_t)EventFlag::Error; - } - } - - return (rflags); -} - -uint32_t Thread::awaitFlags(uint32_t flags, uint32_t options, uint32_t timeout) { - uint32_t rflags, nval; - uint32_t clear; - TickType_t t0, td, tout; - BaseType_t rval; - - if (kernel::isIsr()) { - rflags = (uint32_t)EventFlag::ErrorISR; - } else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { - rflags = (uint32_t)EventFlag::ErrorParameter; - } else { - if ((options & EventFlag::NoClear) == EventFlag::NoClear) { - clear = 0U; - } else { - clear = flags; - } - - rflags = 0U; - tout = timeout; - - t0 = xTaskGetTickCount(); - do { - rval = xTaskNotifyWaitIndexed(THREAD_NOTIFY_INDEX, 0, clear, &nval, tout); - - if (rval == pdPASS) { - rflags &= flags; - rflags |= nval; - - if ((options & EventFlag::WaitAll) == EventFlag::WaitAll) { - if ((flags & rflags) == flags) { - break; - } else { - if (timeout == 0U) { - rflags = (uint32_t)EventFlag::ErrorResource; - break; - } - } - } else { - if ((flags & rflags) != 0) { - break; - } else { - if (timeout == 0U) { - rflags = (uint32_t)EventFlag::ErrorResource; - break; - } - } - } - - /* Update timeout */ - td = xTaskGetTickCount() - t0; - - if (td > tout) { - tout = 0; - } else { - tout -= td; - } - } else { - if (timeout == 0) { - rflags = (uint32_t)EventFlag::ErrorResource; - } else { - rflags = (uint32_t)EventFlag::ErrorTimeout; - } - } - } while (rval != pdFAIL); - } - - /* Return flags before clearing */ - return (rflags); -} - -uint32_t Thread::getStackSpace(ThreadId threadId) { - auto hTask = (TaskHandle_t)threadId; - uint32_t sz; - - if (kernel::isIsr() || (hTask == nullptr)) { - sz = 0U; - } else { - sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t)); - } - - return (sz); -} - -void Thread::suspend(ThreadId threadId) { - auto hTask = (TaskHandle_t)threadId; - vTaskSuspend(hTask); -} - -void Thread::resume(ThreadId threadId) { - auto hTask = (TaskHandle_t)threadId; - if (kernel::isIsr()) { - xTaskResumeFromISR(hTask); - } else { - vTaskResume(hTask); - } -} - -bool Thread::isSuspended(ThreadId threadId) { - auto hTask = (TaskHandle_t)threadId; - return eTaskGetState(hTask) == eSuspended; -} - -} // namespace diff --git a/TactilityCore/Source/Timer.cpp b/TactilityCore/Source/Timer.cpp deleted file mode 100644 index 7478ee0c..00000000 --- a/TactilityCore/Source/Timer.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "Tactility/Timer.h" - -#include "Tactility/Check.h" -#include "Tactility/RtosCompat.h" -#include "Tactility/kernel/Kernel.h" - -namespace tt { - -void Timer::onCallback(TimerHandle_t hTimer) { - auto* timer = static_cast(pvTimerGetTimerID(hTimer)); - if (timer != nullptr) { - timer->callback(); - } -} - -static TimerHandle_t createTimer(Timer::Type type, void* timerId, TimerCallbackFunction_t callback) { - assert(timerId != nullptr); - assert(callback != nullptr); - - UBaseType_t reload; - if (type == Timer::Type::Once) { - reload = pdFALSE; - } else { - reload = pdTRUE; - } - - return xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, timerId, callback); -} - -Timer::Timer(Type type, Callback callback) : - callback(callback), - handle(createTimer(type, this, onCallback)) -{ - assert(!kernel::isIsr()); - assert(handle != nullptr); -} - -Timer::~Timer() { - assert(!kernel::isIsr()); -} - -bool Timer::start(TickType_t interval) { - assert(!kernel::isIsr()); - assert(interval < portMAX_DELAY); - return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS; -} - -bool Timer::restart(TickType_t interval) { - assert(!kernel::isIsr()); - assert(interval < portMAX_DELAY); - return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS && - xTimerReset(handle.get(), portMAX_DELAY) == pdPASS; -} - -bool Timer::stop() { - assert(!kernel::isIsr()); - return xTimerStop(handle.get(), portMAX_DELAY) == pdPASS; -} - -bool Timer::isRunning() { - assert(!kernel::isIsr()); - return xTimerIsTimerActive(handle.get()) == pdTRUE; -} - -TickType_t Timer::getExpireTime() { - assert(!kernel::isIsr()); - return xTimerGetExpiryTime(handle.get()); -} - -bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) { - if (kernel::isIsr()) { - assert(timeout == 0); - return xTimerPendFunctionCallFromISR(callback, callbackContext, callbackArg, nullptr) == pdPASS; - } else { - return xTimerPendFunctionCall(callback, callbackContext, callbackArg, timeout) == pdPASS; - } -} - -void Timer::setThreadPriority(Thread::Priority priority) { - assert(!kernel::isIsr()); - - TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); - assert(task_handle); // Don't call this method before timer task start - - vTaskPrioritySet(task_handle, static_cast(priority)); -} - -} // namespace diff --git a/TactilityCore/Source/file/File.cpp b/TactilityCore/Source/file/File.cpp index d9a78def..20ae6617 100644 --- a/TactilityCore/Source/file/File.cpp +++ b/TactilityCore/Source/file/File.cpp @@ -15,7 +15,7 @@ constexpr auto* TAG = "file"; class NoLock final : public Lock { bool lock(TickType_t timeout) const override { return true; } - bool unlock() const override { return true; } + void unlock() const override { /* NO-OP */ } }; static std::shared_ptr noLock = std::make_shared(); diff --git a/TactilityCore/Source/kernel/Kernel.cpp b/TactilityCore/Source/kernel/Kernel.cpp deleted file mode 100644 index 822473bd..00000000 --- a/TactilityCore/Source/kernel/Kernel.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "Tactility/kernel/Kernel.h" -#include "Tactility/CoreDefines.h" -#include "Tactility/RtosCompatTask.h" - -#ifdef ESP_PLATFORM -#include "rom/ets_sys.h" -#else -#include -#include -#endif - -namespace tt::kernel { - -bool isRunning() { - return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING; -} - -bool lock() { - assert(!kernel::isIsr()); - - int32_t lock; - - switch (xTaskGetSchedulerState()) { - // Already suspended - case taskSCHEDULER_SUSPENDED: - return true; - - case taskSCHEDULER_RUNNING: - vTaskSuspendAll(); - return true; - - case taskSCHEDULER_NOT_STARTED: - default: - return false; - } - - /* Return previous lock state */ - return (lock); -} - -bool unlock() { - assert(!kernel::isIsr()); - - switch (xTaskGetSchedulerState()) { - case taskSCHEDULER_SUSPENDED: - if (xTaskResumeAll() != pdTRUE) { - if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) { - return false; - } - } - return true; - - case taskSCHEDULER_RUNNING: - return true; - - case taskSCHEDULER_NOT_STARTED: - default: - return false; - } -} - -bool restoreLock(bool lock) { - assert(!kernel::isIsr()); - - switch (xTaskGetSchedulerState()) { - case taskSCHEDULER_SUSPENDED: - case taskSCHEDULER_RUNNING: - if (lock) { - vTaskSuspendAll(); - return true; - } else { - if (xTaskResumeAll() != pdTRUE) { - if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { - return false; - } - } - return true; - } - - case taskSCHEDULER_NOT_STARTED: - default: - return false; - } -} - -uint32_t getTickFrequency() { - /* Return frequency in hertz */ - return (configTICK_RATE_HZ); -} - -void delayTicks(TickType_t ticks) { - assert(!kernel::isIsr()); - if (ticks == 0U) { - taskYIELD(); - } else { - vTaskDelay(ticks); - } -} - -bool delayUntilTick(TickType_t tick) { - assert(!kernel::isIsr()); - - TickType_t tcnt, delay; - - tcnt = xTaskGetTickCount(); - - /* Determine remaining number of tick to delay */ - delay = (TickType_t)tick - tcnt; - - /* Check if target tick has not expired */ - if ((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) { - if (xTaskDelayUntil(&tcnt, delay) == pdPASS) { - return true; - } - } - - return false; -} - -TickType_t getTicks() { - if (kernel::isIsr() != 0U) { - return xTaskGetTickCountFromISR(); - } else { - return xTaskGetTickCount(); - } -} - -TickType_t millisToTicks(uint32_t milliseconds) { -#if configTICK_RATE_HZ == 1000 - return (TickType_t)milliseconds; -#else - return (TickType_t)((float)configTICK_RATE_HZ) / 1000.0f * (float)milliseconds; -#endif -} - -void delayMillis(uint32_t milliseconds) { - if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { - if (milliseconds > 0 && milliseconds < portMAX_DELAY - 1) { - milliseconds += 1; - } -#if configTICK_RATE_HZ_RAW == 1000 - tt_delay_tick(milliseconds); -#else - delayTicks(kernel::millisToTicks(milliseconds)); -#endif - } else if (milliseconds > 0) { - kernel::delayMicros(milliseconds * 1000); - } -} - -void delayMicros(uint32_t microseconds) { -#ifdef ESP_PLATFORM - ets_delay_us(microseconds); -#else - usleep(microseconds); -#endif -} - -Platform getPlatform() { -#ifdef ESP_PLATFORM - return PlatformEsp; -#else - return PlatformSimulator; -#endif -} - -} // namespace diff --git a/TactilityCore/Source/kernel/Platform.cpp b/TactilityCore/Source/kernel/Platform.cpp new file mode 100644 index 00000000..fd1cefae --- /dev/null +++ b/TactilityCore/Source/kernel/Platform.cpp @@ -0,0 +1,13 @@ +#include "Tactility/kernel/Platform.h" + +namespace tt::kernel { + +Platform getPlatform() { +#ifdef ESP_PLATFORM + return PlatformEsp; +#else + return PlatformSimulator; +#endif +} + +} // namespace diff --git a/TactilityCore/Source/kernel/critical/Critical.cpp b/TactilityCore/Source/kernel/critical/Critical.cpp index 11bff966..3e6a97bb 100644 --- a/TactilityCore/Source/kernel/critical/Critical.cpp +++ b/TactilityCore/Source/kernel/critical/Critical.cpp @@ -1,7 +1,7 @@ -#include "Tactility/kernel/critical/Critical.h" +#include -#include "Tactility/RtosCompatTask.h" -#include "Tactility/kernel/Kernel.h" +#include +#include #ifdef ESP_PLATFORM static portMUX_TYPE critical_mutex; @@ -15,7 +15,7 @@ namespace tt::kernel::critical { CriticalInfo enter() { CriticalInfo info = { .isrm = 0, - .fromIsr = kernel::isIsr(), + .fromIsr = (xPortInIsrContext() == pdTRUE), .kernelRunning = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) }; diff --git a/TactilityFreeRtos/CMakeLists.txt b/TactilityFreeRtos/CMakeLists.txt new file mode 100644 index 00000000..072cb4f9 --- /dev/null +++ b/TactilityFreeRtos/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.20) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (DEFINED ENV{ESP_IDF_VERSION}) + + idf_component_register( + INCLUDE_DIRS "Include/" + REQUIRES esp_timer + ) + +else() + + add_library(TactilityFreeRtos INTERFACE) + + target_include_directories(TactilityFreeRtos + INTERFACE Include/ + ) + + target_link_libraries(TactilityFreeRtos + INTERFACE freertos_kernel + ) +endif() \ No newline at end of file diff --git a/TactilityFreeRtos/Include/Tactility/Dispatcher.h b/TactilityFreeRtos/Include/Tactility/Dispatcher.h new file mode 100644 index 00000000..071c39dd --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/Dispatcher.h @@ -0,0 +1,121 @@ +/** +* Dispatcher is a thread-safe code execution queue. +*/ +#pragma once + +#include "EventGroup.h" +#include "Mutex.h" +#include "kernel/Kernel.h" + +#ifdef ESP_PLATFORM +#include +#endif + +#include +#include +#include + +namespace tt { + +/** + * A thread-safe way to defer code execution. + * Generally, one task would dispatch the execution, + * while the other thread consumes and executes the work. + */ +class Dispatcher final { + + static constexpr auto TAG = "Dispatcher"; + static constexpr EventBits_t BACKPRESSURE_WARNING_COUNT = 100U; + static constexpr EventBits_t WAIT_FLAG = 1U; + +public: + + typedef std::function Function; + +private: + + Mutex mutex; + std::queue queue = {}; + EventGroup eventFlag; + +public: + + explicit Dispatcher() = default; + + ~Dispatcher() { + mutex.lock(); + mutex.unlock(); + } + + /** + * Queue a function to be consumed elsewhere. + * @param[in] function the function to execute elsewhere + * @param[in] timeout lock acquisition timeout + * @return true if dispatching was successful (timeout not reached) + */ + bool dispatch(Function function, TickType_t timeout = kernel::MAX_TICKS) { + // Mutate + if (!mutex.lock(timeout)) { +#ifdef ESP_PLATFORM + ESP_LOGE(TAG, "Mutex acquisition timeout"); +#endif + return false; + } + + queue.push(std::move(function)); + if (queue.size() == BACKPRESSURE_WARNING_COUNT) { +#ifdef ESP_PLATFORM + ESP_LOGW(TAG, "Backpressure: You're not consuming fast enough (100 queued)"); +#endif + } + mutex.unlock(); + if (!eventFlag.set(WAIT_FLAG)) { +#ifdef ESP_PLATFORM + ESP_LOGE(TAG, "Failed to set flag"); +#endif + } + return true; + } + + /** + * Consume 1 or more dispatched function (if any) until the queue is empty. + * @warning The timeout is only the wait time before consuming the message! It is not a limit to the total execution time when calling this method. + * @param[in] timeout the ticks to wait for a message + * @return the amount of messages that were consumed + */ + uint32_t consume(TickType_t timeout = kernel::MAX_TICKS) { + // Wait for signal + if (!eventFlag.wait(WAIT_FLAG, false, true, timeout)) { + return 0; + } + + // Mutate + bool processing = true; + uint32_t consumed = 0; + do { + if (mutex.lock(10)) { + if (!queue.empty()) { + auto function = queue.front(); + queue.pop(); + consumed++; + processing = !queue.empty(); + // Don't keep lock as callback might be slow + mutex.unlock(); + function(); + } else { + processing = false; + mutex.unlock(); + } + } else { +#ifdef ESP_PLATFORM + ESP_LOGW(TAG, "Mutex acquisition timeout"); +#endif + } + + } while (processing); + + return consumed; + } +}; + +} // namespace diff --git a/TactilityFreeRtos/Include/Tactility/DispatcherThread.h b/TactilityFreeRtos/Include/Tactility/DispatcherThread.h new file mode 100644 index 00000000..9e66a814 --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/DispatcherThread.h @@ -0,0 +1,68 @@ +#pragma once + +#include "Dispatcher.h" +#include "Thread.h" + +namespace tt { + +/** Starts a Thread to process dispatched messages */ +class DispatcherThread final { + + Dispatcher dispatcher; + std::unique_ptr thread; + bool interruptThread = true; + + int32_t threadMain() { + do { + /** + * If this value is too high (e.g. 1 second) then the dispatcher destroys too slowly when the simulator exits. + * This causes the problems with other services doing an update (e.g. Statusbar) and calling into destroyed mutex in the global scope. + */ + dispatcher.consume(100 / portTICK_PERIOD_MS); + } while (!interruptThread); + + return 0; + } + +public: + + explicit DispatcherThread(const std::string& threadName, size_t threadStackSize = 4096) { + thread = std::make_unique( + threadName, + threadStackSize, + [this] { + return threadMain(); + } + ); + } + + ~DispatcherThread() { + if (thread->getState() != Thread::State::Stopped) { + stop(); + } + } + + /** + * Dispatch a message. + */ + bool dispatch(const Dispatcher::Function& function, TickType_t timeout = kernel::MAX_TICKS) { + return dispatcher.dispatch(function, timeout); + } + + /** Start the thread (blocking). */ + void start() { + interruptThread = false; + thread->start(); + } + + /** Stop the thread (blocking). */ + void stop() { + interruptThread = true; + thread->join(); + } + + /** @return true of the thread is started */ + bool isStarted() const { return thread != nullptr && !interruptThread; } +}; + +} \ No newline at end of file diff --git a/TactilityFreeRtos/Include/Tactility/EventGroup.h b/TactilityFreeRtos/Include/Tactility/EventGroup.h new file mode 100644 index 00000000..3cce00ba --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/EventGroup.h @@ -0,0 +1,165 @@ +#pragma once + +#include "freertoscompat/EventGroups.h" +#include "freertoscompat/PortCompat.h" +#include "kernel/Kernel.h" + +#include +#include + +namespace tt { + +/** Wrapper for FreeRTOS xEventGroup and related code */ +class EventGroup final { + + struct EventGroupHandleDeleter { + static void operator()(EventGroupHandle_t handleToDelete) { + vEventGroupDelete(handleToDelete); + } + }; + + std::unique_ptr, EventGroupHandleDeleter> handle = std::unique_ptr, EventGroupHandleDeleter>(xEventGroupCreate()); + +public: + + EventGroup() { + assert(xPortInIsrContext() == pdFALSE); + assert(handle != nullptr); + } + + ~EventGroup() { + assert(xPortInIsrContext() == pdFALSE); + } + + enum class Error { + Unknown, + Timeout, + Resource, + Parameter, + IsrStatus + }; + + /** + * Set the flags. + * @param[in] flags the flags to set + * @param[out] outFlags optional resulting flags: this is set when the return value is true + * @param[out] outError optional error output: this is set when the return value is false + * @return true on success + */ + bool set(uint32_t flags, uint32_t* outFlags = nullptr, Error* outError = nullptr) const { + assert(handle); + if (xPortInIsrContext() == pdTRUE) { + uint32_t result; + BaseType_t yield = pdFALSE; + if (xEventGroupSetBitsFromISR(handle.get(), flags, &yield) == pdFAIL) { + if (outError != nullptr) { + *outError = Error::Resource; + } + return false; + } else { + if (outFlags != nullptr) { + *outFlags = flags; + } + portYIELD_FROM_ISR(yield); + return true; + } + } else { + auto result = xEventGroupSetBits(handle.get(), flags); + if (outFlags != nullptr) { + *outFlags = result; + } + return true; + } + } + + /** + * Clear flags + * @param[in] flags the flags to clear + * @param[out] outFlags optional resulting flags: this is set when the return value is true + * @param[out] outError optional error output: this is set when the return value is false + * @return true on success + */ + uint32_t clear(uint32_t flags, uint32_t* outFlags = nullptr, Error* outError = nullptr) const { + if (xPortInIsrContext() == pdTRUE) { + uint32_t result = xEventGroupGetBitsFromISR(handle.get()); + if (xEventGroupClearBitsFromISR(handle.get(), flags) == pdFAIL) { + if (outError != nullptr) { + *outError = Error::Resource; + } + return false; + } else { + if (outFlags != nullptr) { + *outFlags = result; + } + return true; + } + } else { + auto result = xEventGroupClearBits(handle.get(), flags); + if (outFlags != nullptr) { + *outFlags = result; + } + return true; + } + } + + /** + * @return the current flags + */ + uint32_t get() const { + if (xPortInIsrContext() == pdTRUE) { + return xEventGroupGetBitsFromISR(handle.get()); + } else { + return xEventGroupGetBits(handle.get()); + } + } + + /** + * Wait for flags to be set + * @param[in] flags the flags to await + * @param[in] awaitAll If true, await for all bits to be set. Otherwise, await for any. + * @param[in] clearOnExit If true, clears all the bits on exit, otherwise don't clear. + * @param[in] timeout the maximum amount of ticks to wait for flags to be set + * @param[out] outFlags optional resulting flags: this is set when the return value is true + * @param[out] outError optional error output: this is set when the return value is false + */ + bool wait( + uint32_t flags, + bool awaitAll = false, + bool clearOnExit = true, + TickType_t timeout = kernel::MAX_TICKS, + uint32_t* outFlags = nullptr, + Error* outError = nullptr + ) const { + assert(xPortInIsrContext() == pdFALSE); + + uint32_t result_flags = xEventGroupWaitBits( + handle.get(), + flags, + clearOnExit ? pdTRUE : pdFALSE, + awaitAll ? pdTRUE : pdFALSE, + timeout + ); + + auto invalid_flags = awaitAll + ? ((flags & result_flags) != flags) // await all + : ((flags & result_flags) == 0U); // await any + if (invalid_flags) { + if (outError != nullptr) { + if (timeout > 0U) { // assume time-out + *outError = Error::Timeout; + } else { + *outError = Error::Resource; + } + } + return false; + } + + if (outFlags != nullptr) { + *outFlags = result_flags; + } + + return true; + } +}; + +} // namespace diff --git a/TactilityCore/Include/Tactility/Lock.h b/TactilityFreeRtos/Include/Tactility/Lock.h similarity index 74% rename from TactilityCore/Include/Tactility/Lock.h rename to TactilityFreeRtos/Include/Tactility/Lock.h index 983cbe28..46eaafb9 100644 --- a/TactilityCore/Include/Tactility/Lock.h +++ b/TactilityFreeRtos/Include/Tactility/Lock.h @@ -1,9 +1,10 @@ #pragma once -#include "Check.h" -#include "RtosCompat.h" -#include +#include "kernel/Kernel.h" + +#include #include +#include namespace tt { @@ -18,9 +19,9 @@ public: virtual bool lock(TickType_t timeout) const = 0; - bool lock() const { return lock(portMAX_DELAY); } + bool lock() const { return lock(kernel::MAX_TICKS); } - virtual bool unlock() const = 0; + virtual void unlock() const = 0; void withLock(TickType_t timeout, const std::function& onLockAcquired) const { if (lock(timeout)) { @@ -38,11 +39,11 @@ public: } } - void withLock(const std::function& onLockAcquired) const { withLock(portMAX_DELAY, onLockAcquired); } + void withLock(const std::function& onLockAcquired) const { withLock(kernel::MAX_TICKS, onLockAcquired); } - void withLock(const std::function& onLockAcquired, const std::function& onLockFailed) const { withLock(portMAX_DELAY, onLockAcquired, onLockFailed); } + void withLock(const std::function& onLockAcquired, const std::function& onLockFailed) const { withLock(kernel::MAX_TICKS, onLockAcquired, onLockFailed); } - ScopedLock asScopedLock() const; + ScopedLock asScopedLock() const; }; /** @@ -70,9 +71,13 @@ public: return lockable.lock(timeout); } - bool unlock() const override { - return lockable.unlock(); + void unlock() const override { + lockable.unlock(); } }; +inline ScopedLock Lock::asScopedLock() const { + return ScopedLock(*this); +} + } diff --git a/TactilityFreeRtos/Include/Tactility/MessageQueue.h b/TactilityFreeRtos/Include/Tactility/MessageQueue.h new file mode 100644 index 00000000..54fb020c --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/MessageQueue.h @@ -0,0 +1,131 @@ +/** + * MessageQueue is a wrapper for FreeRTOS xQueue functionality. + * There is no additional thread-safety on top of the xQueue functionality, + * so make sure you create a lock if needed. + */ +#pragma once + +#include +#include + +#include "freertoscompat/PortCompat.h" +#include "freertoscompat/Queue.h" + +namespace tt { + +/** + * Wraps xQueue functionality. + * Calls can be done from ISR context unless otherwise specified. + */ +class MessageQueue final { + + static QueueHandle_t createQueue(uint32_t capacity, uint32_t messageSize) { + assert(capacity > 0U); + assert(messageSize > 0U); + return xQueueCreate(capacity, messageSize); + } + + struct QueueHandleDeleter { + static void operator()(QueueHandle_t handleToDelete) { + vQueueDelete(handleToDelete); + } + }; + + std::unique_ptr, QueueHandleDeleter> handle; + +public: + /** + * Allocate message queue + * @param[in] capacity Maximum messages in queue + * @param[in] messageSize The size in bytes of a single message + */ + MessageQueue(uint32_t capacity, uint32_t messageSize) : handle(createQueue(capacity, messageSize)) { + assert(handle != nullptr); + assert(xPortInIsrContext() == pdFALSE); + } + + /** + * @warning Don't call this from ISR context + */ + ~MessageQueue() { + assert(xPortInIsrContext() == pdFALSE); + } + + /** + * Post a message to the queue. + * The message is queued by copy, not by reference. + * @param[in] message A pointer to a message. The message will be copied into a buffer. + * @param[in] timeout + * @return success result + */ + bool put(const void* message, TickType_t timeout) const { + assert(handle != nullptr); + assert(message != nullptr); + + if (xPortInIsrContext() == pdTRUE) { + if (timeout != 0U) { + return false; + } + + BaseType_t yield = pdFALSE; + if (xQueueSendToBackFromISR(handle.get(), message, &yield) != pdTRUE) { + return false; + } + + portYIELD_FROM_ISR(yield); + return true; + } else { + return xQueueSendToBack(handle.get(), message, timeout) == pdPASS; + } + } + + /** + * Get message from queue + * @param[out] message A pointer to an already allocated message object + * @param[in] timeout + * @return success result + */ + bool get(void* message, TickType_t timeout) const { + assert(handle != nullptr); + assert(message != nullptr); + + if (xPortInIsrContext() == pdTRUE) { + if (timeout != 0U) { + return false; + } + + BaseType_t yield = pdFALSE; + if (xQueueReceiveFromISR(handle.get(), message, &yield) != pdPASS) { + return false; + } + + portYIELD_FROM_ISR(yield); + return true; + } else { + return xQueueReceive(handle.get(), message, timeout) == pdPASS; + } + } + + /** + * @return The amount of messages in the queue. + */ + uint32_t getCount() const { + assert(handle != nullptr); + return (xPortInIsrContext() == pdTRUE) + ? uxQueueMessagesWaitingFromISR(handle.get()) + : uxQueueMessagesWaiting(handle.get()); + } + + /** + * Reset queue + * @warning Don't call this from ISR context + * @return success result + */ + void reset() const { + assert(handle != nullptr); + assert(xPortInIsrContext() == pdFALSE); + xQueueReset(handle.get()); + } +}; + +} // namespace diff --git a/TactilityCore/Include/Tactility/Mutex.h b/TactilityFreeRtos/Include/Tactility/Mutex.h similarity index 54% rename from TactilityCore/Include/Tactility/Mutex.h rename to TactilityFreeRtos/Include/Tactility/Mutex.h index b69b48b3..759f3bb7 100644 --- a/TactilityCore/Include/Tactility/Mutex.h +++ b/TactilityFreeRtos/Include/Tactility/Mutex.h @@ -1,13 +1,8 @@ -/** - * @file Mutex.h - * Mutex - */ #pragma once #include "Lock.h" -#include "RtosCompatSemaphore.h" -#include "Thread.h" -#include "kernel/Kernel.h" +#include "freertoscompat/PortCompat.h" +#include "freertoscompat/Semaphore.h" #include #include @@ -16,19 +11,10 @@ namespace tt { /** * Wrapper for FreeRTOS xSemaphoreCreateMutex - * Cannot be used in IRQ mode (within ISR context) + * Cannot be used from ISR context */ class Mutex final : public Lock { -private: - - struct SemaphoreHandleDeleter { - void operator()(QueueHandle_t handleToDelete) { - assert(!kernel::isIsr()); - vSemaphoreDelete(handleToDelete); - } - }; - std::unique_ptr, SemaphoreHandleDeleter> handle = std::unique_ptr, SemaphoreHandleDeleter>(xSemaphoreCreateMutex()); public: @@ -46,21 +32,19 @@ public: * @return success result */ bool lock(TickType_t timeout) const override { - assert(!kernel::isIsr()); + assert(xPortInIsrContext() == pdFALSE); return xSemaphoreTake(handle.get(), timeout) == pdPASS; } - /** Attempt to unlock the mutex. - * @return success result - */ - bool unlock() const override { - assert(!kernel::isIsr()); - return xSemaphoreGive(handle.get()) == pdPASS; + /** Unlock the mutex */ + void unlock() const override { + assert(xPortInIsrContext() == pdFALSE); + xSemaphoreGive(handle.get()); } - /** @return the owner of the thread */ - ThreadId getOwner() const { - assert(!kernel::isIsr()); + /** @return the task handle of the owning task */ + TaskHandle_t getOwner() const { + assert(xPortInIsrContext() == pdFALSE); return xSemaphoreGetMutexHolder(handle.get()); } }; diff --git a/TactilityCore/Include/Tactility/PubSub.h b/TactilityFreeRtos/Include/Tactility/PubSub.h similarity index 69% rename from TactilityCore/Include/Tactility/PubSub.h rename to TactilityFreeRtos/Include/Tactility/PubSub.h index 77dd087f..17b8b6e2 100644 --- a/TactilityCore/Include/Tactility/PubSub.h +++ b/TactilityFreeRtos/Include/Tactility/PubSub.h @@ -1,8 +1,17 @@ #pragma once #include "Mutex.h" +#include "kernel/Kernel.h" + +#include #include +#ifdef ESP_PLATFORM +#include +#endif + +#include + namespace tt { /** Publish and subscribe to messages in a thread-safe manner. */ @@ -27,16 +36,26 @@ public: ~PubSub() { if (!items.empty()) { - TT_LOG_W("Loader", "Destroying PubSub with %d active subscriptions", items.size()); +#ifdef ESP_PLATFORM + ESP_LOGW("PubSub", "Destroying with %d active subscriptions", items.size()); +#endif + } + + // Wait for Mutex usage + if (mutex.lock(kernel::MAX_TICKS)) { + // TODO: Fix the case where the mutex might be immediately locked after this point and then crashes when deleted + mutex.unlock(); } } - /** Start receiving messages at the specified handle (Threadsafe, Re-entrable) + /** + * Start receiving messages at the specified handle (Re-entrable) * @param[in] callback * @return subscription instance */ SubscriptionHandle subscribe(std::function callback) { mutex.lock(); + items.push_back({ .id = (++lastId), .callback = std::move(callback) @@ -47,14 +66,15 @@ public: return reinterpret_cast(lastId); } - /** Stop receiving messages at the specified handle (Threadsafe, Re-entrable.) - * No use of `tt_pubsub_subscription` allowed after call of this method + /** + * Stop receiving messages at the specified handle (Re-entrable) * @param[in] subscription */ void unsubscribe(SubscriptionHandle subscription) { assert(subscription); mutex.lock(); + bool result = false; auto id = reinterpret_cast(subscription); for (auto it = items.begin(); it != items.end(); ++it) { @@ -66,10 +86,11 @@ public: } mutex.unlock(); - tt_check(result); + assert(result); } - /** Publish something to all subscribers (Threadsafe, Re-entrable.) + /** + * Publish something to all subscribers (Re-entrable) * @param[in] data the data to publish */ void publish(DataType data) { diff --git a/TactilityCore/Include/Tactility/RecursiveMutex.h b/TactilityFreeRtos/Include/Tactility/RecursiveMutex.h similarity index 52% rename from TactilityCore/Include/Tactility/RecursiveMutex.h rename to TactilityFreeRtos/Include/Tactility/RecursiveMutex.h index 3d51522b..5358351d 100644 --- a/TactilityCore/Include/Tactility/RecursiveMutex.h +++ b/TactilityFreeRtos/Include/Tactility/RecursiveMutex.h @@ -1,13 +1,9 @@ -/** - * @file RecursiveMutex.h - * RecursiveMutex - */ #pragma once #include "Lock.h" -#include "RtosCompatSemaphore.h" -#include "Thread.h" -#include "kernel/Kernel.h" +#include "freertoscompat/PortCompat.h" +#include "freertoscompat/Semaphore.h" + #include #include @@ -15,19 +11,10 @@ namespace tt { /** * Wrapper for FreeRTOS xSemaphoreCreateRecursiveMutex - * Cannot be used in IRQ mode (within ISR context) + * Cannot be used from ISR context */ class RecursiveMutex final : public Lock { -private: - - struct SemaphoreHandleDeleter { - void operator()(QueueHandle_t handleToDelete) { - assert(!kernel::isIsr()); - vSemaphoreDelete(handleToDelete); - } - }; - std::unique_ptr, SemaphoreHandleDeleter> handle = std::unique_ptr, SemaphoreHandleDeleter>(xSemaphoreCreateRecursiveMutex()); public: @@ -40,26 +27,25 @@ public: ~RecursiveMutex() override = default; - /** Attempt to lock the mutex. Blocks until timeout passes or lock is acquired. + /** + * Attempt to lock the mutex. Blocks until timeout passes or lock is acquired. * @param[in] timeout * @return success result */ bool lock(TickType_t timeout) const override { - assert(!kernel::isIsr()); + assert(xPortInIsrContext() == pdFALSE); return xSemaphoreTakeRecursive(handle.get(), timeout) == pdPASS; } - /** Attempt to unlock the mutex. - * @return success result - */ - bool unlock() const override { - assert(!kernel::isIsr()); - return xSemaphoreGiveRecursive(handle.get()) == pdPASS; + /** Unlock the mutex */ + void unlock() const override { + assert(xPortInIsrContext() == pdFALSE); + xSemaphoreGiveRecursive(handle.get()); } /** @return the owner of the thread */ - ThreadId getOwner() const { - assert(!kernel::isIsr()); + TaskHandle_t getOwner() const { + assert(xPortInIsrContext() == pdFALSE); return xSemaphoreGetMutexHolder(handle.get()); } }; diff --git a/TactilityFreeRtos/Include/Tactility/Semaphore.h b/TactilityFreeRtos/Include/Tactility/Semaphore.h new file mode 100644 index 00000000..8dc0bc9b --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/Semaphore.h @@ -0,0 +1,124 @@ +#pragma once + +#include "Lock.h" +#include "freertoscompat/PortCompat.h" +#include "freertoscompat/Semaphore.h" + +#include +#include + +#ifdef ESP_PLATFORM +#include +#endif + +namespace tt { + +/** + * Wrapper for xSemaphoreCreateBinary (max count == 1) and xSemaphoreCreateCounting (max count > 1) + * Can be used from ISR context, but cannot be created/destroyed from such a context. + */ +class Semaphore final : public Lock { + + static QueueHandle_t createHandle(uint32_t maxCount, uint32_t initialAvailable) { + assert(maxCount > 0U); + assert(initialAvailable <= maxCount); + + if (maxCount == 1U) { + assert(initialAvailable == maxCount); // TODO: Consider supporting this deviation + return xSemaphoreCreateBinary(); + } else { + return xSemaphoreCreateCounting(maxCount, initialAvailable); + } + } + + std::unique_ptr, SemaphoreHandleDeleter> handle; + +public: + + using Lock::lock; + + /** + * Cannot be called from ISR context. + * @param[in] maxAvailable The maximum count + * @param[in] initialAvailable The initial count + */ + Semaphore(uint32_t maxAvailable, uint32_t initialAvailable) : handle(createHandle(maxAvailable, initialAvailable)) { + assert(xPortInIsrContext() == pdFALSE); + assert(handle != nullptr); + } + + /** + * Cannot be called from IRQ/ISR mode. + * @param[in] maxAvailable The maximum count + */ + explicit Semaphore(uint32_t maxAvailable) : Semaphore(maxAvailable, maxAvailable) {} + + /** Cannot be called from IRQ/ISR mode. */ + ~Semaphore() override { + assert(xPortInIsrContext() == pdFALSE); + } + + Semaphore(Semaphore& other) : handle(std::move(other.handle)) {} + + /** + * Acquire the semaphore + * @param[in] timeout + * @return true on success + */ + bool acquire(TickType_t timeout) const { + if (xPortInIsrContext() == pdTRUE) { + if (timeout != 0U) { + return false; + } + + BaseType_t yield = pdFALSE; + if (xSemaphoreTakeFromISR(handle.get(), &yield) != pdPASS) { + return false; + } + + portYIELD_FROM_ISR(yield); + return true; + } else { + return xSemaphoreTake(handle.get(), timeout) == pdPASS; + } + } + + /** + * Release the semaphore + * @return true on success + */ + bool release() const { + if (xPortInIsrContext() == pdTRUE) { + BaseType_t yield = pdFALSE; + if (xSemaphoreGiveFromISR(handle.get(), &yield) != pdTRUE) { + return false; + } + + portYIELD_FROM_ISR(yield); + return true; + } else { + return xSemaphoreGive(handle.get()) == pdPASS; + } + } + + /** @return the acquisition count */ + uint32_t getAvailable() const { + if (xPortInIsrContext() == pdTRUE) { + return uxSemaphoreGetCountFromISR(handle.get()); + } else { + return uxSemaphoreGetCount(handle.get()); + } + } + + // region Lock + + /** Calls acquire() */ + bool lock(TickType_t timeout) const override { return acquire(timeout); } + + /** Calls release() */ + void unlock() const override { release(); } + + // endregion +}; + +} // namespace diff --git a/TactilityFreeRtos/Include/Tactility/Thread.h b/TactilityFreeRtos/Include/Tactility/Thread.h new file mode 100644 index 00000000..6bb6e204 --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/Thread.h @@ -0,0 +1,262 @@ +#pragma once + +#include "freertoscompat/Task.h" +#include "kernel/Kernel.h" +#include "Mutex.h" + +#include +#include +#include +#include + +#ifdef ESP_PLATFORM +#include +#endif + +namespace tt { + +class Thread final { + + static constexpr size_t LOCAL_STORAGE_SELF_POINTER_INDEX = 0; + +public: + + enum class State{ + Stopped, + Starting, + Running, + }; + + /** ThreadPriority */ + enum class Priority : UBaseType_t { + None = 0U, + Idle = 1U, + Lower = 2U, + Low = 3U, + Normal = 4U, + High = 5U, + Higher = 6U, + Critical = 7U + }; + + typedef std::function MainFunction; + + typedef void (*StateCallback)(State state, void* context); + +private: + + static constexpr auto TAG = "Thread"; + + static_assert(static_cast(Priority::Critical) <= configMAX_PRIORITIES, "Highest thread priority is higher than max priority"); + + static void mainBody(void* context) { + assert(context != nullptr); + auto* thread = static_cast(context); + + // Save Thread instance pointer to task local storage + assert(pvTaskGetThreadLocalStoragePointer(nullptr, LOCAL_STORAGE_SELF_POINTER_INDEX) == nullptr); + vTaskSetThreadLocalStoragePointer(nullptr, LOCAL_STORAGE_SELF_POINTER_INDEX, thread); + +#ifdef ESP_PLATFORM + ESP_LOGI(TAG, "Starting %s", thread->name.c_str()); +#endif + assert(thread->state == State::Starting); + thread->setState(State::Running); + thread->callbackResult = thread->mainFunction(); + assert(thread->state == State::Running); + thread->setState(State::Stopped); +#ifdef ESP_PLATFORM + ESP_LOGI(TAG, "Stopped %s", thread->name.c_str()); +#endif + + vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr); + thread->taskHandle = nullptr; + + vTaskDelete(nullptr); + } + + TaskHandle_t taskHandle = nullptr; + State state = State::Stopped; + MainFunction mainFunction; + int32_t callbackResult = 0; + StateCallback stateCallback = nullptr; + void* stateCallbackContext = nullptr; + std::string name = {}; + Priority priority = Priority::Normal; + Mutex mutex; + configSTACK_DEPTH_TYPE stackSize = 0; + portBASE_TYPE affinity = -1; + + void setState(State newState) { + // mutex.lock(); + state = newState; + if (stateCallback) { + stateCallback(state, stateCallbackContext); + } + } + +public: + + Thread() = default; + + Thread( + std::string name, + configSTACK_DEPTH_TYPE stackSize, + MainFunction function, + portBASE_TYPE affinity = -1 + ) : + mainFunction(function), + name(std::move(name)), + stackSize(stackSize), + affinity(affinity) + {} + + /** @warning If thread is running, you mjust call join() first */ + ~Thread() { + assert(state == State::Stopped); + assert(taskHandle == nullptr); + } + + void setName(std::string newName) { + mutex.lock(); + assert(state == State::Stopped); + name = std::move(newName); + mutex.unlock(); + } + + void setStackSize(size_t newStackSize) { + mutex.lock(); + assert(state == State::Stopped); + assert(stackSize % 4 == 0); + stackSize = newStackSize; + mutex.unlock(); + } + + void setAffinity(portBASE_TYPE newAffinity) { + mutex.lock(); + assert(state == State::Stopped); + affinity = newAffinity; + mutex.unlock(); + } + + void setMainFunction(MainFunction function) { + mutex.lock(); + assert(state == State::Stopped); + mainFunction = function; + mutex.unlock(); + } + + void setPriority(Priority newPriority) { + mutex.lock(); + assert(state == State::Stopped); + priority = newPriority; + mutex.unlock(); + } + + void setStateCallback(StateCallback callback, _Nullable void* callbackContext = nullptr) { + mutex.lock(); + assert(state == State::Stopped); + stateCallback = callback; + stateCallbackContext = callbackContext; + mutex.unlock(); + } + + State getState() const { + auto lock = mutex.asScopedLock(); + lock.lock(); + return state; + } + + void start() { + mutex.lock(); + assert(mainFunction); + assert(state == State::Stopped); + assert(stackSize > 0 && stackSize < (UINT16_MAX * sizeof(StackType_t))); + mutex.unlock(); + + setState(State::Starting); + + mutex.lock(); + uint32_t stack_depth = stackSize / sizeof(StackType_t); + mutex.unlock(); + + BaseType_t result; + if (affinity != -1) { +#ifdef ESP_PLATFORM + result = xTaskCreatePinnedToCore( + mainBody, + name.c_str(), + stack_depth, + this, + static_cast(priority), + &taskHandle, + affinity + ); +#else + // Pinned tasks are not supported by current FreeRTOS platform - creating regular one + result = xTaskCreate( + mainBody, + name.c_str(), + stack_depth, + this, + static_cast(priority), + &taskHandle + ); +#endif + } else { + result = xTaskCreate( + mainBody, + name.c_str(), + stack_depth, + this, + static_cast(priority), + &taskHandle + ); + } + + assert(result == pdPASS); + assert(taskHandle != nullptr || getState() == State::Stopped); + } + + /** + * @warning If this blocks forever, it might be because of the Thread, but it could also be because another task is blocking the CPU. + */ + bool join(TickType_t timeout = kernel::MAX_TICKS, TickType_t pollInterval = 10) { + assert(getCurrent() != this); + + TickType_t start_ticks = kernel::getTicks(); + while (getTaskHandle()) { + kernel::delayTicks(pollInterval); + if (kernel::getTicks() - start_ticks > timeout) { + return false; + } + } + + return true; + } + + TaskHandle_t getTaskHandle() const { return taskHandle; } + + int32_t getReturnCode() const { + assert(getState() == State::Stopped); + return callbackResult; + } + + uint32_t getStackSpace() const { + if (xPortInIsrContext() == pdTRUE || getTaskHandle() == nullptr) { + return 0; + } else { + return uxTaskGetStackHighWaterMark(taskHandle) * sizeof(StackType_t); + } + } + + static Thread* getCurrent() { + return static_cast(pvTaskGetThreadLocalStoragePointer(nullptr, LOCAL_STORAGE_SELF_POINTER_INDEX)); + } +}; + +constexpr auto THREAD_PRIORITY_SERVICE = Thread::Priority::High; +constexpr auto THREAD_PRIORITY_RENDER = Thread::Priority::Higher; +constexpr auto THREAD_PRIORITY_ISR = Thread::Priority::Critical; + +} // namespace diff --git a/TactilityFreeRtos/Include/Tactility/Timer.h b/TactilityFreeRtos/Include/Tactility/Timer.h new file mode 100644 index 00000000..ff0f90f3 --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/Timer.h @@ -0,0 +1,151 @@ +#pragma once + +#include "Thread.h" +#include "freertoscompat/Timers.h" + +#include +#include + +namespace tt { + +/** + * Wrapper class for xTimer functions. + * @warning Cannot be called from an ISR context except for ::setPendingCallback() + */ +class Timer final { + +public: + + enum class Type { + Once = 0, // Timer triggers once after time has passed + Periodic = 1 // Timer triggers repeatedly after time has passed + }; + + typedef std::function Callback; + typedef void (*PendingCallback)(void* context, uint32_t arg); + +private: + + struct TimerHandleDeleter { + void operator()(TimerHandle_t handleToDelete) const { + xTimerDelete(handleToDelete, kernel::MAX_TICKS); + } + }; + + Callback callback; + std::unique_ptr, TimerHandleDeleter> handle; + + static TimerHandle_t createTimer(Type type, TickType_t ticks, void* timerId, TimerCallbackFunction_t callback) { + assert(timerId != nullptr); + assert(callback != nullptr); + BaseType_t auto_reload = (type == Type::Once) ? pdFALSE : pdTRUE; + return xTimerCreate(nullptr, ticks, auto_reload, timerId, callback); + } + + + static void onCallback(TimerHandle_t hTimer) { + auto* timer = static_cast(pvTimerGetTimerID(hTimer)); + if (timer != nullptr) { + timer->callback(); + } + } + +public: + + /** + * @param[in] type The timer type + * @param[in] callback The callback function + */ + Timer(Type type, TickType_t ticks, Callback callback) : + callback(callback), + handle(createTimer(type, ticks, this, onCallback)) + { + assert(xPortInIsrContext() == pdFALSE); + assert(handle != nullptr); + } + + ~Timer() { + assert(xPortInIsrContext() == pdFALSE); + } + + /** + * Start the timer + * @return success result + */ + bool start() const { + assert(xPortInIsrContext() == pdFALSE); + return xTimerStart(handle.get(), kernel::MAX_TICKS) == pdPASS; + } + + /** Stop the timer + * @warning If the timer was just triggered, the callback might still be going on after stop() was called + * @return success result + */ + bool stop() const { + assert(xPortInIsrContext() == pdFALSE); + return xTimerStop(handle.get(), kernel::MAX_TICKS) == pdPASS; + } + + /** + * Set a new interval and reset the timer + * @param[in] interval The new timer interval + * @return success result + */ + bool reset(TickType_t interval) const { + assert(xPortInIsrContext() == pdFALSE); + return xTimerChangePeriod(handle.get(), interval, kernel::MAX_TICKS) == pdPASS && + xTimerReset(handle.get(), kernel::MAX_TICKS) == pdPASS; + } + + /** + * Reset the timer + * @return success result + */ + bool reset() const { + assert(xPortInIsrContext() == pdFALSE); + return xTimerReset(handle.get(), kernel::MAX_TICKS) == pdPASS; + } + + /** @return true when the timer is running */ + bool isRunning() const { + assert(xPortInIsrContext() == pdFALSE); + return xTimerIsTimerActive(handle.get()) == pdTRUE; + } + + /** @return the expiry time in ticks */ + TickType_t getExpiryTime() const { + assert(xPortInIsrContext() == pdFALSE); + return xTimerGetExpiryTime(handle.get()); + } + + /** + * Calls xTimerPendFunctionCall internally. + * @param[in] callback the function to call + * @param[in] callbackContext the first function argument + * @param[in] callbackArg the second function argument + * @param[in] timeout the function timeout (must set to 0 in ISR mode) + * @return true on success + */ + bool setPendingCallback(PendingCallback newCallback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) const { + if (xPortInIsrContext() == pdTRUE) { + assert(timeout == 0); + return xTimerPendFunctionCallFromISR(newCallback, callbackContext, callbackArg, nullptr) == pdPASS; + } else { + return xTimerPendFunctionCall(newCallback, callbackContext, callbackArg, timeout) == pdPASS; + } + } + + /** Set callback priority + * @param[in] priority The priority + */ + void setCallbackPriority(Thread::Priority priority) const { + assert(xPortInIsrContext() == pdFALSE); + + TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); + assert(task_handle); // Don't call this method before timer task start + + vTaskPrioritySet(task_handle, static_cast(priority)); + } +}; + +} // namespace diff --git a/TactilityFreeRtos/Include/Tactility/freertoscompat/EventGroups.h b/TactilityFreeRtos/Include/Tactility/freertoscompat/EventGroups.h new file mode 100644 index 00000000..49b52699 --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/freertoscompat/EventGroups.h @@ -0,0 +1,10 @@ +#pragma once + +#ifdef ESP_PLATFORM +#include +#include +#else +#include +#include +#endif + diff --git a/TactilityFreeRtos/Include/Tactility/freertoscompat/PortCompat.h b/TactilityFreeRtos/Include/Tactility/freertoscompat/PortCompat.h new file mode 100644 index 00000000..6a43514f --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/freertoscompat/PortCompat.h @@ -0,0 +1,7 @@ +#pragma once + +#include "RTOS.h" + +#ifndef ESP_PLATFORM +#define xPortInIsrContext(x) (false) +#endif diff --git a/TactilityFreeRtos/Include/Tactility/freertoscompat/Queue.h b/TactilityFreeRtos/Include/Tactility/freertoscompat/Queue.h new file mode 100644 index 00000000..b8db380a --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/freertoscompat/Queue.h @@ -0,0 +1,8 @@ +#ifdef ESP_PLATFORM +#include +#include +#else +#include +#include +#endif + diff --git a/TactilityFreeRtos/Include/Tactility/freertoscompat/README.md b/TactilityFreeRtos/Include/Tactility/freertoscompat/README.md new file mode 100644 index 00000000..9b41539d --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/freertoscompat/README.md @@ -0,0 +1,3 @@ +Compatibility include files for FreeRTOS. +Custom FreeRTOS from ESP-IDF prefixes paths with "freertos/", +but this isn't the normal behaviour for the regular FreeRTOS project. diff --git a/TactilityFreeRtos/Include/Tactility/freertoscompat/RTOS.h b/TactilityFreeRtos/Include/Tactility/freertoscompat/RTOS.h new file mode 100644 index 00000000..42481234 --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/freertoscompat/RTOS.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef ESP_PLATFORM +#include +#else +#include +#endif diff --git a/TactilityFreeRtos/Include/Tactility/freertoscompat/Semaphore.h b/TactilityFreeRtos/Include/Tactility/freertoscompat/Semaphore.h new file mode 100644 index 00000000..4594a934 --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/freertoscompat/Semaphore.h @@ -0,0 +1,19 @@ +#pragma once + +#ifdef ESP_PLATFORM +#include +#include +#else +#include +#include +#endif + +#include + +struct SemaphoreHandleDeleter { + static void operator()(QueueHandle_t handleToDelete) { + assert(xPortInIsrContext() == pdFALSE); + vSemaphoreDelete(handleToDelete); + } +}; + diff --git a/TactilityFreeRtos/Include/Tactility/freertoscompat/Task.h b/TactilityFreeRtos/Include/Tactility/freertoscompat/Task.h new file mode 100644 index 00000000..59b5d65c --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/freertoscompat/Task.h @@ -0,0 +1,10 @@ +#pragma once + +#ifdef ESP_PLATFORM +#include +#include +#else +#include +#include +#endif + diff --git a/TactilityFreeRtos/Include/Tactility/freertoscompat/Timers.h b/TactilityFreeRtos/Include/Tactility/freertoscompat/Timers.h new file mode 100644 index 00000000..45cf6c6e --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/freertoscompat/Timers.h @@ -0,0 +1,9 @@ +#pragma once + +#ifdef ESP_PLATFORM +#include +#include +#else +#include +#include +#endif diff --git a/TactilityFreeRtos/Include/Tactility/kernel/Kernel.h b/TactilityFreeRtos/Include/Tactility/kernel/Kernel.h new file mode 100644 index 00000000..2fdbc444 --- /dev/null +++ b/TactilityFreeRtos/Include/Tactility/kernel/Kernel.h @@ -0,0 +1,99 @@ +#pragma once + +#include "../freertoscompat/PortCompat.h" +#include "../freertoscompat/Task.h" + +#ifdef ESP_PLATFORM +#include +#include +#else +#include +#include +#include +#endif + +#include + +namespace tt::kernel { + +constexpr TickType_t MAX_TICKS = ~static_cast(0); + +/** @return the frequency at which the kernel task schedulers operate */ +constexpr uint32_t getTickFrequency() { + return configTICK_RATE_HZ; +} + +/** @return the amount of ticks that has passed in the main kernel task */ +constexpr TickType_t getTicks() { + if (xPortInIsrContext() == pdTRUE) { + return xTaskGetTickCountFromISR(); + } else { + return xTaskGetTickCount(); + } +} + + +/** @return the amount of milliseconds that has passed in the main kernel tasks */ +constexpr size_t getMillis() { + return getTicks() / portTICK_PERIOD_MS; +} + +/** @return the microseconds that have passed since boot */ +constexpr int64_t getMicrosSinceBoot() { +#ifdef ESP_PLATFORM + return static_cast(esp_timer_get_time()); +#else + timeval tv; + gettimeofday(&tv, nullptr); + return 1000000 * tv.tv_sec + tv.tv_usec; +#endif +} + +/** Convert seconds to ticks */ +constexpr TickType_t secondsToTicks(uint32_t seconds) { + return static_cast(seconds) * 1000U / portTICK_PERIOD_MS; +} + +/** Convert milliseconds to ticks */ +constexpr TickType_t millisToTicks(uint32_t milliSeconds) { +#if configTICK_RATE_HZ == 1000 + return static_cast(milliSeconds); +#else + return static_cast(((float)configTICK_RATE_HZ) / 1000.0f * (float)milliSeconds); +#endif +} + +/** + * Delay the current task for the specified amount of ticks + * @warning Does not work in ISR context + */ +constexpr void delayTicks(TickType_t ticks) { + assert(xPortInIsrContext() == pdFALSE); + if (ticks == 0U) { + taskYIELD(); + } else { + vTaskDelay(ticks); + } +} + +/** + * Delay the current task for the specified amount of milliseconds + * @warning Does not work in ISR context + */ +constexpr void delayMillis(uint32_t milliSeconds) { + delayTicks(millisToTicks(milliSeconds)); +} + +/** + * Stall the currently active CPU core for the specified amount of microseconds. + * This does not allow other tasks to run on the stalled CPU core. + */ +constexpr void delayMicros(uint32_t microseconds) { +#ifdef ESP_PLATFORM + ets_delay_us(microseconds); +#else + usleep(microseconds); +#endif +} + +} // namespace diff --git a/TactilityFreeRtos/README.md b/TactilityFreeRtos/README.md new file mode 100644 index 00000000..3a2b82ab --- /dev/null +++ b/TactilityFreeRtos/README.md @@ -0,0 +1,7 @@ +## Description + +A collection of FreeRTOS C++ wrappers. + +## License + +[GNU General Public License Version 3](LICENSE.md) diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index d6120d46..3ab79bd1 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -4,8 +4,10 @@ set(DOCTESTINC ${PROJECT_SOURCE_DIR}/Include) enable_testing() add_subdirectory(TactilityCore) +add_subdirectory(TactilityFreeRtos) add_subdirectory(Tactility) add_custom_target(build-tests) add_dependencies(build-tests TactilityCoreTests) +add_dependencies(build-tests TactilityFreeRtosTests) add_dependencies(build-tests TactilityTests) diff --git a/Tests/TactilityCore/CMakeLists.txt b/Tests/TactilityCore/CMakeLists.txt index e97a60d8..b11ae240 100644 --- a/Tests/TactilityCore/CMakeLists.txt +++ b/Tests/TactilityCore/CMakeLists.txt @@ -20,5 +20,6 @@ add_test(NAME TactilityCoreTests target_link_libraries(TactilityCoreTests PUBLIC TactilityCore + TactilityFreeRtos freertos_kernel ) diff --git a/Tests/TactilityFreeRtos/CMakeLists.txt b/Tests/TactilityFreeRtos/CMakeLists.txt new file mode 100644 index 00000000..788f2f67 --- /dev/null +++ b/Tests/TactilityFreeRtos/CMakeLists.txt @@ -0,0 +1,24 @@ +project(TactilityFreeRtosTests) + +enable_language(C CXX ASM) + +set(CMAKE_CXX_COMPILER g++) + +file(GLOB_RECURSE TEST_SOURCES ${PROJECT_SOURCE_DIR}/*.cpp) +add_executable(TactilityFreeRtosTests EXCLUDE_FROM_ALL ${TEST_SOURCES}) + +add_definitions(-D_Nullable=) +add_definitions(-D_Nonnull=) + +target_include_directories(TactilityFreeRtosTests PRIVATE + ${DOCTESTINC} +) + +add_test(NAME TactilityFreeRtosTests + COMMAND TactilityFreeRtosTests +) + +target_link_libraries(TactilityFreeRtosTests PUBLIC + TactilityFreeRtos + freertos_kernel +) diff --git a/Tests/TactilityCore/DispatcherTest.cpp b/Tests/TactilityFreeRtos/DispatcherTest.cpp similarity index 87% rename from Tests/TactilityCore/DispatcherTest.cpp rename to Tests/TactilityFreeRtos/DispatcherTest.cpp index 8f34e5fa..024dec77 100644 --- a/Tests/TactilityCore/DispatcherTest.cpp +++ b/Tests/TactilityFreeRtos/DispatcherTest.cpp @@ -1,5 +1,4 @@ #include "doctest.h" -#include #include using namespace tt; @@ -24,8 +23,8 @@ TEST_CASE("dispatcher should call callback when consume is called") { int counter = 0; Dispatcher dispatcher; - dispatcher.dispatch([&counter]() { counter++; }); - dispatcher.consume(100); + CHECK_EQ(dispatcher.dispatch([&counter] { counter++; }), true); + CHECK_EQ(dispatcher.consume(100), 1); CHECK_EQ(counter, 1); } diff --git a/Tests/TactilityCore/DispatcherThreadTest.cpp b/Tests/TactilityFreeRtos/DispatcherThreadTest.cpp similarity index 94% rename from Tests/TactilityCore/DispatcherThreadTest.cpp rename to Tests/TactilityFreeRtos/DispatcherThreadTest.cpp index 346d509a..7a3d6d16 100644 --- a/Tests/TactilityCore/DispatcherThreadTest.cpp +++ b/Tests/TactilityFreeRtos/DispatcherThreadTest.cpp @@ -1,5 +1,4 @@ #include "doctest.h" -#include #include using namespace tt; diff --git a/Tests/TactilityCore/LockTest.cpp b/Tests/TactilityFreeRtos/LockTest.cpp similarity index 96% rename from Tests/TactilityCore/LockTest.cpp rename to Tests/TactilityFreeRtos/LockTest.cpp index 03212bab..2eb5a6e0 100644 --- a/Tests/TactilityCore/LockTest.cpp +++ b/Tests/TactilityFreeRtos/LockTest.cpp @@ -1,7 +1,7 @@ #include "doctest.h" +#include #include #include -#include using namespace tt; @@ -35,5 +35,5 @@ TEST_CASE("withLock() unlocks correctly on Mutex") { }); CHECK_EQ(mutex->lock(1), true); - CHECK_EQ(mutex->unlock(), true); + mutex->unlock(); } diff --git a/Tests/TactilityFreeRtos/Main.cpp b/Tests/TactilityFreeRtos/Main.cpp new file mode 100644 index 00000000..c1dd29aa --- /dev/null +++ b/Tests/TactilityFreeRtos/Main.cpp @@ -0,0 +1,58 @@ +#define DOCTEST_CONFIG_IMPLEMENT +#include "doctest.h" +#include + +#include "FreeRTOS.h" +#include "task.h" + +typedef struct { + int argc; + char** argv; + int result; +} TestTaskData; + +void test_task(void* parameter) { + auto* data = (TestTaskData*)parameter; + + doctest::Context context; + + context.applyCommandLine(data->argc, data->argv); + + // overrides + context.setOption("no-breaks", true); // don't break in the debugger when assertions fail + + data->result = context.run(); + + if (context.shouldExit()) { // important - query flags (and --exit) rely on the user doing this + vTaskEndScheduler(); + } + + vTaskDelete(nullptr); +} + +int main(int argc, char** argv) { + TestTaskData data = { + .argc = argc, + .argv = argv, + .result = 0 + }; + + BaseType_t task_result = xTaskCreate( + test_task, + "test_task", + 8192, + &data, + 1, + nullptr + ); + assert(task_result == pdPASS); + + vTaskStartScheduler(); +} + +extern "C" { + // Required for FreeRTOS + void vAssertCalled(unsigned long line, const char* const file) { + __assert_fail("assert failed", file, line, ""); + } +} diff --git a/Tests/TactilityCore/MessageQueueTest.cpp b/Tests/TactilityFreeRtos/MessageQueueTest.cpp similarity index 84% rename from Tests/TactilityCore/MessageQueueTest.cpp rename to Tests/TactilityFreeRtos/MessageQueueTest.cpp index 5972fbab..90463c53 100644 --- a/Tests/TactilityCore/MessageQueueTest.cpp +++ b/Tests/TactilityFreeRtos/MessageQueueTest.cpp @@ -3,13 +3,6 @@ using namespace tt; -TEST_CASE("message queue capacity should be correct") { - MessageQueue queue(10, 1); - - uint32_t capacity = queue.getCapacity(); - CHECK_EQ(capacity, 10); -} - TEST_CASE("message queue initial count should be 0") { MessageQueue queue(10, 1); @@ -17,13 +10,6 @@ TEST_CASE("message queue initial count should be 0") { CHECK_EQ(count, 0); } -TEST_CASE("message queue message size should be correct") { - MessageQueue queue(1, 123); - - uint32_t message_size = queue.getMessageSize(); - CHECK_EQ(message_size, 123); -} - TEST_CASE("message queue count should increase when message is added") { MessageQueue queue(10, sizeof(uint32_t)); @@ -83,4 +69,3 @@ TEST_CASE("message queue should make copy of data") { CHECK_EQ(queue.get(&queue_number, 100), true); CHECK_EQ(queue_number, test_value); } - diff --git a/Tests/TactilityCore/MutexTest.cpp b/Tests/TactilityFreeRtos/MutexTest.cpp similarity index 52% rename from Tests/TactilityCore/MutexTest.cpp rename to Tests/TactilityFreeRtos/MutexTest.cpp index 768f638c..b30ee2bd 100644 --- a/Tests/TactilityCore/MutexTest.cpp +++ b/Tests/TactilityFreeRtos/MutexTest.cpp @@ -1,18 +1,19 @@ #include "doctest.h" -#include +#include #include +#include using namespace tt; -TEST_CASE("a mutex can block a thread") { +TEST_CASE("a Mutex can block a thread") { auto mutex = Mutex(); - mutex.lock(portMAX_DELAY); + CHECK_EQ(mutex.lock(kernel::MAX_TICKS), true); Thread thread = Thread( "thread", 1024, - [&mutex]() { - mutex.lock(portMAX_DELAY); + [&mutex] { + mutex.lock(kernel::MAX_TICKS); return 0; } ); @@ -33,17 +34,5 @@ TEST_CASE("a Mutex can be locked exactly once") { Mutex mutex; CHECK_EQ(mutex.lock(0), true); CHECK_EQ(mutex.lock(0), false); - CHECK_EQ(mutex.unlock(), true); -} - -TEST_CASE("unlocking a Mutex without locking returns false") { - Mutex mutex; - CHECK_EQ(mutex.unlock(), false); -} - -TEST_CASE("unlocking a Mutex twice returns false on the second attempt") { - Mutex mutex; - CHECK_EQ(mutex.lock(0), true); - CHECK_EQ(mutex.unlock(), true); - CHECK_EQ(mutex.unlock(), false); + mutex.unlock(); } diff --git a/Tests/TactilityCore/PubSubTest.cpp b/Tests/TactilityFreeRtos/PubSubTest.cpp similarity index 95% rename from Tests/TactilityCore/PubSubTest.cpp rename to Tests/TactilityFreeRtos/PubSubTest.cpp index 600233be..20ce9b1f 100644 --- a/Tests/TactilityCore/PubSubTest.cpp +++ b/Tests/TactilityFreeRtos/PubSubTest.cpp @@ -1,5 +1,4 @@ #include "doctest.h" -#include #include using namespace tt; @@ -18,6 +17,8 @@ TEST_CASE("PubSub subscription receives published data") { }); pubsub.publish(1); + pubsub.unsubscribe(subscription); + CHECK_EQ(value, 1); } diff --git a/Tests/TactilityFreeRtos/RecursiveMutexTest.cpp b/Tests/TactilityFreeRtos/RecursiveMutexTest.cpp new file mode 100644 index 00000000..907edd88 --- /dev/null +++ b/Tests/TactilityFreeRtos/RecursiveMutexTest.cpp @@ -0,0 +1,38 @@ +#include "doctest.h" +#include +#include +#include + +using namespace tt; + +TEST_CASE("a RecursiveMutex can block a thread") { + auto mutex = RecursiveMutex(); + CHECK_EQ(mutex.lock(kernel::MAX_TICKS), true); + + Thread thread = Thread( + "thread", + 1024, + [&mutex] { + mutex.lock(kernel::MAX_TICKS); + return 0; + } + ); + thread.start(); + + kernel::delayMillis(5); + CHECK_EQ(thread.getState(), Thread::State::Running); + + mutex.unlock(); + + kernel::delayMillis(5); + CHECK_EQ(thread.getState(), Thread::State::Stopped); + + thread.join(); +} + +TEST_CASE("a RecursiveMutex can be locked more than once from the same context") { + RecursiveMutex mutex; + CHECK_EQ(mutex.lock(0), true); + CHECK_EQ(mutex.lock(0), true); + mutex.unlock(); +} diff --git a/Tests/TactilityCore/SemaphoreTest.cpp b/Tests/TactilityFreeRtos/SemaphoreTest.cpp similarity index 100% rename from Tests/TactilityCore/SemaphoreTest.cpp rename to Tests/TactilityFreeRtos/SemaphoreTest.cpp diff --git a/Tests/TactilityCore/ThreadTest.cpp b/Tests/TactilityFreeRtos/ThreadTest.cpp similarity index 93% rename from Tests/TactilityCore/ThreadTest.cpp rename to Tests/TactilityFreeRtos/ThreadTest.cpp index dbfa4b1d..13188b13 100644 --- a/Tests/TactilityCore/ThreadTest.cpp +++ b/Tests/TactilityFreeRtos/ThreadTest.cpp @@ -1,5 +1,4 @@ #include "doctest.h" -#include #include using namespace tt; @@ -54,12 +53,12 @@ TEST_CASE("thread id should only be set at when thread is started") { return 0; } ); - CHECK_EQ(thread->getId(), nullptr); + CHECK_EQ(thread->getTaskHandle(), nullptr); thread->start(); - CHECK_NE(thread->getId(), nullptr); + CHECK_NE(thread->getTaskHandle(), nullptr); interrupted = true; thread->join(); - CHECK_EQ(thread->getId(), nullptr); + CHECK_EQ(thread->getTaskHandle(), nullptr); delete thread; } diff --git a/Tests/TactilityCore/TimerTest.cpp b/Tests/TactilityFreeRtos/TimerTest.cpp similarity index 96% rename from Tests/TactilityCore/TimerTest.cpp rename to Tests/TactilityFreeRtos/TimerTest.cpp index 1fc50dbf..1c3caf82 100644 --- a/Tests/TactilityCore/TimerTest.cpp +++ b/Tests/TactilityFreeRtos/TimerTest.cpp @@ -1,5 +1,4 @@ #include "doctest.h" -#include #include using namespace tt;