parent
39296f1b8a
commit
3fd42eb41a
@ -0,0 +1,176 @@ |
|||||||
|
#include <stdbool.h> |
||||||
|
#include "debounce.h" |
||||||
|
#include "timebase.h" |
||||||
|
#include "malloc_safe.h" |
||||||
|
|
||||||
|
// ms debounce time
|
||||||
|
|
||||||
|
#define DEF_DEBO_TIME 20 |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
GPIO_TypeDef *GPIOx; ///< GPIO base
|
||||||
|
uint16_t pin; ///< bit mask
|
||||||
|
bool state; ///< current state
|
||||||
|
bool invert; ///< invert pin
|
||||||
|
debo_id_t id; ///< pin ID
|
||||||
|
uint32_t cb_payload; ///< payload passed to the callbac
|
||||||
|
ms_time_t debo_time; ///< debouncing time (ms)
|
||||||
|
ms_time_t counter_0; ///< counter for falling edge (ms)
|
||||||
|
ms_time_t counter_1; ///< counter for rising edge (ms)
|
||||||
|
void (*falling_cb)(uint32_t); |
||||||
|
void (*rising_cb)(uint32_t); |
||||||
|
} debo_slot_t; |
||||||
|
|
||||||
|
|
||||||
|
/** Number of allocated slots */ |
||||||
|
static size_t debo_slot_count = 0; |
||||||
|
|
||||||
|
/** Slots array */ |
||||||
|
static debo_slot_t *debo_slots; |
||||||
|
|
||||||
|
/** Next free pin ID for make_id() */ |
||||||
|
static debo_id_t next_pin_id = 1; |
||||||
|
|
||||||
|
void debo_periodic_task(void *unused); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a valid free pin ID for a new entry. |
||||||
|
* @return the ID. |
||||||
|
*/ |
||||||
|
static debo_id_t make_id(void) |
||||||
|
{ |
||||||
|
debo_id_t id = next_pin_id++; |
||||||
|
|
||||||
|
// make sure no task is given PID 0
|
||||||
|
if (next_pin_id == DEBO_PIN_NONE) { |
||||||
|
next_pin_id++; |
||||||
|
} |
||||||
|
|
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Init the debouncer */ |
||||||
|
void debounce_init(size_t slot_count) |
||||||
|
{ |
||||||
|
debo_slots = calloc_s(slot_count, sizeof(debo_slot_t)); |
||||||
|
debo_slot_count = slot_count; |
||||||
|
|
||||||
|
add_periodic_task(debo_periodic_task, NULL, 1, false); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Register a pin */ |
||||||
|
debo_id_t debo_register_pin(debo_init_t *init) |
||||||
|
{ |
||||||
|
for (size_t i = 0; i < debo_slot_count; i++) { |
||||||
|
debo_slot_t *slot = &debo_slots[i]; |
||||||
|
|
||||||
|
if (slot->id != DEBO_PIN_NONE) continue; // slot is used
|
||||||
|
|
||||||
|
slot->GPIOx = init->GPIOx; |
||||||
|
slot->pin = init->pin; |
||||||
|
slot->falling_cb = init->falling_cb; |
||||||
|
slot->rising_cb = init->rising_cb; |
||||||
|
slot->cb_payload = init->cb_payload; |
||||||
|
slot->invert = init->invert; |
||||||
|
slot->counter_0 = 0; |
||||||
|
slot->counter_1 = 0; |
||||||
|
slot->debo_time = (init->debo_time == 0) ? DEF_DEBO_TIME : init->debo_time; |
||||||
|
|
||||||
|
bool state = HAL_GPIO_ReadPin(slot->GPIOx, slot->pin); |
||||||
|
if (slot->invert) state = !state; |
||||||
|
slot->state = state; |
||||||
|
|
||||||
|
slot->id = make_id(); |
||||||
|
|
||||||
|
return slot->id; |
||||||
|
} |
||||||
|
|
||||||
|
return DEBO_PIN_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Callback that must be called every 1 ms */ |
||||||
|
void debo_periodic_task(void *unused) |
||||||
|
{ |
||||||
|
UNUSED(unused); |
||||||
|
|
||||||
|
for (size_t i = 0; i < debo_slot_count; i++) { |
||||||
|
debo_slot_t *slot = &debo_slots[i]; |
||||||
|
if (slot->id == DEBO_PIN_NONE) continue; // unused
|
||||||
|
|
||||||
|
bool state = HAL_GPIO_ReadPin(slot->GPIOx, slot->pin); |
||||||
|
if (slot->invert) state = !state; |
||||||
|
|
||||||
|
if (slot->state != state) { |
||||||
|
if (state == 0) { |
||||||
|
// falling
|
||||||
|
|
||||||
|
if (slot->counter_0++ == slot->debo_time) { |
||||||
|
slot->state = 0; |
||||||
|
|
||||||
|
if (slot->falling_cb != NULL) { |
||||||
|
slot->falling_cb(slot->cb_payload); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
// rising
|
||||||
|
|
||||||
|
if (slot->counter_1++ == slot->debo_time) { |
||||||
|
slot->state = 1; |
||||||
|
|
||||||
|
if (slot->rising_cb != NULL) { |
||||||
|
slot->rising_cb(slot->cb_payload); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
// reset counters
|
||||||
|
slot->counter_0 = 0; |
||||||
|
slot->counter_1 = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if a pin is high |
||||||
|
* @param pin_id : Slot ID |
||||||
|
* @return true if the pin is registered and is HIGH |
||||||
|
*/ |
||||||
|
bool debo_pin_state(debo_id_t pin_id) |
||||||
|
{ |
||||||
|
if (pin_id == DEBO_PIN_NONE) return false; |
||||||
|
|
||||||
|
for (size_t i = 0; i < debo_slot_count; i++) { |
||||||
|
debo_slot_t *slot = &debo_slots[i]; |
||||||
|
if (slot->id != pin_id) continue; |
||||||
|
|
||||||
|
return slot->state; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a pin entry from the debouncer. |
||||||
|
* @param pin_id : Slot ID |
||||||
|
* @return true if task found & removed. |
||||||
|
*/ |
||||||
|
bool debo_remove_pin(debo_id_t pin_id) |
||||||
|
{ |
||||||
|
if (pin_id == DEBO_PIN_NONE) return false; |
||||||
|
|
||||||
|
for (size_t i = 0; i < debo_slot_count; i++) { |
||||||
|
debo_slot_t *slot = &debo_slots[i]; |
||||||
|
if (slot->id != pin_id) continue; |
||||||
|
|
||||||
|
slot->id = DEBO_PIN_NONE; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
#ifndef MPORK_DEBOUNCE_H |
||||||
|
#define MPORK_DEBOUNCE_H |
||||||
|
|
||||||
|
#include "timebase.h" |
||||||
|
#include "stm32f1xx_hal.h" |
||||||
|
|
||||||
|
// Debouncer requires that you setup SysTick first.
|
||||||
|
|
||||||
|
/** Debounced pin ID - used for state readout */ |
||||||
|
typedef uint32_t debo_id_t; |
||||||
|
|
||||||
|
/** debo_id_t indicating unused slot */ |
||||||
|
#define DEBO_PIN_NONE 0 |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the debouncer. |
||||||
|
* |
||||||
|
* Registers the callback. |
||||||
|
* |
||||||
|
* @param pin_count : number of pin slots to allocate |
||||||
|
*/ |
||||||
|
void debounce_init(size_t pin_count); |
||||||
|
|
||||||
|
|
||||||
|
typedef struct { |
||||||
|
GPIO_TypeDef *GPIOx; ///< GPIO base
|
||||||
|
uint16_t pin; ///< pin mask
|
||||||
|
bool invert; ///< invert value read from GPIO (button to ground)
|
||||||
|
ms_time_t debo_time; ///< debounce time in ms, 0 = default (20 ms)
|
||||||
|
uint32_t cb_payload; ///< Value passed to the callback func
|
||||||
|
void (*rising_cb)(uint32_t); ///< callback when the pin goes HIGH
|
||||||
|
void (*falling_cb)(uint32_t); ///< callback when the pin goes LOW
|
||||||
|
} debo_init_t; |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a pin for debouncing. |
||||||
|
* |
||||||
|
* The pin state will be checked with the configured hysteresis |
||||||
|
* and callbacks will be called when a state change is detected. |
||||||
|
*/ |
||||||
|
debo_id_t debo_register_pin(debo_init_t *init_struct); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if a pin is high |
||||||
|
* @param pin_id : Slot ID |
||||||
|
* @return true if the pin is registered and is HIGH |
||||||
|
*/ |
||||||
|
bool debo_pin_state(debo_id_t pin_id); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a pin entry from the debouncer. |
||||||
|
* @param pin_id : Slot ID |
||||||
|
* @return true if task found & removed. |
||||||
|
*/ |
||||||
|
bool debo_remove_pin(debo_id_t pin_id); |
||||||
|
|
||||||
|
#endif /* MPORK_DEBOUNCE_H */ |
@ -0,0 +1,347 @@ |
|||||||
|
#include "timebase.h" |
||||||
|
#include "malloc_safe.h" |
||||||
|
|
||||||
|
// Time base
|
||||||
|
static volatile ms_time_t SystemTime_ms = 0; |
||||||
|
|
||||||
|
|
||||||
|
typedef struct { |
||||||
|
/** User callback with arg */ |
||||||
|
void (*callback)(void *); |
||||||
|
/** Arg for the arg callback */ |
||||||
|
void *cb_arg; |
||||||
|
/** Callback interval */ |
||||||
|
ms_time_t interval_ms; |
||||||
|
/** Counter, when reaches interval_ms, is cleared and callback is called. */ |
||||||
|
ms_time_t countup; |
||||||
|
/** Unique task ID (for cancelling / modification) */ |
||||||
|
task_pid_t pid; |
||||||
|
/** Enable flag - disabled tasks still count, but CB is not run */ |
||||||
|
bool enabled; |
||||||
|
/** Marks that the task is due to be run */ |
||||||
|
bool enqueue; |
||||||
|
} periodic_task_t; |
||||||
|
|
||||||
|
|
||||||
|
typedef struct { |
||||||
|
/** User callback with arg */ |
||||||
|
void (*callback)(void *); |
||||||
|
/** Arg for the arg callback */ |
||||||
|
void *cb_arg; |
||||||
|
/** Counter, when reaches 0ms, callback is called and the task is removed */ |
||||||
|
ms_time_t countdown_ms; |
||||||
|
/** Unique task ID (for cancelling / modification) */ |
||||||
|
task_pid_t pid; |
||||||
|
/** Whether this task is long and needs posting on the queue */ |
||||||
|
bool enqueue; |
||||||
|
} future_task_t; |
||||||
|
|
||||||
|
|
||||||
|
static size_t periodic_slot_count = 0; |
||||||
|
static size_t future_slot_count = 0; |
||||||
|
|
||||||
|
static periodic_task_t *periodic_tasks; |
||||||
|
static future_task_t *future_tasks; |
||||||
|
|
||||||
|
|
||||||
|
/** Init timebase */ |
||||||
|
void timebase_init(size_t periodic, size_t future) |
||||||
|
{ |
||||||
|
periodic_slot_count = periodic; |
||||||
|
future_slot_count = future; |
||||||
|
|
||||||
|
periodic_tasks = calloc_s(periodic, sizeof(periodic_task_t)); |
||||||
|
future_tasks = calloc_s(future, sizeof(future_task_t)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static task_pid_t next_task_pid = 1; // 0 (PID_NONE) is reserved
|
||||||
|
|
||||||
|
|
||||||
|
/** Get a valid free PID for a new task. */ |
||||||
|
static task_pid_t make_pid(void) |
||||||
|
{ |
||||||
|
task_pid_t pid = next_task_pid++; |
||||||
|
|
||||||
|
// make sure no task is given PID 0
|
||||||
|
if (next_task_pid == PID_NONE) { |
||||||
|
next_task_pid++; |
||||||
|
} |
||||||
|
|
||||||
|
return pid; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Take an empty periodic task slot and populate the basics. */ |
||||||
|
static periodic_task_t* claim_periodic_task_slot(ms_time_t interval, bool enqueue) |
||||||
|
{ |
||||||
|
for (size_t i = 0; i < periodic_slot_count; i++) { |
||||||
|
periodic_task_t *task = &periodic_tasks[i]; |
||||||
|
if (task->pid != PID_NONE) continue; // task is used
|
||||||
|
|
||||||
|
task->countup = 0; |
||||||
|
task->interval_ms = interval - 1; |
||||||
|
task->enqueue = enqueue; |
||||||
|
task->pid = make_pid(); |
||||||
|
task->enabled = true; |
||||||
|
return task; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO logging
|
||||||
|
//error("Periodic task table full.");
|
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Take an empty future task slot and populate the basics. */ |
||||||
|
static future_task_t* claim_future_task_slot(ms_time_t delay, bool enqueue) |
||||||
|
{ |
||||||
|
for (size_t i = 0; i < future_slot_count; i++) { |
||||||
|
future_task_t *task = &future_tasks[i]; |
||||||
|
if (task->pid != PID_NONE) continue; // task is used
|
||||||
|
|
||||||
|
task->countdown_ms = delay; |
||||||
|
task->enqueue = enqueue; |
||||||
|
task->pid = make_pid(); |
||||||
|
return task; |
||||||
|
} |
||||||
|
|
||||||
|
//error("Future task table full.");
|
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/** Add a periodic task with an arg. */ |
||||||
|
task_pid_t add_periodic_task(void (*callback)(void*), void* arg, ms_time_t interval, bool enqueue) |
||||||
|
{ |
||||||
|
periodic_task_t *task = claim_periodic_task_slot(interval, enqueue); |
||||||
|
|
||||||
|
if (task == NULL) return PID_NONE; |
||||||
|
|
||||||
|
task->callback = callback; |
||||||
|
task->cb_arg = arg; |
||||||
|
|
||||||
|
return task->pid; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Schedule a future task, with uint32_t argument. */ |
||||||
|
task_pid_t schedule_task(void (*callback)(void*), void *arg, ms_time_t delay, bool enqueue) |
||||||
|
{ |
||||||
|
future_task_t *task = claim_future_task_slot(delay, enqueue); |
||||||
|
|
||||||
|
if (task == NULL) return PID_NONE; |
||||||
|
|
||||||
|
task->callback = callback; |
||||||
|
task->cb_arg = arg; |
||||||
|
|
||||||
|
return task->pid; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Enable or disable a periodic task. */ |
||||||
|
bool enable_periodic_task(task_pid_t pid, bool enable) |
||||||
|
{ |
||||||
|
if (pid == PID_NONE) return false; |
||||||
|
|
||||||
|
for (size_t i = 0; i < periodic_slot_count; i++) { |
||||||
|
periodic_task_t *task = &periodic_tasks[i]; |
||||||
|
if (task->pid != pid) continue; |
||||||
|
|
||||||
|
task->enabled = (enable == ENABLE); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Check if a periodic task is enabled */ |
||||||
|
bool is_periodic_task_enabled(task_pid_t pid) |
||||||
|
{ |
||||||
|
if (pid == PID_NONE) return false; |
||||||
|
|
||||||
|
for (size_t i = 0; i < periodic_slot_count; i++) { |
||||||
|
periodic_task_t *task = &periodic_tasks[i]; |
||||||
|
if (task->pid != pid) continue; |
||||||
|
|
||||||
|
return task->enabled; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool reset_periodic_task(task_pid_t pid) |
||||||
|
{ |
||||||
|
if (pid == PID_NONE) return false; |
||||||
|
|
||||||
|
for (size_t i = 0; i < periodic_slot_count; i++) { |
||||||
|
periodic_task_t *task = &periodic_tasks[i]; |
||||||
|
if (task->pid != pid) continue; |
||||||
|
|
||||||
|
task->countup = 0; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool set_periodic_task_interval(task_pid_t pid, ms_time_t interval) |
||||||
|
{ |
||||||
|
if (pid == PID_NONE) return false; |
||||||
|
|
||||||
|
for (size_t i = 0; i < periodic_slot_count; i++) { |
||||||
|
periodic_task_t *task = &periodic_tasks[i]; |
||||||
|
if (task->pid != pid) continue; |
||||||
|
task->interval_ms = interval; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Remove a periodic task. */ |
||||||
|
bool remove_periodic_task(task_pid_t pid) |
||||||
|
{ |
||||||
|
if (pid == PID_NONE) return false; |
||||||
|
|
||||||
|
for (size_t i = 0; i < periodic_slot_count; i++) { |
||||||
|
periodic_task_t *task = &periodic_tasks[i]; |
||||||
|
if (task->pid != pid) continue; |
||||||
|
|
||||||
|
task->pid = PID_NONE; // mark unused
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Abort a scheduled task. */ |
||||||
|
bool abort_scheduled_task(task_pid_t pid) |
||||||
|
{ |
||||||
|
if (pid == PID_NONE) return false; |
||||||
|
|
||||||
|
for (size_t i = 0; i < future_slot_count; i++) { |
||||||
|
future_task_t *task = &future_tasks[i]; |
||||||
|
if (task->pid != pid) continue; |
||||||
|
|
||||||
|
task->pid = PID_NONE; // mark unused
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Run a periodic task */ |
||||||
|
static void run_periodic_task(periodic_task_t *task) |
||||||
|
{ |
||||||
|
if (!task->enabled) return; |
||||||
|
|
||||||
|
if (task->enqueue) { |
||||||
|
// queued task
|
||||||
|
// FIXME re-implement queue
|
||||||
|
//tq_post(task->callback, task->cb_arg);
|
||||||
|
} else { |
||||||
|
// immediate task
|
||||||
|
task->callback(task->cb_arg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Run a future task */ |
||||||
|
static void run_future_task(future_task_t *task) |
||||||
|
{ |
||||||
|
if (task->enqueue) { |
||||||
|
// queued task
|
||||||
|
// FIXME re-implement queue
|
||||||
|
//tq_post(task->callback, task->cb_arg);
|
||||||
|
} else { |
||||||
|
// immediate task
|
||||||
|
task->callback(task->cb_arg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Millisecond callback, should be run in the SysTick handler. |
||||||
|
*/ |
||||||
|
void timebase_ms_cb(void) |
||||||
|
{ |
||||||
|
// increment global time
|
||||||
|
SystemTime_ms++; |
||||||
|
|
||||||
|
// run periodic tasks
|
||||||
|
for (size_t i = 0; i < periodic_slot_count; i++) { |
||||||
|
periodic_task_t *task = &periodic_tasks[i]; |
||||||
|
if (task->pid == PID_NONE) continue; // unused
|
||||||
|
|
||||||
|
if (task->countup++ >= task->interval_ms) { |
||||||
|
// run if enabled
|
||||||
|
run_periodic_task(task); |
||||||
|
// restart counter
|
||||||
|
task->countup = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// run planned future tasks
|
||||||
|
for (size_t i = 0; i < future_slot_count; i++) { |
||||||
|
future_task_t *task = &future_tasks[i]; |
||||||
|
if (task->pid == PID_NONE) continue; // unused
|
||||||
|
|
||||||
|
if (task->countdown_ms-- == 0) { |
||||||
|
// run
|
||||||
|
run_future_task(task); |
||||||
|
// release the slot
|
||||||
|
task->pid = PID_NONE; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Seconds delay */ |
||||||
|
void delay_s(uint32_t s) |
||||||
|
{ |
||||||
|
while (s-- != 0) { |
||||||
|
delay_ms(1000); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Delay N ms */ |
||||||
|
void delay_ms(ms_time_t ms) |
||||||
|
{ |
||||||
|
ms_time_t start = SystemTime_ms; |
||||||
|
while ((SystemTime_ms - start) < ms); // overrun solved by unsigned arithmetic
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Get milliseconds elapsed since start timestamp */ |
||||||
|
ms_time_t ms_elapsed(ms_time_t start) |
||||||
|
{ |
||||||
|
return SystemTime_ms - start; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Get current timestamp. */ |
||||||
|
ms_time_t ms_now(void) |
||||||
|
{ |
||||||
|
return SystemTime_ms; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Helper for looping with periodic branches */ |
||||||
|
bool ms_loop_elapsed(ms_time_t *start, ms_time_t duration) |
||||||
|
{ |
||||||
|
if (SystemTime_ms - *start >= duration) { |
||||||
|
*start = SystemTime_ms; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
@ -0,0 +1,167 @@ |
|||||||
|
#ifndef MPORK_TIMEBASE_H |
||||||
|
#define MPORK_TIMEBASE_H |
||||||
|
|
||||||
|
/**
|
||||||
|
* To use the Timebase functionality, |
||||||
|
* set up SysTick to 1 kHz and call |
||||||
|
* timebase_ms_cb() in the IRQ. |
||||||
|
* |
||||||
|
* If you plan to use pendable future tasks, |
||||||
|
* also make sure you call run_pending_tasks() |
||||||
|
* in your main loop. |
||||||
|
* |
||||||
|
* This is not needed for non-pendable tasks. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
|
||||||
|
/** Task PID. */ |
||||||
|
typedef uint32_t task_pid_t; |
||||||
|
|
||||||
|
/** Time value in ms */ |
||||||
|
typedef uint32_t ms_time_t; |
||||||
|
|
||||||
|
// PID value that can be used to indicate no task
|
||||||
|
#define PID_NONE 0 |
||||||
|
|
||||||
|
/** Loop until timeout - use in place of while() or for(). break and continue work too! */ |
||||||
|
#define until_timeout(to_ms) for(uint32_t _utmeo = ms_now(); ms_elapsed(_utmeo) < (to_ms);) |
||||||
|
|
||||||
|
/** Retry a call until a timeout. Variable 'suc' is set to the return value. Must be defined. */ |
||||||
|
#define retry_TO(to_ms, call) \ |
||||||
|
until_timeout(to_ms) { \
|
||||||
|
suc = call; \
|
||||||
|
if (suc) break; \
|
||||||
|
} |
||||||
|
|
||||||
|
/** Init timebase, allocate slots for tasks. */ |
||||||
|
void timebase_init(size_t periodic_count, size_t future_count); |
||||||
|
|
||||||
|
/** Must be called every 1 ms */ |
||||||
|
void timebase_ms_cb(void); |
||||||
|
|
||||||
|
|
||||||
|
// --- Periodic -----------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a periodic task with an arg. |
||||||
|
* @param callback : task callback |
||||||
|
* @param arg : callback argument |
||||||
|
* @param interval : task interval (ms) |
||||||
|
* @param enqueue : put on the task queue when due |
||||||
|
* @return task PID |
||||||
|
*/ |
||||||
|
task_pid_t add_periodic_task(void (*callback)(void *), void *arg, ms_time_t interval, bool enqueue); |
||||||
|
|
||||||
|
|
||||||
|
/** Destroy a periodic task. */ |
||||||
|
bool remove_periodic_task(task_pid_t pid); |
||||||
|
|
||||||
|
/** Enable or disable a periodic task. Returns true on success. */ |
||||||
|
bool enable_periodic_task(task_pid_t pid, bool cmd); |
||||||
|
|
||||||
|
/** Check if a periodic task exists and is enabled. */ |
||||||
|
bool is_periodic_task_enabled(task_pid_t pid); |
||||||
|
|
||||||
|
/** Reset timer for a task */ |
||||||
|
bool reset_periodic_task(task_pid_t pid); |
||||||
|
|
||||||
|
/** Set inteval */ |
||||||
|
bool set_periodic_task_interval(task_pid_t pid, ms_time_t interval); |
||||||
|
|
||||||
|
|
||||||
|
// --- Future -------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Schedule a future task, with uint32_t argument. |
||||||
|
* @param callback : task callback |
||||||
|
* @param arg : callback argument |
||||||
|
* @param delay : task delay (ms) |
||||||
|
* @param enqueue : put on the task queue when due |
||||||
|
* @return task PID |
||||||
|
*/ |
||||||
|
task_pid_t schedule_task(void (*callback_arg)(void *), void *arg, ms_time_t delay, bool enqueue); |
||||||
|
|
||||||
|
|
||||||
|
/** Abort a scheduled task. */ |
||||||
|
bool abort_scheduled_task(task_pid_t pid); |
||||||
|
|
||||||
|
|
||||||
|
// --- Waiting functions --------------------------------------
|
||||||
|
|
||||||
|
/** Get milliseconds elapsed since start timestamp */ |
||||||
|
ms_time_t ms_elapsed(ms_time_t start); |
||||||
|
|
||||||
|
|
||||||
|
/** Get current timestamp. */ |
||||||
|
ms_time_t ms_now(void); |
||||||
|
|
||||||
|
|
||||||
|
/** Delay using SysTick */ |
||||||
|
void delay_ms(ms_time_t ms); |
||||||
|
|
||||||
|
|
||||||
|
/** Delay N seconds */ |
||||||
|
void delay_s(uint32_t s); |
||||||
|
|
||||||
|
|
||||||
|
inline __attribute__((always_inline)) |
||||||
|
void delay_cycles(uint32_t n) |
||||||
|
{ |
||||||
|
uint32_t l = n >> 2; |
||||||
|
|
||||||
|
__asm volatile( |
||||||
|
"0: mov r0,r0;" |
||||||
|
"subs %[count], #1;" |
||||||
|
"bne 0b;" |
||||||
|
: [count] "+r"(l) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
inline __attribute__((always_inline)) |
||||||
|
void delay_ns(uint32_t ns) |
||||||
|
{ |
||||||
|
delay_cycles(ns / 24); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Microsecond delay. |
||||||
|
* @param us |
||||||
|
*/ |
||||||
|
inline __attribute__((always_inline)) |
||||||
|
void delay_us(uint32_t us) |
||||||
|
{ |
||||||
|
delay_ns(us * 1150); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if time since `start` elapsed. |
||||||
|
* |
||||||
|
* If so, sets the *start variable to the current time. |
||||||
|
* |
||||||
|
* Example: |
||||||
|
* |
||||||
|
* ms_time_t s = ms_now(); |
||||||
|
* |
||||||
|
* while(1) { |
||||||
|
* if (ms_loop_elapsed(&s, 100)) { |
||||||
|
* // this is called every 100 ms
|
||||||
|
* } |
||||||
|
* // ... rest of the loop ...
|
||||||
|
* } |
||||||
|
* |
||||||
|
* @param start start time variable |
||||||
|
* @param duration delay length |
||||||
|
* @return delay elapsed; start was updated. |
||||||
|
*/ |
||||||
|
bool ms_loop_elapsed(ms_time_t *start, ms_time_t duration); |
||||||
|
|
||||||
|
#endif /* MPORK_TIMEBASE_H */ |
Loading…
Reference in new issue