Create TactilityFreertos subproject (#440)

This commit is contained in:
Ken Van Hoeylandt
2026-01-03 00:19:40 +01:00
committed by GitHub
parent a4dc633063
commit 7283920def
154 changed files with 1926 additions and 2676 deletions
+4 -4
View File
@@ -1,9 +1,9 @@
#include "Tactility/Check.h"
#include <Tactility/Check.h>
#include "Tactility/Log.h"
#include "Tactility/RtosCompatTask.h"
#include <Tactility/Log.h>
#include <Tactility/freertoscompat/Task.h>
#define TAG "kernel"
constexpr auto TAG = "kernel";
static void logMemoryInfo() {
#ifdef ESP_PLATFORM
-70
View File
@@ -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
-47
View File
@@ -1,47 +0,0 @@
#include "Tactility/DispatcherThread.h"
namespace tt {
DispatcherThread::DispatcherThread(const std::string& threadName, size_t threadStackSize) {
thread = std::make_unique<Thread>(
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();
}
}
-135
View File
@@ -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
-9
View File
@@ -1,9 +0,0 @@
#include "Tactility/Lock.h"
namespace tt {
ScopedLock Lock::asScopedLock() const {
return ScopedLock(*this);
}
}
-136
View File
@@ -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
-79
View File
@@ -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
-71
View File
@@ -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
-386
View File
@@ -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 <string>
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<UBaseType_t>(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<Thread*>(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<UBaseType_t>(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<UBaseType_t>(priority),
&taskHandle
);
#endif
} else {
result = xTaskCreate(
mainBody,
name.c_str(),
stack_depth,
this,
static_cast<UBaseType_t>(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<Thread*>(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
-88
View File
@@ -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<Timer*>(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<UBaseType_t>(priority));
}
} // namespace
+1 -1
View File
@@ -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<Lock> noLock = std::make_shared<NoLock>();
-167
View File
@@ -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 <cassert>
#include <unistd.h>
#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
+13
View File
@@ -0,0 +1,13 @@
#include "Tactility/kernel/Platform.h"
namespace tt::kernel {
Platform getPlatform() {
#ifdef ESP_PLATFORM
return PlatformEsp;
#else
return PlatformSimulator;
#endif
}
} // namespace
@@ -1,13 +1,15 @@
#include "Tactility/kernel/critical/Critical.h"
#include <Tactility/kernel/critical/Critical.h>
#include "Tactility/RtosCompatTask.h"
#include "Tactility/kernel/Kernel.h"
#include <Tactility/freertoscompat/Task.h>
#include <Tactility/kernel/Kernel.h>
#ifdef ESP_PLATFORM
static portMUX_TYPE critical_mutex;
#define TT_ENTER_CRITICAL() taskENTER_CRITICAL(&critical_mutex)
#define TT_EXIT_CRITICAL() taskEXIT_CRITICAL(&critical_mutex)
#else
#define TT_ENTER_CRITICAL() taskENTER_CRITICAL()
#define TT_EXIT_CRITICAL() taskEXIT_CRITICAL()
#endif
namespace tt::kernel::critical {
@@ -15,7 +17,7 @@ namespace tt::kernel::critical {
CriticalInfo enter() {
CriticalInfo info = {
.isrm = 0,
.fromIsr = kernel::isIsr(),
.fromIsr = (xPortInIsrContext() == pdTRUE),
.kernelRunning = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)
};
@@ -34,7 +36,7 @@ void exit(CriticalInfo info) {
if (info.fromIsr) {
taskEXIT_CRITICAL_FROM_ISR(info.isrm);
} else if (info.kernelRunning) {
TT_ENTER_CRITICAL();
TT_EXIT_CRITICAL();
} else {
portENABLE_INTERRUPTS();
}