Initial import

This commit is contained in:
2016-09-04 16:29:04 +02:00
commit ecd1aa9e65
105 changed files with 190869 additions and 0 deletions
+174
View File
@@ -0,0 +1,174 @@
#include <common.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 (*callback)(uint32_t, bool);
} 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->callback = init->callback;
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->callback != NULL) {
slot->callback(slot->cb_payload, false);
}
}
} else {
// rising
if (slot->counter_1++ == slot->debo_time) {
slot->state = 1;
if (slot->callback != NULL) {
slot->callback(slot->cb_payload, true);
}
}
}
} 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;
}
+60
View File
@@ -0,0 +1,60 @@
#ifndef MPORK_DEBOUNCE_H
#define MPORK_DEBOUNCE_H
#include <common.h>
#include "timebase.h"
// Debouncer requires that you set up timebase 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 (*callback)(uint32_t, bool); ///< callback
} 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 */
+119
View File
@@ -0,0 +1,119 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include "debug.h"
#include "timebase.h"
void dbg_printf(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
vprintf(fmt, va);
va_end(va);
}
void dbg_va_base(const char *fmt, const char *tag, va_list va)
{
ms_time_t now = ms_now();
uint32_t secs = now / 1000;
uint32_t ms = now % 1000;
printf("%4"PRIu32".%03"PRIu32" ", secs, ms);
dbg_raw(tag);
vprintf(fmt, va);
dbg_raw(DEBUG_EOL);
}
/** Print a log message with a DEBUG tag and newline */
void dbg(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_BASE, va);
va_end(va);
}
/** Print a log message with an INFO tag and newline */
void info(const char *fmt, ...)
{
v100_attr(FMT_WHITE);
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_INFO, va);
va_end(va);
v100_attr(FMT_RESET);
}
/** Print a log message with an INFO tag and newline */
void banner(const char *fmt, ...)
{
v100_attr(FMT_GREEN, FMT_BRIGHT);
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_INFO, va);
va_end(va);
v100_attr(FMT_RESET);
}
/** Print a log message with a warning tag and newline */
void warn(const char *fmt, ...)
{
v100_attr(FMT_YELLOW, FMT_BRIGHT);
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_WARN, va);
va_end(va);
v100_attr(FMT_RESET);
}
/** Print a log message with an ERROR tag and newline */
void error(const char *fmt, ...)
{
v100_attr(FMT_RED, FMT_BRIGHT);
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_ERROR, va);
va_end(va);
v100_attr(FMT_RESET);
}
void v100_attr_(uint8_t count, ...)
{
va_list va;
va_start(va, count);
putchar(27);
putchar('[');
for (int i = 0; i < count; i++) {
int attr = va_arg(va, int);
// comma
if (i > 0) putchar(';');
// number
printf("%d", attr);
}
putchar('m');
va_end(va);
}
+98
View File
@@ -0,0 +1,98 @@
#ifndef MPORK_DEBUG_H
#define MPORK_DEBUG_H
#include <common.h>
#include <stdarg.h>
// helper to mark printf functions
#define PRINTF_LIKE __attribute__((format(printf, 1, 2)))
// formatting symbols
#define DEBUG_EOL "\r\n"
#define DEBUG_TAG_WARN "[W] "
#define DEBUG_TAG_ERROR "[E] "
#define DEBUG_TAG_BASE "[ ] "
#define DEBUG_TAG_INFO "[i] "
/** Print a log message with no tag and no newline */
void dbg_printf(const char *fmt, ...) PRINTF_LIKE;
/** Print via va_list */
void dbg_va_base(const char *fmt, const char *tag, va_list va);
/** Print a string to the debug interface (length not limited) */
static inline void dbg_raw(const char *str)
{
fputs(str, stdout);
}
/** Print a char to the debug interface */
static inline void dbg_raw_c(char c)
{
putchar((uint8_t) c);
}
/** Print a log message with a "debug" tag and newline */
void dbg(const char *fmt, ...) PRINTF_LIKE;
/** Print a log message with an "info" tag and newline */
void info(const char *fmt, ...) PRINTF_LIKE;
/** Print a log message with a "banner" tag and newline */
void banner(const char *fmt, ...) PRINTF_LIKE;
/** Print a log message with a "warning" tag and newline */
void warn(const char *fmt, ...) PRINTF_LIKE;
/** Print a log message with an "error" tag and newline */
void error(const char *fmt, ...) PRINTF_LIKE;
/** ANSI formatting attributes */
typedef enum {
// Non-colour Attributes
FMT_RESET = 0, // Reset all attributes
FMT_BRIGHT = 1, // Bright
FMT_DIM = 2, // Dim
FMT_UNDER = 4, // Underscore
FMT_BLINK = 5, // Blink
FMT_INVERS = 7, // Reverse
FMT_HIDDEN = 8, // Hidden
FMT_ITALIC = 16, // Italic font
FMT_FAINT = 32, // Faint color
// Foreground Colours
FMT_BLACK = 30, // Black
FMT_RED = 31, // Red
FMT_GREEN = 32, // Green
FMT_YELLOW = 33, // Yellow
FMT_BLUE = 34, // Blue
FMT_MAGENTA = 35, // Magenta
FMT_CYAN = 36, // Cyan
FMT_WHITE = 37, // White
// Background Colours
FMT_BLACK_BG = 40, // Black
FMT_RED_BG = 41, // Red
FMT_GREEN_BG = 42, // Green
FMT_YELLOW_BG = 43, // Yellow
FMT_BLUE_BG = 44, // Blue
FMT_MAGENTA_BG = 45, // Magenta
FMT_CYAN_BG = 46, // Cyan
FMT_WHITE_BG = 47, // White
} ANSI_attr_t;
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5,4,3,2,1)
#define VA_NUM_ARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N
#define v100_attr(...) v100_attr_(VA_NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
/**
* Send formatting code to a com interface
*/
void v100_attr_(uint8_t count, ...);
#endif //MPORK_DEBUG_H
+37
View File
@@ -0,0 +1,37 @@
#include <common.h>
#include "handlers.h"
#include "malloc_safe.h"
static void reset_when_done(void)
{
//
HAL_NVIC_SystemReset();
}
void *malloc_safe_do(size_t size, const char* file, uint32_t line)
{
void *mem = malloc(size);
if (mem == NULL) {
// malloc failed
user_error_file_line("Malloc failed", file, line);
reset_when_done();
}
return mem;
}
void *calloc_safe_do(size_t nmemb, size_t size, const char* file, uint32_t line)
{
void *mem = calloc(nmemb, size);
if (mem == NULL) {
// malloc failed
user_error_file_line("Calloc failed", file, line);
reset_when_done();
}
return mem;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef MPORK_MALLOC_SAFE_H
#define MPORK_MALLOC_SAFE_H
/**
* Malloc that prints error and restarts the system on failure.
*/
#include <common.h>
#include <stm32f1xx_hal.h>
void *malloc_safe_do(size_t size, const char* file, uint32_t line);
void *calloc_safe_do(size_t nmemb, size_t size, const char* file, uint32_t line);
#define malloc_s(size) malloc_safe_do(size, __FILE__, __LINE__)
#define calloc_s(nmemb, size) calloc_safe_do(nmemb, size, __FILE__, __LINE__)
#endif //MPORK_MALLOC_SAFE_H
+83
View File
@@ -0,0 +1,83 @@
#include <errno.h>
#include <stdio.h>
#include <usart.h>
#include <sys/stat.h>
register char *stack_ptr asm("sp");
caddr_t _sbrk(int incr)
{
extern char end __asm("end");
static char *heap_end;
char *prev_heap_end;
if (heap_end == 0)
heap_end = &end;
prev_heap_end = heap_end;
if (heap_end + incr > stack_ptr) {
// write(1, "Heap and stack collision\n", 25);
// abort();
errno = ENOMEM;
return (caddr_t) -1;
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
/**
* @brief Write to a file by file descriptor.
*
* @param fd : open file descriptor
* @param buf : data to write
* @param len : buffer size
* @return number of written bytes
*/
int _write(int fd, const char *buf, int len)
{
switch (fd) {
case 1: // stdout
case 2: // stderr
HAL_UART_Transmit(&huart1, (uint8_t*)buf, len, 10);
return len;
default:
return 0;
}
}
// region stubs
int _read(int fd, char *buf, int len)
{
return 0;
}
int _fstat(int file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
int _lseek(int file, int ptr, int dir)
{
return 0;
}
int _close(int file)
{
return -1;
}
int _isatty(int fd)
{
if (fd == 0 || fd == 1 || fd == 2)
return 1;
else
return 0;
}
// endregion
+349
View File
@@ -0,0 +1,349 @@
#include <common.h>
#include "debug.h"
#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;
}
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;
}
+165
View File
@@ -0,0 +1,165 @@
#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 <common.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