Implement UI scaling and more (#501)

**New Features**
 * Runtime font accessors and new symbol fonts for text, launcher, statusbar, and shared icons.
 * Added font height base setting to device.properties
 * Text fonts now have 3 sizes: small, default, large

**Improvements**
 * Renamed `UiScale` to `UiDensity`
 * Statusbar, toolbar and many UI components now compute heights and spacing from fonts/density.
 * SSD1306 initialization sequence refined for more stable startup.
 * Multiple image assets replaced by symbol-font rendering.
 * Many layout improvements related to density, font scaling and icon scaling
 * Updated folder name capitalization for newer style
This commit is contained in:
Ken Van Hoeylandt
2026-02-15 01:41:47 +01:00
committed by GitHub
parent 72c9b2b113
commit 9a11e6f47b
264 changed files with 5923 additions and 494 deletions
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
/**
* Creates required aliases for the devicetree generation.
* @param compatible_name the "compatible" value for the related driver
* @param config_type the internal configuration type for a device
*/
#define DEFINE_DEVICETREE(compatible_name, config_type) typedef config_type compatible_name##_config_dt;
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/drivers/gpio.h>
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/bindings/bindings.h>
#include <tactility/drivers/root.h>
DEFINE_DEVICETREE(root, struct RootConfig)
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <tactility/log.h>
__attribute__((noreturn)) extern void __crash(void);
#define CHECK_GET_MACRO(_1, _2, NAME, ...) NAME
#define check(...) CHECK_GET_MACRO(__VA_ARGS__, CHECK_MSG, CHECK_NO_MSG)(__VA_ARGS__)
#define CHECK_NO_MSG(condition) \
do { \
if (!(condition)) { \
LOG_E("Error", "Check failed: %s\n\tat %s:%d", #condition, __FILE__, __LINE__); \
__crash(); \
} \
} while (0)
#define CHECK_MSG(condition, message) \
do { \
if (!(condition)) { \
LOG_E("Error", "Check failed: %s\n\tat %s:%d", message, __FILE__, __LINE__); \
__crash(); \
} \
} while (0)
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Dispatcher is a thread-safe code execution queue.
*/
#pragma once
#include <tactility/freertos/freertos.h>
#include <tactility/error.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*DispatcherCallback)(void* context);
typedef void* DispatcherHandle_t;
DispatcherHandle_t dispatcher_alloc(void);
void dispatcher_free(DispatcherHandle_t dispatcher);
/**
* Queue a function to be consumed elsewhere.
*
* @param[in] callbackContext the data to pass to the function upon execution
* @param[in] callback the function to execute elsewhere
* @param[in] timeout lock acquisition timeout
* @retval ERROR_TIMEOUT
* @retval ERROR_RESOURCE when failing to set event
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
* @retval ERROR_NONE
*/
error_t dispatcher_dispatch_timed(DispatcherHandle_t dispatcher, void* callbackContext, DispatcherCallback callback, TickType_t timeout);
/**
* Queue a function to be consumed elsewhere.
*
* @param[in] callbackContext the data to pass to the function upon execution
* @param[in] callback the function to execute elsewhere
* @retval ERROR_RESOURCE when failing to set event
* @retval ERROR_TIMEOUT unlikely to occur unless there's an issue with the internal mutex
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
* @retval ERROR_NONE
*/
static inline error_t dispatcher_dispatch(DispatcherHandle_t dispatcher, void* callbackContext, DispatcherCallback callback) {
return dispatcher_dispatch_timed(dispatcher, callbackContext, callback, portMAX_DELAY);
}
/**
* Consume 1 or more dispatched functions (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
* @retval ERROR_TIMEOUT
* @retval ERROR_RESOURCE failed to wait for event
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
* @retval ERROR_NONE
*/
error_t dispatcher_consume_timed(DispatcherHandle_t dispatcher, TickType_t timeout);
/**
* Consume 1 or more dispatched functions (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.
*
* @retval ERROR_TIMEOUT unlikely to occur unless there's an issue with the internal mutex
* @retval ERROR_RESOURCE failed to wait for event
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
* @retval ERROR_NONE
*/
static inline error_t dispatcher_consume(DispatcherHandle_t dispatcher) {
return dispatcher_consume_timed(dispatcher, portMAX_DELAY);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <tactility/freertos/event_groups.h>
#include <tactility/check.h>
#include <tactility/error.h>
static inline void event_group_construct(EventGroupHandle_t* eventGroup) {
check(xPortInIsrContext() == pdFALSE);
*eventGroup = xEventGroupCreate();
}
static inline void event_group_destruct(EventGroupHandle_t* eventGroup) {
check(xPortInIsrContext() == pdFALSE);
check(*eventGroup != NULL);
vEventGroupDelete(*eventGroup);
*eventGroup = NULL;
}
/**
* Set the flags.
*
* @param[in] eventGroup the event group
* @param[in] flags the flags to set
* @retval ERROR_RESOURCE when setting failed
* @retval ERROR_NONE
*/
error_t event_group_set(EventGroupHandle_t eventGroup, uint32_t flags);
/**
* Clear flags
*
* @param[in] eventGroup the event group
* @param[in] flags the flags to clear
* @retval ERROR_RESOURCE when clearing failed
* @retval ERROR_NONE
*/
error_t event_group_clear(EventGroupHandle_t eventGroup, uint32_t flags);
/**
* @param[in] eventGroup the event group
* @return the bitset (always succeeds)
*/
uint32_t event_group_get(EventGroupHandle_t eventGroup);
/**
* Wait for flags to be set
*
* @param[in] eventGroup the event group
* @param[in] inFlags 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[out] outFlags If set to non-NULL value, this will hold the resulting flags. Only set when return value is ERROR_NONE
* @param[in] timeout the maximum amount of ticks to wait for flags to be set
* @retval ERROR_ISR_STATUS when the function was called from an ISR context
* @retval ERROR_TIMEOUT
* @retval ERROR_RESOURCE when flags were triggered, but not in a way that was expected (e.g. waiting for all flags, but was only partially set)
* @retval ERROR_NONE
*/
error_t event_group_wait(
EventGroupHandle_t eventGroup,
uint32_t inFlags,
bool awaitAll,
bool clearOnExit,
uint32_t* outFlags,
TickType_t timeout
);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <assert.h>
#include <stdbool.h>
#include <tactility/freertos/semphr.h>
#include <tactility/check.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Mutex {
QueueHandle_t handle;
// TODO: Debugging functionality
};
inline static void mutex_construct(struct Mutex* mutex) {
mutex->handle = xSemaphoreCreateMutex();
}
inline static void mutex_destruct(struct Mutex* mutex) {
vPortAssertIfInISR();
vSemaphoreDelete(mutex->handle);
mutex->handle = NULL;
}
inline static void mutex_lock(struct Mutex* mutex) {
check(xPortInIsrContext() != pdTRUE);
xSemaphoreTake(mutex->handle, portMAX_DELAY);
}
inline static bool mutex_try_lock(struct Mutex* mutex, TickType_t timeout) {
check(xPortInIsrContext() != pdTRUE);
return xSemaphoreTake(mutex->handle, timeout) == pdTRUE;
}
inline static bool mutex_is_locked(struct Mutex* mutex) {
if (xPortInIsrContext() == pdTRUE) {
return xSemaphoreGetMutexHolderFromISR(mutex->handle) != NULL;
} else {
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
}
}
inline static void mutex_unlock(struct Mutex* mutex) {
check(xPortInIsrContext() != pdTRUE);
xSemaphoreGive(mutex->handle);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/freertos/semphr.h>
#include <tactility/check.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
struct RecursiveMutex {
QueueHandle_t handle;
// TODO: Debugging functionality
};
inline static void recursive_mutex_construct(struct RecursiveMutex* mutex) {
mutex->handle = xSemaphoreCreateRecursiveMutex();
}
inline static void recursive_mutex_destruct(struct RecursiveMutex* mutex) {
check(xPortInIsrContext() != pdTRUE);
vSemaphoreDelete(mutex->handle);
mutex->handle = NULL;
}
inline static void recursive_mutex_lock(struct RecursiveMutex* mutex) {
check(xPortInIsrContext() != pdTRUE);
xSemaphoreTakeRecursive(mutex->handle, portMAX_DELAY);
}
inline static bool recursive_mutex_is_locked(struct RecursiveMutex* mutex) {
if (xPortInIsrContext() == pdTRUE) {
return xSemaphoreGetMutexHolderFromISR(mutex->handle) != NULL;
} else {
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
}
}
inline static bool recursive_mutex_try_lock(struct RecursiveMutex* mutex, TickType_t timeout) {
check(xPortInIsrContext() != pdTRUE);
return xSemaphoreTakeRecursive(mutex->handle, timeout) == pdTRUE;
}
inline static void recursive_mutex_unlock(struct RecursiveMutex* mutex) {
check(xPortInIsrContext() != pdTRUE);
xSemaphoreGiveRecursive(mutex->handle);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,182 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "tactility/error.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef ESP_PLATFORM
#include <esp_log.h>
#endif
#include <tactility/freertos/task.h>
#include <tactility/concurrent/mutex.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
typedef enum {
THREAD_STATE_STOPPED,
THREAD_STATE_STARTING,
THREAD_STATE_RUNNING,
} ThreadState;
/** ThreadPriority */
enum ThreadPriority {
THREAD_PRIORITY_NONE = 0U,
THREAD_PRIORITY_IDLE = 1U,
THREAD_PRIORITY_LOWER = 2U,
THREAD_PRIORITY_LOW = 3U,
THREAD_PRIORITY_NORMAL = 4U,
THREAD_PRIORITY_HIGH = 5U,
THREAD_PRIORITY_HIGHER = 6U,
THREAD_PRIORITY_CRITICAL = 7U
};
typedef int32_t (*thread_main_fn_t)(void* context);
typedef void (*thread_state_callback_t)(ThreadState state, void* context);
struct Thread;
typedef struct Thread Thread;
/**
* @brief Creates a new thread instance with default settings.
* @return A pointer to the created Thread instance, or NULL if allocation failed.
*/
Thread* thread_alloc(void);
/**
* @brief Creates a new thread instance with specified parameters.
* @param[in] name The name of the thread.
* @param[in] stack_size The size of the thread stack in bytes.
* @param[in] function The main function to be executed by the thread.
* @param[in] function_context A pointer to the context to be passed to the main function.
* @param[in] affinity The CPU core affinity for the thread (e.g., tskNO_AFFINITY).
* @return A pointer to the created Thread instance, or NULL if allocation failed.
*/
Thread* thread_alloc_full(
const char* name,
configSTACK_DEPTH_TYPE stack_size,
thread_main_fn_t function,
void* function_context,
portBASE_TYPE affinity
);
/**
* @brief Destroys a thread instance.
* @param[in] thread The thread instance to destroy.
* @note The thread must be in the STOPPED state.
*/
void thread_free(Thread* thread);
/**
* @brief Sets the name of the thread.
* @param[in] thread The thread instance.
* @param[in] name The new name for the thread.
* @note Can only be called when the thread is in the STOPPED state.
*/
void thread_set_name(Thread* thread, const char* name);
/**
* @brief Sets the stack size for the thread.
* @param[in] thread The thread instance.
* @param[in] stack_size The stack size in bytes. Must be a multiple of 4.
* @note Can only be called when the thread is in the STOPPED state.
*/
void thread_set_stack_size(Thread* thread, size_t stack_size);
/**
* @brief Sets the CPU core affinity for the thread.
* @param[in] thread The thread instance.
* @param[in] affinity The CPU core affinity.
* @note Can only be called when the thread is in the STOPPED state.
*/
void thread_set_affinity(Thread* thread, portBASE_TYPE affinity);
/**
* @brief Sets the main function and context for the thread.
* @param[in] thread The thread instance.
* @param[in] function The main function to be executed.
* @param[in] context A pointer to the context to be passed to the main function.
* @note Can only be called when the thread is in the STOPPED state.
*/
void thread_set_main_function(Thread* thread, thread_main_fn_t function, void* context);
/**
* @brief Sets the priority for the thread.
* @param[in] thread The thread instance.
* @param[in] priority The thread priority.
* @note Can only be called when the thread is in the STOPPED state.
*/
void thread_set_priority(Thread* thread, enum ThreadPriority priority);
/**
* @brief Sets a callback to be invoked when the thread state changes.
* @param[in] thread The thread instance.
* @param[in] callback The callback function.
* @param[in] context A pointer to the context to be passed to the callback function.
* @note Can only be called when the thread is in the STOPPED state.
*/
void thread_set_state_callback(Thread* thread, thread_state_callback_t callback, void* context);
/**
* @brief Gets the current state of the thread.
* @param[in] thread The thread instance.
* @return The current ThreadState.
*/
ThreadState thread_get_state(Thread* thread);
/**
* @brief Starts the thread execution.
* @param[in] thread The thread instance.
* @note The thread must be in the STOPPED state and have a main function set.
* @retval ERROR_NONE when the thread was started
* @retval ERROR_UNDEFINED when the thread failed to start
*/
error_t thread_start(Thread* thread);
/**
* @brief Waits for the thread to finish execution.
* @param[in] thread The thread instance.
* @param[in] timeout The maximum time to wait in ticks.
* @param[in] poll_interval The interval between status checks in ticks.
* @retval ERROR_NONE when the thread was stopped
* @retval ERROR_TIMEOUT when the thread was not stopped because the timeout has passed
* @note Cannot be called from the thread being joined.
*/
error_t thread_join(Thread* thread, TickType_t timeout, TickType_t poll_interval);
/**
* @brief Gets the FreeRTOS task handle associated with the thread.
* @param[in] thread The thread instance.
* @return The TaskHandle_t, or NULL if the thread is not running.
*/
TaskHandle_t thread_get_task_handle(Thread* thread);
/**
* @brief Gets the return code from the thread's main function.
* @param[in] thread The thread instance.
* @return The return code of the thread's main function.
* @note The thread must be in the STOPPED state.
*/
int32_t thread_get_return_code(Thread* thread);
/**
* @brief Gets the minimum remaining stack space for the thread since it started.
* @param[in] thread The thread instance.
* @return The minimum remaining stack space in bytes.
* @note The thread must be in the RUNNING state.
*/
uint32_t thread_get_stack_space(Thread* thread);
/**
* @brief Gets the current thread instance.
* @return A pointer to the current Thread instance, or NULL if not called from a thread created by this module.
*/
Thread* thread_get_current(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "tactility/error.h"
#include "tactility/freertos/timers.h"
#include "tactility/concurrent/thread.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
enum TimerType {
TIMER_TYPE_ONCE = 0, // Timer triggers once after time has passed
TIMER_TYPE_PERIODIC = 1 // Timer triggers repeatedly after time has passed
};
typedef void (*timer_callback_t)(void* context);
typedef void (*timer_pending_callback_t)(void* context, uint32_t arg);
struct Timer;
/**
* @brief Creates a new timer instance.
* @param[in] type The timer type.
* @param[in] ticks The timer period in ticks.
* @param[in] callback The callback function.
* @param[in] context The context to pass to the callback function.
* @return A pointer to the created timer instance, or NULL if allocation failed.
*/
struct Timer* timer_alloc(enum TimerType type, TickType_t ticks, timer_callback_t callback, void* context);
/**
* @brief Destroys a timer instance.
* @param[in] timer The timer instance to destroy.
*/
void timer_free(struct Timer* timer);
/**
* @brief Starts the timer.
* @param[in] timer The timer instance.
* @return ERROR_NONE on success, ERROR_TIMEOUT if the command queue was full.
*/
error_t timer_start(struct Timer* timer);
/**
* @brief Stops the timer.
* @param[in] timer The timer instance.
* @return ERROR_NONE on success, ERROR_TIMEOUT if the command queue was full.
*/
error_t timer_stop(struct Timer* timer);
/**
* @brief Set a new interval and reset the timer.
* @param[in] timer The timer instance.
* @param[in] interval The new timer interval in ticks.
* @return ERROR_NONE on success, ERROR_TIMEOUT if the command queue was full.
*/
error_t timer_reset_with_interval(struct Timer* timer, TickType_t interval);
/**
* @brief Reset the timer.
* @param[in] timer The timer instance.
* @return ERROR_NONE on success, ERROR_TIMEOUT if the command queue was full.
*/
error_t timer_reset(struct Timer* timer);
/**
* @brief Check if the timer is running.
* @param[in] timer The timer instance.
* @return true when the timer is running.
*/
bool timer_is_running(struct Timer* timer);
/**
* @brief Gets the expiry time of the timer.
* @param[in] timer The timer instance.
* @return The expiry time in ticks.
*/
TickType_t timer_get_expiry_time(struct Timer* timer);
/**
* @brief Calls xTimerPendFunctionCall internally.
* @param[in] timer The timer instance.
* @param[in] callback the function to call
* @param[in] context the first function argument
* @param[in] arg the second function argument
* @param[in] timeout the function timeout (must set to 0 in ISR mode)
* @return ERROR_NONE on success, ERROR_TIMEOUT if the command queue was full.
*/
error_t timer_set_pending_callback(struct Timer* timer, timer_pending_callback_t callback, void* context, uint32_t arg, TickType_t timeout);
/**
* @brief Set callback priority (priority of the timer daemon task).
* @param[in] timer The timer instance.
* @param[in] priority The priority.
*/
void timer_set_callback_priority(struct Timer* timer, enum ThreadPriority priority);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
/**
* @brief Contains various unsorted defines
* @note Preprocessor defines with potentially clashing names implement an #ifdef check.
*/
#pragma once
#ifndef MIN
/** @brief Get the minimum value of 2 values */
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
/** @brief Get the maximum value of 2 values */
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef CLAMP
/** @brief Clamp a value between the provided minimum and maximum */
#define CLAMP(min, max, value) (((value) < (min)) ? (min) : (((value) > (max)) ? (max) : (value)))
#endif
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include "time.h"
#include <stdint.h>
#ifdef ESP_PLATFORM
#include <rom/ets_sys.h>
#else
#include <unistd.h>
#endif
#include <tactility/freertos/freertos.h>
#include <tactility/check.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Delay the current task for the specified amount of ticks
* @warning Does not work in ISR context
*/
static inline void delay_ticks(TickType_t ticks) {
check(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
*/
static inline void delay_millis(uint32_t milliSeconds) {
delay_ticks(millis_to_ticks(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.
*/
static inline void delay_micros(uint32_t microseconds) {
#ifdef ESP_PLATFORM
ets_delay_us(microseconds);
#else
usleep(microseconds);
#endif
}
#ifdef __cplusplus
}
#endif
+278
View File
@@ -0,0 +1,278 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "driver.h"
#include "error.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <tactility/concurrent/mutex.h>
#include <tactility/freertos/freertos.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Driver;
struct DeviceInternal;
/** Enables discovering devices of the same type */
struct DeviceType {
const char* name;
};
/** Represents a piece of hardware */
struct Device {
/** The name of the device. Valid characters: a-z a-Z 0-9 - _ . */
const char* name;
/** The configuration data for the device's driver */
const void* config;
/** The parent device that this device belongs to. Can be NULL, but only the root device should have a NULL parent. */
struct Device* parent;
/**
* Internal state managed by the kernel.
* Device implementers should initialize this to NULL.
* Do not access or modify directly; use device_* functions.
*/
struct DeviceInternal* internal;
};
/**
* Initialize the properties of a device.
*
* @param[in,out] device a device with all non-internal properties set
* @retval ERROR_OUT_OF_MEMORY if internal data allocation failed
* @retval ERROR_NONE on success
*/
error_t device_construct(struct Device* device);
/**
* Deinitialize the properties of a device.
* This fails when a device is busy or has children.
*
* @param[in,out] device non-null device pointer
* @retval ERROR_INVALID_STATE if the device is busy or has children
* @retval ERROR_NONE on success
*/
error_t device_destruct(struct Device* device);
/**
* Register a device to all relevant systems:
* - the global ledger
* - its parent (if any)
*
* @param[in,out] device non-null device pointer
* @retval ERROR_INVALID_STATE if the device is already added
* @retval ERROR_NONE on success
*/
error_t device_add(struct Device* device);
/**
* Deregister a device. Remove it from all relevant systems:
* - the global ledger
* - its parent (if any)
*
* @param[in,out] device non-null device pointer
* @retval ERROR_INVALID_STATE if the device is still started
* @retval ERROR_NOT_FOUND if the device was not found in the system
* @retval ERROR_NONE on success
*/
error_t device_remove(struct Device* device);
/**
* Attach the driver.
*
* @warning must call device_construct() and device_add() first
* @param[in,out] device non-null device pointer
* @retval ERROR_INVALID_STATE if the device is already started or not added
* @retval ERROR_RESOURCE when driver binding fails
* @retval ERROR_NONE on success
*/
error_t device_start(struct Device* device);
/**
* Detach the driver.
*
* @param[in,out] device non-null device pointer
* @retval ERROR_INVALID_STATE if the device is not started
* @retval ERROR_RESOURCE when driver unbinding fails
* @retval ERROR_NONE on success
*/
error_t device_stop(struct Device* device);
/**
* Construct and add a device with the given compatible string.
*
* @param[in,out] device non-NULL device
* @param[in] compatible compatible string
* @retval ERROR_NONE on success
* @retval error_t error code on failure
*/
error_t device_construct_add_start(struct Device* device, const char* compatible);
/**
* Construct and add a device with the given compatible string.
*
* @param[in,out] device non-NULL device
* @param[in] compatible compatible string
* @retval ERROR_NONE on success
* @retval error_t error code on failure
*/
error_t device_construct_add(struct Device* device, const char* compatible);
/**
* Set or unset a parent.
*
* @warning must call before device_add()
* @param[in,out] device non-NULL device
* @param[in] parent nullable parent device
*/
void device_set_parent(struct Device* device, struct Device* parent);
/**
* Set the driver for a device.
*
* @warning must call before device_add()
* @param[in,out] device non-NULL device
* @param[in] driver nullable driver
*/
void device_set_driver(struct Device* device, struct Driver* driver);
/**
* Get the driver for a device.
*
* @param[in] device non-null device pointer
* @return the driver, or NULL if the device has no driver
*/
struct Driver* device_get_driver(struct Device* device);
/**
* Get the parent device of a device.
*
* @param[in] device non-null device pointer
* @return the parent device, or NULL if the device has no parent
*/
struct Device* device_get_parent(struct Device* device);
/**
* Indicates whether the device is in a state where its API is available
*
* @param[in] device non-null device pointer
* @return true if the device is ready for use
*/
bool device_is_ready(const struct Device* device);
/**
* Indicates whether the device is compatible with the given compatible string.
* @param[in] device non-null device pointer
* @param[in] compatible compatible string
* @return true if the device is compatible
*/
bool device_is_compatible(const struct Device* device, const char* compatible);
/**
* Set the driver data for a device.
*
* @param[in,out] device non-null device pointer
* @param[in] driver_data the driver data
*/
void device_set_driver_data(struct Device* device, void* driver_data);
/**
* Get the driver data for a device.
*
* @param[in] device non-null device pointer
* @return the driver data
*/
void* device_get_driver_data(struct Device* device);
/**
* Indicates whether the device has been added to the system.
*
* @param[in] device non-null device pointer
* @return true if the device has been added
*/
bool device_is_added(const struct Device* device);
/**
* Lock the device for exclusive access.
*
* @param[in,out] device non-null device pointer
*/
void device_lock(struct Device* device);
/**
* Try to lock the device for exclusive access.
*
* @param[in,out] device non-null device pointer
* @param[in] timeout how long to wait for the lock
* @return true if the device was locked successfully
*/
bool device_try_lock(struct Device* device, TickType_t timeout);
/**
* Unlock the device.
*
* @param[in,out] device non-null device pointer
*/
void device_unlock(struct Device* device);
/**
* Get the type of a device.
*
* @param[in] device non-null device pointer
* @return the device type
*/
const struct DeviceType* device_get_type(struct Device* device);
/**
* Iterate through all the known devices
*
* @param[in] callback_context the parameter to pass to the callback. NULL is valid.
* @param[in] on_device the function to call for each filtered device. return true to continue iterating or false to stop.
*/
void device_for_each(void* callback_context, bool(*on_device)(struct Device* device, void* context));
/**
* Iterate through all the child devices of the specified device
*
* @param[in] device non-null device pointer
* @param[in] callback_context the parameter to pass to the callback. NULL is valid.
* @param[in] on_device the function to call for each filtered device. return true to continue iterating or false to stop.
*/
void device_for_each_child(struct Device* device, void* callback_context, bool(*on_device)(struct Device* device, void* context));
/**
* Iterate through all the known devices of a specific type
*
* @param[in] type the type to filter
* @param[in] callback_context the parameter to pass to the callback. NULL is valid.
* @param[in] on_device the function to call for each filtered device. return true to continue iterating or false to stop.
*/
void device_for_each_of_type(const struct DeviceType* type, void* callback_context, bool(*on_device)(struct Device* device, void* context));
/**
* Check if a device of the specified type exists.
*
* @param[in] type the type to check
* @return true if a device of the specified type exists
*/
bool device_exists_of_type(const struct DeviceType* type);
/**
* Find a device by its name.
*
* @param[in] name non-null device name to look up
* @return the device pointer if found, or NULL if not found
*/
struct Device* device_find_by_name(const char* name);
#ifdef __cplusplus
}
#endif
+145
View File
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "error.h"
#include <stdbool.h>
struct Device;
struct DeviceType;
struct Module;
struct DriverInternal;
struct Driver {
/** The driver name */
const char* name;
/** Array of const char*, terminated by NULL */
const char**compatible;
/** Function to initialize the driver for a device */
error_t (*start_device)(struct Device* dev);
/** Function to deinitialize the driver for a device */
error_t (*stop_device)(struct Device* dev);
/** Contains the driver's functions */
const void* api;
/** Which type of devices this driver creates (can be NULL) */
const struct DeviceType* device_type;
/** The module that owns this driver. When it is NULL, the system owns the driver and it cannot be removed from registration. */
const struct Module* owner;
/**
* Internal state managed by the kernel.
* Driver implementers should initialize this to NULL.
* Do not access or modify directly; use driver_* functions.
*/
struct DriverInternal* internal;
};
/**
* @brief Construct a driver.
*
* This initializes the internal state of the driver.
*
* @param driver The driver to construct.
* @return ERROR_NONE if successful, or an error code otherwise.
*/
error_t driver_construct(struct Driver* driver);
/**
* @brief Destruct a driver.
*
* This cleans up the internal state of the driver.
*
* @param driver The driver to destruct.
* @return ERROR_NONE if successful, or an error code otherwise.
*/
error_t driver_destruct(struct Driver* driver);
/**
* @brief Add a driver to the system.
*
* This registers the driver so it can be used to bind to devices.
*
* @param driver The driver to add.
* @return ERROR_NONE if successful, or an error code otherwise.
*/
error_t driver_add(struct Driver* driver);
/**
* @brief Remove a driver from the system.
*
* This unregisters the driver.
*
* @param driver The driver to remove.
* @return ERROR_NONE if successful, or an error code otherwise.
*/
error_t driver_remove(struct Driver* driver);
/**
* @brief Construct and add a driver to the system.
*
* @param driver The driver to construct and add.
* @return ERROR_NONE if successful, or an error code otherwise.
*/
error_t driver_construct_add(struct Driver* driver);
/**
* @brief Remove and destruct a driver.
*
* @param driver The driver to remove and destruct.
* @return ERROR_NONE if successful, or an error code otherwise.
*/
error_t driver_remove_destruct(struct Driver* driver);
/**
* @brief Bind a driver to a device.
*
* This calls the driver's start_device function and increments the driver's use count.
*
* @param driver The driver to bind.
* @param device The device to bind to.
* @return ERROR_NONE if successful, or an error code otherwise.
*/
error_t driver_bind(struct Driver* driver, struct Device* device);
/**
* @brief Unbind a driver from a device.
*
* This calls the driver's stop_device function and decrements the driver's use count.
*
* @param driver The driver to unbind.
* @param device The device to unbind from.
* @return ERROR_NONE if successful, or an error code otherwise.
*/
error_t driver_unbind(struct Driver* driver, struct Device* device);
/**
* @brief Check if a driver is compatible with a given string.
*
* @param driver The driver to check.
* @param compatible The compatibility string to check.
* @return true if compatible, false otherwise.
*/
bool driver_is_compatible(struct Driver* driver, const char* compatible);
/**
* @brief Find a driver compatible with a given string.
*
* @param compatible The compatibility string to find.
* @return The compatible driver, or NULL if not found.
*/
struct Driver* driver_find_compatible(const char* compatible);
/**
* @brief Get the device type of a driver.
*
* @param driver The driver to get the device type from.
* @return The device type of the driver.
*/
const struct DeviceType* driver_get_device_type(struct Driver* driver);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <tactility/device.h>
#include <stdbool.h>
#include <stdint.h>
#define GPIO_FLAGS_MASK 0x1f
#define GPIO_PIN_NONE -1
#define GPIO_PIN_SPEC_NONE ((struct GpioPinSpec) { NULL, 0, GPIO_FLAG_NONE })
#define GPIO_FLAG_NONE 0
#define GPIO_FLAG_ACTIVE_HIGH (0 << 0)
#define GPIO_FLAG_ACTIVE_LOW (1 << 0)
#define GPIO_FLAG_DIRECTION_INPUT (1 << 1)
#define GPIO_FLAG_DIRECTION_OUTPUT (1 << 2)
#define GPIO_FLAG_DIRECTION_INPUT_OUTPUT (GPIO_FLAG_DIRECTION_INPUT | GPIO_FLAG_DIRECTION_OUTPUT)
#define GPIO_FLAG_PULL_UP (0 << 3)
#define GPIO_FLAG_PULL_DOWN (1 << 4)
#define GPIO_FLAG_INTERRUPT_BITMASK (0b111 << 5) // 3 bits to hold the values [0, 5]
#define GPIO_FLAG_INTERRUPT_FROM_OPTIONS(options) (gpio_int_type_t)((options & GPIO_FLAG_INTERRUPT_BITMASK) >> 5)
#define GPIO_FLAG_INTERRUPT_TO_OPTIONS(options, interrupt) (options | (interrupt << 5))
typedef enum {
GPIO_INTERRUPT_DISABLE = 0,
GPIO_INTERRUPT_POS_EDGE = 1,
GPIO_INTERRUPT_NEG_EDGE = 2,
GPIO_INTERRUPT_ANY_EDGE = 3,
GPIO_INTERRUPT_LOW_LEVEL = 4,
GPIO_INTERRUPT_HIGH_LEVEL = 5,
GPIO_MAX,
} GpioInterruptType;
enum GpioOwnerType {
/** @brief Pin is unclaimed/free */
GPIO_OWNER_NONE,
/** @brief Pin is owned by a hog */
GPIO_OWNER_HOG,
/** @brief Pin is claimed by a regular consumer */
GPIO_OWNER_GPIO,
/** @brief Pin is owned by SPI. This is a special case because of CS pin transfer from hog to SPI controller. */
GPIO_OWNER_SPI
};
/** The index of a GPIO pin on a GPIO Controller */
typedef uint8_t gpio_pin_t;
/** Specifies the configuration flags for a GPIO pin (or set of pins) */
typedef uint16_t gpio_flags_t;
/**
* Specifies a pin and its properties for a specific GPIO controller.
* Used by the devicetree, drivers and application code to refer to GPIO pins and acquire them via the gpio_controller API.
*/
struct GpioPinSpec {
/** GPIO device controlling the pin */
struct Device* gpio_controller;
/** The pin's number on the device */
gpio_pin_t pin;
/** The pin's configuration flags as specified in devicetree */
gpio_flags_t flags;
};
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,148 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "gpio.h"
#include <tactility/error.h>
struct Device;
struct GpioDescriptor;
struct GpioControllerApi {
/**
* @brief Sets the logical level of a GPIO pin.
* @param[in,out] descriptor the pin descriptor
* @param[in] high true to set the pin high, false to set it low
* @return ERROR_NONE if successful
*/
error_t (*set_level)(struct GpioDescriptor* descriptor, bool high);
/**
* @brief Gets the logical level of a GPIO pin.
* @param[in] descriptor the pin descriptor
* @param[out] high pointer to store the pin level
* @return ERROR_NONE if successful
*/
error_t (*get_level)(struct GpioDescriptor* descriptor, bool* high);
/**
* @brief Configures the options for a GPIO pin.
* @param[in,out] descriptor the pin descriptor
* @param[in] flags configuration flags (direction, pull-up/down, etc.)
* @return ERROR_NONE if successful
*/
error_t (*set_flags)(struct GpioDescriptor* descriptor, gpio_flags_t flags);
/**
* @brief Gets the configuration options for a GPIO pin.
* @param[in] descriptor the pin descriptor
* @param[out] flags pointer to store the configuration flags
* @return ERROR_NONE if successful
*/
error_t (*get_flags)(struct GpioDescriptor* descriptor, gpio_flags_t* flags);
/**
* @brief Gets the native pin number associated with a descriptor.
* @param[in] descriptor the pin descriptor
* @param[out] pin_number pointer to store the pin number
* @return ERROR_NONE if successful
*/
error_t (*get_native_pin_number)(struct GpioDescriptor* descriptor, void* pin_number);
};
struct GpioDescriptor* gpio_descriptor_acquire(
struct Device* controller,
gpio_pin_t pin_number,
enum GpioOwnerType owner
);
error_t gpio_descriptor_release(struct GpioDescriptor* descriptor);
/**
* @brief Gets the pin number associated with a descriptor.
* @param[in] descriptor the pin descriptor
* @param[out] pin pointer to store the pin number
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_get_pin_number(struct GpioDescriptor* descriptor, gpio_pin_t* pin);
/**
* @brief Gets the pin owner type associated with a descriptor.
* @param[in] descriptor the pin descriptor
* @param[out] owner_type pointer to output owner type
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_get_owner_type(struct GpioDescriptor* descriptor, enum GpioOwnerType* owner_type);
/**
* @brief Sets the logical level of a GPIO pin.
* @param[in] descriptor the pin descriptor
* @param[in] high true to set the pin high, false to set it low
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_set_level(struct GpioDescriptor* descriptor, bool high);
/**
* @brief Gets the logical level of a GPIO pin.
* @param[in] descriptor the pin descriptor
* @param[out] high pointer to store the pin level
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_get_level(struct GpioDescriptor* descriptor, bool* high);
/**
* @brief Configures the options for a GPIO pin.
* @param[in] descriptor the pin descriptor
* @param[in] flags configuration flags (direction, pull-up/down, etc.)
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_set_flags(struct GpioDescriptor* descriptor, gpio_flags_t flags);
/**
* @brief Gets the configuration options for a GPIO pin.
* @param[in] descriptor the pin descriptor
* @param[out] flags pointer to store the configuration flags
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_get_flags(struct GpioDescriptor* descriptor, gpio_flags_t* flags);
/**
* @brief Gets the native pin number associated with a descriptor.
* @param[in] descriptor the pin descriptor
* @param[out] pin_number pointer to store the pin number
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_get_native_pin_number(struct GpioDescriptor* descriptor, void* pin_number);
/**
* @brief Gets the number of pins supported by the controller.
* @param[in] device the GPIO controller device
* @param[out] count pointer to store the number of pins
* @return ERROR_NONE if successful
*/
error_t gpio_controller_get_pin_count(struct Device* device, uint32_t* count);
/**
* @brief Initializes GPIO descriptors for a controller.
* @param[in,out] device the GPIO controller device
* @param[in] controller_context pointer to store in the controller's internal data
* @return ERROR_NONE if successful
*/
error_t gpio_controller_init_descriptors(struct Device* device, uint32_t pin_count, void* controller_context);
/**
* @brief Deinitializes GPIO descriptors for a controller.
* @param[in,out] device the GPIO controller device
* @return ERROR_NONE if successful
*/
error_t gpio_controller_deinit_descriptors(struct Device* device);
extern const struct DeviceType GPIO_CONTROLLER_TYPE;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,16 @@
#pragma once
#include "gpio.h"
struct Device;
struct GpioDescriptor {
/** @brief The controller that owns this pin */
struct Device* controller;
/** @brief Physical pin number */
gpio_pin_t pin;
/** @brief Current owner */
enum GpioOwnerType owner_type;
/** @brief Implementation-specific context (e.g. from esp32 controller internally) */
void* controller_context;
};
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "gpio.h"
#include <tactility/freertos/freertos.h>
#include <tactility/error.h>
struct Device;
/**
* @brief API for I2C controller drivers.
*/
struct I2cControllerApi {
/**
* @brief Reads data from an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[out] data the buffer to store the read data
* @param[in] dataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*read)(struct Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
/**
* @brief Writes data to an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] data the buffer containing the data to write
* @param[in] dataSize the number of bytes to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*write)(struct Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
/**
* @brief Writes data to then reads data from an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] writeData the buffer containing the data to write
* @param[in] writeDataSize the number of bytes to write
* @param[out] readData the buffer to store the read data
* @param[in] readDataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*write_read)(struct Device* device, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout);
/**
* @brief Reads data from a register of an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] reg the register address to read from
* @param[out] data the buffer to store the read data
* @param[in] dataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*read_register)(struct Device* device, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout);
/**
* @brief Writes data to a register of an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] reg the register address to write to
* @param[in] data the buffer containing the data to write
* @param[in] dataSize the number of bytes to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*write_register)(struct Device* device, uint8_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
};
/**
* @brief Reads data from an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[out] data the buffer to store the read data
* @param[in] dataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
*/
error_t i2c_controller_read(struct Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
/**
* @brief Writes data to an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] data the buffer containing the data to write
* @param[in] dataSize the number of bytes to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
*/
error_t i2c_controller_write(struct Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
/**
* @brief Writes data to then reads data from an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] writeData the buffer containing the data to write
* @param[in] writeDataSize the number of bytes to write
* @param[out] readData the buffer to store the read data
* @param[in] readDataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the operation was successful
*/
error_t i2c_controller_write_read(struct Device* device, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout);
/**
* @brief Reads data from a register of an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] reg the register address to read from
* @param[out] data the buffer to store the read data
* @param[in] dataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
*/
error_t i2c_controller_read_register(struct Device* device, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout);
/**
* @brief Writes data to a register of an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] reg the register address to write to
* @param[in] data the buffer containing the data to write
* @param[in] dataSize the number of bytes to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
*/
error_t i2c_controller_write_register(struct Device* device, uint8_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
/**
* @brief Writes an array of register-value pairs to an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] data an array of bytes where even indices are register addresses and odd indices are values
* @param[in] dataSize the number of bytes in the data array (must be even)
* @param[in] timeout the maximum time to wait for each operation to complete
* @retval ERROR_NONE when all write operations were successful
*/
error_t i2c_controller_write_register_array(struct Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
/**
* @brief Checks if an I2C device is present at the specified address.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address to check
* @param[in] timeout the maximum time to wait for the check to complete
* @retval ERROR_NONE when a device responded at the address
*/
error_t i2c_controller_has_device_at_address(struct Device* device, uint8_t address, TickType_t timeout);
extern const struct DeviceType I2C_CONTROLLER_TYPE;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
#include "gpio.h"
#include <tactility/freertos/freertos.h>
#include <tactility/error.h>
struct Device;
/**
* @brief I2S communication format
*/
enum I2sCommunicationFormat {
I2S_FORMAT_STAND_I2S = 0x01,
I2S_FORMAT_STAND_MSB = 0x02,
I2S_FORMAT_STAND_PCM_SHORT = 0x04,
I2S_FORMAT_STAND_PCM_LONG = 0x08,
};
#define I2S_CHANNEL_NONE -1
/**
* @brief I2S Config
*/
struct I2sConfig {
enum I2sCommunicationFormat communication_format;
uint32_t sample_rate;
uint8_t bits_per_sample; // 16, 24, 32
int8_t channel_left; // I2S_CHANNEL_NONE to disable
int8_t channel_right; // I2S_CHANNEL_NONE to disable
};
/**
* @brief API for I2S controller drivers.
*/
struct I2sControllerApi {
/**
* @brief Reads data from I2S.
* @param[in] device the I2S controller device
* @param[out] data the buffer to store the read data
* @param[in] data_size the number of bytes to read
* @param[out] bytes_read the number of bytes actually read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*read)(struct Device* device, void* data, size_t data_size, size_t* bytes_read, TickType_t timeout);
/**
* @brief Writes data to I2S.
* @param[in] device the I2S controller device
* @param[in] data the buffer containing the data to write
* @param[in] data_size the number of bytes to write
* @param[out] bytes_written the number of bytes actually written
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*write)(struct Device* device, const void* data, size_t data_size, size_t* bytes_written, TickType_t timeout);
/**
* @brief Sets the I2S configuration.
* @param[in] device the I2S controller device
* @param[in] config the new configuration
* @retval ERROR_NONE when the operation was successful
*/
error_t (*set_config)(struct Device* device, const struct I2sConfig* config);
/**
* @brief Gets the current I2S configuration.
* @param[in] device the I2S controller device
* @param[out] config the buffer to store the current configuration
* @retval ERROR_NONE when the operation was successful
*/
error_t (*get_config)(struct Device* device, struct I2sConfig* config);
/**
* @brief Resets the I2S controller. Must configure it again before it can be used.
* @param[in] device the I2S controller device
* @retval ERROR_NONE when the operation was successful
*/
error_t (*reset)(struct Device* device);
};
/**
* @brief Reads data from I2S using the specified controller.
* @param[in] device the I2S controller device
* @param[out] data the buffer to store the read data
* @param[in] data_size the number of bytes to read
* @param[out] bytes_read the number of bytes actually read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
*/
error_t i2s_controller_read(struct Device* device, void* data, size_t data_size, size_t* bytes_read, TickType_t timeout);
/**
* @brief Writes data to I2S using the specified controller.
* @param[in] device the I2S controller device
* @param[in] data the buffer containing the data to write
* @param[in] data_size the number of bytes to write
* @param[out] bytes_written the number of bytes actually written
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
*/
error_t i2s_controller_write(struct Device* device, const void* data, size_t data_size, size_t* bytes_written, TickType_t timeout);
/**
* @brief Sets the I2S configuration using the specified controller.
* @param[in] device the I2S controller device
* @param[in] config the new configuration
* @retval ERROR_NONE when the operation was successful
*/
error_t i2s_controller_set_config(struct Device* device, const struct I2sConfig* config);
/**
* @brief Gets the current I2S configuration using the specified controller.
* @param[in] device the I2S controller device
* @param[out] config the buffer to store the current configuration
* @retval ERROR_NONE when the operation was successful
*/
error_t i2s_controller_get_config(struct Device* device, struct I2sConfig* config);
/**
* @brief Resets the I2S controller. Must configure it again before it can be used.
* @param[in] device the I2S controller device
* @retval ERROR_NONE when the operation was successful
*/
error_t i2s_controller_reset(struct Device* device);
extern const struct DeviceType I2S_CONTROLLER_TYPE;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
struct RootConfig {
const char* model;
};
/**
* Indicates whether the device's model matches the specified model.
* @param[in] device the device to check (non-null)
* @param[in] model the model to check against
* @return true if the device's model matches the specified model
*/
bool root_is_model(const struct Device* device, const char* model);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
#include <tactility/freertos/freertos.h>
#include <tactility/error.h>
struct Device;
/**
* @brief API for SPI controller drivers.
*/
struct SpiControllerApi {
/**
* @brief Locks the SPI controller.
* @param[in] device the SPI controller device
* @retval ERROR_NONE when the operation was successful
*/
error_t (*lock)(struct Device* device);
/**
* @brief Tries to lock the SPI controller.
* @param[in] device the SPI controller device
* @param[in] timeout the maximum time to wait for the lock
* @retval ERROR_NONE when the operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*try_lock)(struct Device* device, TickType_t timeout);
/**
* @brief Unlocks the SPI controller.
* @param[in] device the SPI controller device
* @retval ERROR_NONE when the operation was successful
*/
error_t (*unlock)(struct Device* device);
};
/**
* @brief Locks the SPI controller using the specified controller.
* @param[in] device the SPI controller device
* @retval ERROR_NONE when the operation was successful
*/
error_t spi_controller_lock(struct Device* device);
/**
* @brief Tries to lock the SPI controller using the specified controller.
* @param[in] device the SPI controller device
* @param[in] timeout the maximum ticks to wait for the lock
* @retval ERROR_NONE when the operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t spi_controller_try_lock(struct Device* device, TickType_t timeout);
/**
* @brief Unlocks the SPI controller using the specified controller.
* @param[in] device the SPI controller device
* @retval ERROR_NONE when the operation was successful
*/
error_t spi_controller_unlock(struct Device* device);
extern const struct DeviceType SPI_CONTROLLER_TYPE;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,227 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
#include <tactility/freertos/freertos.h>
#include <tactility/error.h>
struct Device;
/**
* @brief UART parity modes
*/
enum UartParity {
UART_CONTROLLER_PARITY_DISABLE = 0x0,
UART_CONTROLLER_PARITY_EVEN = 0x1,
UART_CONTROLLER_PARITY_ODD = 0x2,
};
/**
* @brief UART data bits
*/
enum UartDataBits {
UART_CONTROLLER_DATA_5_BITS = 0x0,
UART_CONTROLLER_DATA_6_BITS = 0x1,
UART_CONTROLLER_DATA_7_BITS = 0x2,
UART_CONTROLLER_DATA_8_BITS = 0x3,
};
/**
* @brief UART stop bits
*/
enum UartStopBits {
UART_CONTROLLER_STOP_BITS_1 = 0x1,
UART_CONTROLLER_STOP_BITS_1_5 = 0x2,
UART_CONTROLLER_STOP_BITS_2 = 0x3,
};
/**
* @brief UART Config
*/
struct UartConfig {
uint32_t baud_rate;
enum UartDataBits data_bits;
enum UartParity parity;
enum UartStopBits stop_bits;
};
/**
* @brief API for UART controller drivers.
*/
struct UartControllerApi {
/**
* @brief Reads a single byte from UART.
* @param[in] device the UART controller device
* @param[out] out the buffer to store the read byte
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*read_byte)(struct Device* device, uint8_t* out, TickType_t timeout);
/**
* @brief Writes a single byte to UART.
* @param[in] device the UART controller device
* @param[in] out the byte to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*write_byte)(struct Device* device, uint8_t out, TickType_t timeout);
/**
* @brief Writes multiple bytes to UART.
* @param[in] device the UART controller device
* @param[in] buffer the buffer containing the data to write
* @param[in] buffer_size the number of bytes to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*write_bytes)(struct Device* device, const uint8_t* buffer, size_t buffer_size, TickType_t timeout);
/**
* @brief Reads multiple bytes from UART.
* @param[in] device the UART controller device
* @param[out] buffer the buffer to store the read data
* @param[in] buffer_size the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*read_bytes)(struct Device* device, uint8_t* buffer, size_t buffer_size, TickType_t timeout);
/**
* @brief Returns the number of bytes available for reading.
* @param[in] device the UART controller device
* @param[out] available the number of bytes available
* @return ERROR_NONE on success
*/
error_t (*get_available)(struct Device* device, size_t* available);
/**
* @brief Sets the UART configuration.
* @param[in] device the UART controller device
* @param[in] config the new configuration
* @retval ERROR_NONE when the operation was successful
*/
error_t (*set_config)(struct Device* device, const struct UartConfig* config);
/**
* @brief Gets the current UART configuration.
* @param[in] device the UART controller device
* @param[out] config the buffer to store the current configuration
* @retval ERROR_NONE when the operation was successful
*/
error_t (*get_config)(struct Device* device, struct UartConfig* config);
/**
* @brief Opens the UART controller.
* @param[in] device the UART controller device
* @retval ERROR_NONE when the operation was successful
*/
error_t (*open)(struct Device* device);
/**
* @brief Closes the UART controller.
* @param[in] device the UART controller device
* @retval ERROR_NONE when the operation was successful
*/
error_t (*close)(struct Device* device);
/**
* @brief Checks if the UART controller is open.
* @param[in] device the UART controller device
* @return true if open, false otherwise
*/
bool (*is_open)(struct Device* device);
/**
* @brief Flushes the UART input buffer.
* @param[in] device the UART controller device
* @retval ERROR_NONE when the operation was successful
*/
error_t (*flush_input)(struct Device* device);
};
/**
* @brief Reads a single byte from UART using the specified controller.
*/
error_t uart_controller_read_byte(struct Device* device, uint8_t* out, TickType_t timeout);
/**
* @brief Writes a single byte to UART using the specified controller.
*/
error_t uart_controller_write_byte(struct Device* device, uint8_t out, TickType_t timeout);
/**
* @brief Writes multiple bytes to UART using the specified controller.
*/
error_t uart_controller_write_bytes(struct Device* device, const uint8_t* buffer, size_t buffer_size, TickType_t timeout);
/**
* @brief Reads multiple bytes from UART using the specified controller.
*/
error_t uart_controller_read_bytes(struct Device* device, uint8_t* buffer, size_t buffer_size, TickType_t timeout);
/**
* @brief Reads from UART until a specific byte is encountered.
* @param[in] device the UART controller device
* @param[out] buffer the buffer to store the read data
* @param[in] buffer_size the size of the buffer
* @param[in] until_byte the byte to look for
* @param[in] timeout the maximum time to wait for the operation to complete
* @param[in] add_null_terminator whether to add a null terminator after the until_byte
* @param[out] bytes_read the number of bytes read (excluding null terminator if added)
* @retval ERROR_NONE when the operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t uart_controller_read_until(struct Device* device, uint8_t* buffer, size_t buffer_size, uint8_t until_byte, bool add_null_terminator, size_t* bytes_read, TickType_t timeout);
/**
* @brief Get the number of bytes available for reading using the specified controller.
*/
error_t uart_controller_get_available(struct Device* device, size_t* available);
/**
* @brief Sets the UART configuration using the specified controller.
*/
error_t uart_controller_set_config(struct Device* device, const struct UartConfig* config);
/**
* @brief Gets the current UART configuration using the specified controller.
*/
error_t uart_controller_get_config(struct Device* device, struct UartConfig* config);
/**
* @brief Opens the UART controller using the specified controller.
*/
error_t uart_controller_open(struct Device* device);
/**
* @brief Closes the UART controller using the specified controller.
*/
error_t uart_controller_close(struct Device* device);
/**
* @brief Checks if the UART controller is open using the specified controller.
*/
bool uart_controller_is_open(struct Device* device);
/**
* @brief Flushes the UART input buffer using the specified controller.
*/
error_t uart_controller_flush_input(struct Device* device);
extern const struct DeviceType UART_CONTROLLER_TYPE;
#ifdef __cplusplus
}
#endif
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <stddef.h>
struct Device;
/** Signals the intended state of a device. */
enum DtsDeviceStatus {
/** Device should be constructed, added and started. */
DTS_DEVICE_STATUS_OKAY,
/** Device should be constructed and added, but not started. */
DTS_DEVICE_STATUS_DISABLED
};
/**
* Holds a device pointer and a compatible string.
* The device must not be constructed, added or started yet.
* This is used by the devicetree code generator and the application init sequence.
*/
struct DtsDevice {
/** A pointer to a device. */
struct Device* device;
/** The compatible string contains the identifier of the driver that this device is compatible with. */
const char* compatible;
/** The intended state of the device. */
const enum DtsDeviceStatus status;
};
/** Signals the end of the device array in the generated dts code. */
#define DTS_DEVICE_TERMINATOR { NULL, NULL, DTS_DEVICE_STATUS_DISABLED }
+33
View File
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Avoid potential clash with bits/types/error_t.h
#ifndef __error_t_defined
typedef int error_t;
#endif
#define ERROR_NONE 0
#define ERROR_UNDEFINED 1
#define ERROR_INVALID_STATE 2
#define ERROR_INVALID_ARGUMENT 3
#define ERROR_MISSING_PARAMETER 4
#define ERROR_NOT_FOUND 5
#define ERROR_ISR_STATUS 6
#define ERROR_RESOURCE 7 // A problem with a resource/dependency
#define ERROR_TIMEOUT 8
#define ERROR_OUT_OF_MEMORY 9
#define ERROR_NOT_SUPPORTED 10
#define ERROR_NOT_ALLOWED 11
#define ERROR_BUFFER_OVERFLOW 12
/** Convert an error_t to a human-readable text. Useful for logging. */
const char* error_to_string(error_t error);
#ifdef __cplusplus
}
#endif
@@ -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.
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "freertos.h"
#ifdef ESP_PLATFORM
#include <freertos/event_groups.h>
#else
#include <event_groups.h>
#endif
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef ESP_PLATFORM
#include <freertos/FreeRTOS.h>
#else
#include <FreeRTOS.h>
#endif
// Custom port compatibility definitins, mainly for PC compatibility
#include "port.h"
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "freertos.h"
#ifndef ESP_PLATFORM
// TactilityFreeRTOS co-existence check
#ifndef xPortInIsrContext
#define xPortInIsrContext() (pdFALSE)
#endif
// TactilityFreeRTOS co-existence check
#ifndef vPortAssertIfInISR
#define vPortAssertIfInISR()
#endif
#endif
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "freertos.h"
#ifdef ESP_PLATFORM
#include <freertos/queue.h>
#else
#include <queue.h>
#endif
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "freertos.h"
#ifdef ESP_PLATFORM
#include <freertos/semphr.h>
#else
#include <semphr.h>
#endif
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "freertos.h"
#ifdef ESP_PLATFORM
#include <freertos/task.h>
#else
#include <task.h>
#endif
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "freertos.h"
#ifdef ESP_PLATFORM
#include <freertos/timers.h>
#else
#include <timers.h>
#endif
@@ -0,0 +1,22 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <tactility/device.h>
#include <tactility/error.h>
#include <tactility/module.h>
/**
* Initialize the kernel with platform and device modules, and a device tree.
* @param platform_module The platform module to start. This module should not be constructed yet.
* @param device_module The device module to start. This module should not be constructed yet. This parameter can be NULL.
* @param dts_devices The list of generated devices from the devicetree. The array must be terminated with DTS_DEVICE_TERMINATOR. This parameter can be NULL.
* @return ERROR_NONE on success, otherwise an error code
*/
error_t kernel_init(struct Module* platform_module, struct Module* device_module, struct DtsDevice dts_devices[]);
#ifdef __cplusplus
}
#endif
+44
View File
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef ESP_PLATFORM
#include <esp_log.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** Used for log output filtering */
enum LogLevel {
LOG_LEVEL_ERROR, /*!< Critical errors, software module can not recover on its own */
LOG_LEVEL_WARNING, /*!< Error conditions from which recovery measures have been taken */
LOG_LEVEL_INFO, /*!< Information messages which describe normal flow of events */
LOG_LEVEL_DEBUG, /*!< Extra information which is not necessary for normal use (values, pointers, sizes, etc). */
LOG_LEVEL_VERBOSE /*!< Bigger chunks of debugging information, or frequent messages which can potentially flood the output. */
};
#ifndef ESP_PLATFORM
void log_generic(enum LogLevel level, const char* tag, const char* format, ...);
#define LOG_E(tag, ...) log_generic(LOG_LEVEL_ERROR, tag, ##__VA_ARGS__)
#define LOG_W(tag, ...) log_generic(LOG_LEVEL_WARNING, tag, ##__VA_ARGS__)
#define LOG_I(tag, ...) log_generic(LOG_LEVEL_INFO, tag, ##__VA_ARGS__)
#define LOG_D(tag, ...) log_generic(LOG_LEVEL_DEBUG, tag, ##__VA_ARGS__)
#define LOG_V(tag, ...) log_generic(LOG_LEVEL_VERBOSE, tag, ##__VA_ARGS__)
#else
#define LOG_E(tag, ...) ESP_LOGE(tag, ##__VA_ARGS__)
#define LOG_W(tag, ...) ESP_LOGW(tag, ##__VA_ARGS__)
#define LOG_I(tag, ...) ESP_LOGI(tag, ##__VA_ARGS__)
#define LOG_D(tag, ...) ESP_LOGD(tag, ##__VA_ARGS__)
#define LOG_V(tag, ...) ESP_LOGV(tag, ##__VA_ARGS__)
#endif
#ifdef __cplusplus
}
#endif
+146
View File
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "error.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
#define MODULE_SYMBOL_TERMINATOR { nullptr, nullptr }
#else
#define MODULE_SYMBOL_TERMINATOR { NULL, NULL }
#endif
#define DEFINE_MODULE_SYMBOL(symbol) { #symbol, (void*)&symbol }
#ifdef __cplusplus
extern "C" {
#endif
/** A binary symbol like a function or a variable. */
struct ModuleSymbol {
/** The name of the symbol. */
const char* name;
/** The address of the symbol. */
const void* symbol;
};
struct ModuleInternal;
/**
* A module is a collection of drivers or other functionality that can be loaded and unloaded at runtime.
*/
struct Module {
/**
* The name of the module, for logging/debugging purposes
* Should never be NULL.
* Characters allowed: a-z A-Z 0-9 - _ .
* Desirable format "platform-esp32", "lilygo-tdeck", etc.
*/
const char* name;
/**
* A function to initialize the module.
* Should never be NULL.
* This can be used to load drivers, initialize hardware, etc.
* @return ERROR_NONE if successful
*/
error_t (*start)(void);
/**
* Deinitializes the module.
* Should never be NULL.
* @return ERROR_NONE if successful
*/
error_t (*stop)(void);
/**
* A list of symbols exported by the module.
* Should be terminated by MODULE_SYMBOL_TERMINATOR.
* Can be a NULL value.
*/
const struct ModuleSymbol* symbols;
/**
* Internal state managed by the kernel.
* Module implementers should initialize this to NULL.
* Do not access or modify directly; use module_* functions.
*/
struct ModuleInternal* internal;
};
/**
* @brief Construct a module instance.
* @param module module instance to construct
* @return ERROR_NONE if successful
*/
error_t module_construct(struct Module* module);
/**
* @brief Destruct a module instance.
* @param module module instance to destruct
* @return ERROR_NONE if successful
*/
error_t module_destruct(struct Module* module);
/**
* @brief Add a module to the system.
* @warning Only call this once. This function does not check if it was added before.
* @param module module to add
* @return ERROR_NONE if successful
*/
error_t module_add(struct Module* module);
/**
* @brief Remove a module from the system.
* @param module module to remove
* @return ERROR_NONE if successful
*/
error_t module_remove(struct Module* module);
/**
* @brief Start the module.
* @param module module
* @return ERROR_NONE if already started, or otherwise it returns the result of the module's start function
*/
error_t module_start(struct Module* module);
/**
* @brief Stop the module.
* @param module module
* @return ERROR_NONE if successful or if the module wasn't started, or otherwise it returns the result of the module's stop function
*/
error_t module_stop(struct Module* module);
/**
* @brief Construct, add and start a module.
* @param module module
* @return ERROR_NONE if successful
*/
error_t module_construct_add_start(struct Module* module);
/**
* @brief Check if the module is started.
* @param module module to check
* @return true if the module is started, false otherwise
*/
bool module_is_started(struct Module* module);
/**
* @brief Resolve a symbol from the module.
* @details The module must be started for symbol resolution to succeed.
* @param module module
* @param symbol_name name of the symbol to resolve
* @param symbol_address pointer to store the address of the resolved symbol
* @return true if the symbol was found and the module is started, false otherwise
*/
bool module_resolve_symbol(struct Module* module, const char* symbol_name, uintptr_t* symbol_address);
/**
* @brief Resolve a symbol from any module
* @details This function iterates through all started modules in the parent and attempts to resolve the symbol.
* @param symbol_name name of the symbol to resolve
* @param symbol_address pointer to store the address of the resolved symbol
* @return true if the symbol was found, false otherwise
*/
bool module_resolve_symbol_global(const char* symbol_name, uintptr_t* symbol_address);
#ifdef __cplusplus
}
#endif
+94
View File
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Time-keeping related functionality.
* This includes functionality for both ticks and seconds.
*/
#pragma once
#ifndef __cplusplus
#include <assert.h>
#endif
#include <stdint.h>
#include "defines.h"
#include "tactility/freertos/task.h"
#ifdef ESP_PLATFORM
#include <esp_timer.h>
#else
#include <sys/time.h>
#include <time.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
// Projects that include this header must align with Tactility's frequency (e.g. apps)
#ifdef __cplusplus
static_assert(configTICK_RATE_HZ == 1000);
#else
static_assert(configTICK_RATE_HZ == 1000, "configTICK_RATE_HZ must be 1000");
#endif
static inline uint32_t get_tick_frequency() {
return configTICK_RATE_HZ;
}
/** @return the amount of ticks that have passed since FreeRTOS' main task started */
static inline TickType_t get_ticks() {
if (xPortInIsrContext() == pdTRUE) {
return xTaskGetTickCountFromISR();
} else {
return xTaskGetTickCount();
}
}
/** @return the milliseconds that have passed since FreeRTOS' main task started */
static inline size_t get_millis() {
return get_ticks() * portTICK_PERIOD_MS;
}
static inline TickType_t get_timeout_remaining_ticks(TickType_t timeout, TickType_t start_time) {
TickType_t ticks_passed = get_ticks() - start_time;
if (ticks_passed >= timeout) {
return 0;
}
return timeout - ticks_passed;
}
/** @return the frequency at which the kernel task schedulers operate */
uint32_t kernel_get_tick_frequency();
/** @return the microseconds that have passed since boot */
static inline int64_t get_micros_since_boot() {
#ifdef ESP_PLATFORM
return esp_timer_get_time();
#else
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
return ((int64_t)ts.tv_sec * 1000000LL) + (ts.tv_nsec / 1000);
}
struct timeval tv;
gettimeofday(&tv, NULL);
return ((int64_t)tv.tv_sec * 1000000LL) + tv.tv_usec;
#endif
}
/** Convert seconds to ticks */
static inline TickType_t seconds_to_ticks(uint32_t seconds) {
return (uint64_t)seconds * 1000U / portTICK_PERIOD_MS;
}
/** Convert milliseconds to ticks */
static inline TickType_t millis_to_ticks(uint32_t milliSeconds) {
#if configTICK_RATE_HZ == 1000
return (TickType_t)milliSeconds;
#else
return static_cast<TickType_t>(((float)configTICK_RATE_HZ) / 1000.0f * (float)milliSeconds);
#endif
}
#ifdef __cplusplus
}
#endif