blinkenlights working w sonar
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "circbuf.h"
|
||||
#include "malloc_safe.h"
|
||||
|
||||
// --- Circbuf data structure ----
|
||||
|
||||
/** Offset in void* buffer */
|
||||
#define PV_OFFS(pvBuf, elem_size, index) ((uint8_t*)(pvBuf) + ((elem_size)*(index)))
|
||||
|
||||
|
||||
// Instance structure
|
||||
struct circbuf_struct {
|
||||
void *buf;
|
||||
size_t elem_size;
|
||||
size_t cap;
|
||||
size_t lr; // last read pos
|
||||
size_t nw; // next write pos
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write data to a CircBuf slot
|
||||
* @param cb : circbuf
|
||||
* @param index : slot index
|
||||
* @param source : data source
|
||||
*/
|
||||
static void write_buffer(CircBuf *cb, size_t index, const void *source)
|
||||
{
|
||||
memcpy(PV_OFFS(cb->buf, cb->elem_size, index), source, cb->elem_size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copy data from a CircBuf slot to a buffer
|
||||
* @param cb : circbuf
|
||||
* @param index : slot index
|
||||
* @param dest : destination buffer
|
||||
*/
|
||||
static void read_buffer(const CircBuf *cb, size_t index, void *dest)
|
||||
{
|
||||
memcpy(dest, PV_OFFS(cb->buf, cb->elem_size, index), cb->elem_size);
|
||||
}
|
||||
|
||||
|
||||
/** Create a cbuf */
|
||||
CircBuf *cbuf_create(size_t capacity, size_t elem_size)
|
||||
{
|
||||
// add one, because one is always unused.
|
||||
capacity++;
|
||||
|
||||
// Allocate the structure
|
||||
CircBuf *cb = malloc_s(sizeof(CircBuf));
|
||||
|
||||
// allocate the buffer
|
||||
cb->buf = malloc_s(capacity * elem_size);
|
||||
|
||||
// set capacity, clear state
|
||||
cb->elem_size = elem_size;
|
||||
cb->cap = capacity;
|
||||
cbuf_clear(cb);
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
|
||||
/** Release cbuf memory */
|
||||
void cbuf_destroy(CircBuf *cb)
|
||||
{
|
||||
if (cb != NULL) {
|
||||
if (cb->buf != NULL) {
|
||||
free(cb->buf);
|
||||
}
|
||||
|
||||
free(cb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Check if cbuf is full */
|
||||
bool cbuf_full(const CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL) return false;
|
||||
|
||||
return (cb->lr == cb->nw);
|
||||
}
|
||||
|
||||
|
||||
/** Check if cbuf is empty */
|
||||
bool cbuf_empty(const CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL) return true;
|
||||
|
||||
return ((cb->lr + 1) % cb->cap) == cb->nw;
|
||||
}
|
||||
|
||||
|
||||
/** Write a byte to the buffer, if space left */
|
||||
bool cbuf_append(CircBuf *cb, const void *source)
|
||||
{
|
||||
if (cb == NULL) return false;
|
||||
if (source == NULL) return false;
|
||||
if (cbuf_full(cb)) return false;
|
||||
|
||||
write_buffer(cb, cb->nw, source);
|
||||
|
||||
// increment
|
||||
cb->nw++;
|
||||
if (cb->nw == cb->cap) cb->nw = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Push value to the end, like a stack. */
|
||||
bool cbuf_push(CircBuf *cb, const void *source)
|
||||
{
|
||||
if (cb == NULL) return false;
|
||||
if (source == NULL) return false;
|
||||
if (cbuf_full(cb)) return false;
|
||||
|
||||
write_buffer(cb, cb->lr, source);
|
||||
|
||||
// move lr back
|
||||
if (cb->lr == 0) {
|
||||
cb->lr = cb->cap - 1; // wrap to the end
|
||||
} else {
|
||||
cb->lr--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Read one byte, if not empty. */
|
||||
bool cbuf_pop(CircBuf *cb, void *dest)
|
||||
{
|
||||
if (cb == NULL || dest == NULL) return false;
|
||||
if (cbuf_empty(cb)) return false;
|
||||
|
||||
// increment
|
||||
cb->lr++;
|
||||
if (cb->lr == cb->cap) cb->lr = 0;
|
||||
|
||||
read_buffer(cb, cb->lr, dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Clear a cbuf */
|
||||
void cbuf_clear(CircBuf *cb)
|
||||
{
|
||||
if (cb == NULL) return;
|
||||
|
||||
cb->lr = cb->cap - 1;
|
||||
cb->nw = 0;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @file circbuf.h
|
||||
* @author Ondřej Hruška, 2016
|
||||
*
|
||||
* Circular buffer / queue / stack.
|
||||
* Slots are pre-allocated, values are copied into the buffer.
|
||||
*
|
||||
* The buffer may be used as a stack, event queue or a simple buffer.
|
||||
*
|
||||
* -------------------------------------
|
||||
*
|
||||
* NW LR
|
||||
* append -> [][][][] -> pop
|
||||
* <- push
|
||||
*
|
||||
* NW - next write pointer (stack base)
|
||||
* LR - last read position (stack top)
|
||||
*
|
||||
* -------------------------------------
|
||||
*
|
||||
* MIT license
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
typedef struct circbuf_struct CircBuf;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize a circular buffer. The buffer is malloc'd.
|
||||
* @param capacity : buffer capacity
|
||||
* @param elem_size : size of one element
|
||||
* @return pointer to the buffer instance
|
||||
*/
|
||||
CircBuf *cbuf_create(size_t capacity, size_t elem_size);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Destroy a buffer, freeing used memory.
|
||||
*
|
||||
* @attention
|
||||
* If the buffer items have malloc'd members, you have
|
||||
* to free them manually to avoid a memory leak.
|
||||
*
|
||||
* @param cb : buffer
|
||||
*/
|
||||
void cbuf_destroy(CircBuf *cb);
|
||||
|
||||
|
||||
/** Test for full buffer */
|
||||
bool cbuf_full(const CircBuf *cb);
|
||||
|
||||
|
||||
/** Test for empty buffer */
|
||||
bool cbuf_empty(const CircBuf *cb);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Append a value to the buffer (FIFO)
|
||||
* @param cb : buffer
|
||||
* @param source : pointer to a value (will be copied)
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_append(CircBuf *cb, const void *source);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Push a value into the circbuf (LIFO).
|
||||
*
|
||||
* @param cb : buffer
|
||||
* @param source : pointer to a value (will be copied)
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_push(CircBuf *cb, const void *source);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read a value from the buffer, return susccess.
|
||||
*
|
||||
* @param cb : buffer
|
||||
* @param dest : read destionation. If NULL, value is discarded.
|
||||
* @return success
|
||||
*/
|
||||
bool cbuf_pop(CircBuf *cb, void *dest);
|
||||
|
||||
|
||||
/** @brief Remove all data from buffer */
|
||||
void cbuf_clear(CircBuf *cb);
|
||||
@@ -0,0 +1,171 @@
|
||||
#include "main.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
|
||||
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)(void);
|
||||
void (*rising_cb)(void);
|
||||
} 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;
|
||||
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
/** Register a pin */
|
||||
debo_id_t debo_register_pin(debo_init_t *init)
|
||||
{
|
||||
assert_param(IS_GPIO_ALL_PERIPH(init->GPIOx));
|
||||
assert_param(IS_GET_GPIO_PIN(init->pin));
|
||||
|
||||
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->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 = GPIO_ReadInputDataBit(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)
|
||||
{
|
||||
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 = GPIO_ReadInputDataBit(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();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// rising
|
||||
|
||||
if (slot->counter_1++ == slot->debo_time) {
|
||||
slot->state = 1;
|
||||
|
||||
if (slot->rising_cb != NULL) {
|
||||
slot->rising_cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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,62 @@
|
||||
#pragma once
|
||||
#include "main.h"
|
||||
#include "utils/timebase.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.
|
||||
*
|
||||
* You have to also register the periodic task to timebase.
|
||||
*
|
||||
* @param pin_count : number of pin slots to allocate
|
||||
*/
|
||||
void debounce_init(size_t pin_count);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 1 ms periodic callback for debouncer. Must be registered to timebase.
|
||||
*/
|
||||
void debo_periodic_task(void);
|
||||
|
||||
|
||||
typedef struct {
|
||||
GPIO_TypeDef *GPIOx; ///< GPIO base
|
||||
uint16_t pin; ///< pin mask
|
||||
ms_time_t debo_time; ///< debounce time in ms, 0 = default (20 ms)
|
||||
bool invert; ///< invert value read from GPIO (button to ground)
|
||||
void (*rising_cb)(void); ///< callback when the pin goes HIGH
|
||||
void (*falling_cb)(void); ///< 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);
|
||||
@@ -0,0 +1,33 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "matcher.h"
|
||||
|
||||
void matcher_reset(matcher_t *m)
|
||||
{
|
||||
m->cursor = 0;
|
||||
}
|
||||
|
||||
|
||||
/** Handle incoming char. Returns true if this char completed the match. */
|
||||
bool matcher_test(matcher_t * m, uint8_t b)
|
||||
{
|
||||
// If mismatch, rewind (and check at 0)
|
||||
if (m->pattern[m->cursor] != b) {
|
||||
m->cursor = 0;
|
||||
}
|
||||
|
||||
// Check for match
|
||||
if (m->pattern[m->cursor] == b) {
|
||||
// Good char
|
||||
m->cursor++;
|
||||
if (m->pattern[m->cursor] == 0) { // end of pattern
|
||||
m->cursor = 0; // rewind
|
||||
return true; // indicate success
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file matcher.h
|
||||
* @author Ondřej Hruška, 2016
|
||||
*
|
||||
* String matching utility.
|
||||
*
|
||||
* Matcher can be used for detecting a pattern in a stream of characters.
|
||||
* With each incoming character, call matcher_test().
|
||||
*
|
||||
* It will return true if the character completed a match.
|
||||
*
|
||||
* MIT license
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
const char *pattern;
|
||||
size_t cursor;
|
||||
} matcher_t;
|
||||
|
||||
|
||||
/** reset match progress */
|
||||
void matcher_reset(matcher_t *m);
|
||||
|
||||
|
||||
/**
|
||||
* Consume an incoming character.
|
||||
* If this char was the last char of the pattern, returns true and resets matcher.
|
||||
*
|
||||
* If the char is not in the pattern, resets matcher.
|
||||
*
|
||||
* @returns true if the char concluded the expected pattern.
|
||||
*/
|
||||
bool matcher_test(matcher_t * mb, uint8_t b);
|
||||
@@ -0,0 +1,70 @@
|
||||
#include <stdint.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "meanbuf.h"
|
||||
#include "malloc_safe.h"
|
||||
|
||||
|
||||
struct meanbuf_struct {
|
||||
float * buf; // buffer (allocated at init)
|
||||
size_t cap; // capacity
|
||||
size_t nw; // next write index
|
||||
float mean; // updated on write
|
||||
};
|
||||
|
||||
|
||||
/** Init a buffer */
|
||||
MeanBuf *meanbuf_create(size_t size)
|
||||
{
|
||||
MeanBuf *mb = malloc_s(sizeof(MeanBuf));
|
||||
|
||||
if (size < 1) size = 1;
|
||||
|
||||
mb->buf = calloc_s(size, sizeof(float)); // calloc, so it starts with zeros.
|
||||
mb->cap = size;
|
||||
mb->nw = 0;
|
||||
mb->mean = 0;
|
||||
|
||||
// clean buffer
|
||||
for (uint16_t i = 0; i < size; i++) {
|
||||
mb->buf[i] = 0;
|
||||
}
|
||||
|
||||
return mb;
|
||||
}
|
||||
|
||||
|
||||
void meanbuf_destroy(MeanBuf *mb)
|
||||
{
|
||||
if (mb == NULL) return;
|
||||
|
||||
if (mb->buf != NULL) {
|
||||
free(mb->buf);
|
||||
}
|
||||
|
||||
free(mb);
|
||||
}
|
||||
|
||||
|
||||
/** Add a value to the buffer. Returns current mean. */
|
||||
float meanbuf_add(MeanBuf *mb, float f)
|
||||
{
|
||||
// add sample
|
||||
mb->buf[mb->nw++] = f;
|
||||
if (mb->nw == mb->cap) mb->nw = 0;
|
||||
|
||||
// calculate average
|
||||
float acc = 0;
|
||||
for (size_t i = 0; i < mb->cap; i++) {
|
||||
acc += mb->buf[i];
|
||||
}
|
||||
|
||||
acc /= mb->cap;
|
||||
|
||||
return mb->mean = acc;
|
||||
}
|
||||
|
||||
float meanbuf_current(MeanBuf *mb)
|
||||
{
|
||||
return mb->mean;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file meanbuf.h
|
||||
* @author Ondřej Hruška, 2016
|
||||
*
|
||||
* Averaging float buffer. (You can adjust it to use doubles, if you prefer.)
|
||||
*
|
||||
* The meanbuf_create() function allocates a buffer.
|
||||
*
|
||||
* You can then call meanbuf_add() to add a new value into the buffer (and remove the oldest).
|
||||
* This function returns the current average value.
|
||||
*
|
||||
* This buffer can be used for signal smoothing (such as from an analogue sensor).
|
||||
*
|
||||
* MIT license
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct meanbuf_struct MeanBuf;
|
||||
|
||||
/** Init a buffer */
|
||||
MeanBuf *meanbuf_create(size_t size);
|
||||
|
||||
/** Deinit a buffer (free buffer array) */
|
||||
void meanbuf_destroy(MeanBuf *mb);
|
||||
|
||||
/** Add a value to the buffer. Returns current mean. */
|
||||
float meanbuf_add(MeanBuf *mb, float f);
|
||||
|
||||
float meanbuf_current(MeanBuf *bm);
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
@@ -0,0 +1,286 @@
|
||||
#include "str_utils.h"
|
||||
#include "matcher.h"
|
||||
#include "malloc_safe.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Lot of this stuff is actually not needed anymore,
|
||||
// it was written for the ESP AT firmware, which is no longer used.
|
||||
|
||||
/**
|
||||
* Escape a char.
|
||||
* @returns what to put after backslash, or '\0' for no escape.
|
||||
*/
|
||||
static char escape_char(char c)
|
||||
{
|
||||
switch (c) {
|
||||
case '\r': return 'r';
|
||||
case '\n': return 'n';
|
||||
case '\t': return 't';
|
||||
case '\\': return '\\';
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escape string in place
|
||||
*/
|
||||
void str_escape_ip(char * buf, size_t buf_len)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
// string length (updated when escapes are performed)
|
||||
size_t slen = strlen(buf);
|
||||
|
||||
for (; i < buf_len - 1 && buf[i] != 0; i++) {
|
||||
char replace = escape_char(buf[i]);
|
||||
|
||||
// Escape, shift trailing chars
|
||||
if (replace != 0) {
|
||||
if (i >= buf_len - 2) {
|
||||
break; // discard the char, escape wouldn't fit.
|
||||
}
|
||||
|
||||
// (could be faster if moved starting at the end)
|
||||
|
||||
char m = buf[i + 1]; // remember next char
|
||||
|
||||
buf[i] = '\\';
|
||||
buf[i + 1] = replace;
|
||||
|
||||
slen++; // account for the added backslash
|
||||
|
||||
// shift trailing chars
|
||||
for (size_t j = i + 2; j <= slen; j++) {
|
||||
char n = buf[j];
|
||||
buf[j] = m;
|
||||
m = n;
|
||||
}
|
||||
|
||||
i++; // skip the insterted slash
|
||||
}
|
||||
}
|
||||
|
||||
buf[i] = 0; // add terminator (in case end of string was reached)
|
||||
}
|
||||
|
||||
|
||||
void str_escape(char *dest, const char *src, size_t dest_len)
|
||||
{
|
||||
size_t di = 0, si = 0;
|
||||
|
||||
for (; src[si] != 0 && di < dest_len - 1; si++) {
|
||||
char orig = src[si];
|
||||
char replace = escape_char(orig);
|
||||
|
||||
if (replace == 0) {
|
||||
dest[di++] = orig;
|
||||
} else {
|
||||
if (di >= dest_len - 2) {
|
||||
break; // out of space
|
||||
}
|
||||
|
||||
dest[di++] = '\\';
|
||||
dest[di++] = replace;
|
||||
}
|
||||
}
|
||||
|
||||
dest[di] = 0; // append terminator
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t strpos(const char *haystack, const char *needle)
|
||||
{
|
||||
const char *p = strstr(haystack, needle);
|
||||
if (p) return (p - haystack);
|
||||
return -1; // Not found = -1.
|
||||
}
|
||||
|
||||
|
||||
int32_t strpos_upto(const char *haystack, const char *needle, size_t limit)
|
||||
{
|
||||
if (limit <= 0) return strpos(haystack, needle);
|
||||
|
||||
matcher_t m = {needle, 0};
|
||||
char c;
|
||||
|
||||
for (size_t i = 0; i < limit; i++, haystack++) {
|
||||
c = *haystack;
|
||||
if (c == 0) break;
|
||||
|
||||
if (matcher_test(&m, (uint8_t)c)) {
|
||||
return i - strlen(needle) + 1; // match occured on the last needle char
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int32_t strpos_upto_match(const char *haystack, const char *needle, const char *endmatch)
|
||||
{
|
||||
if (endmatch == NULL) return strpos(haystack, needle);
|
||||
|
||||
matcher_t matcher_needle = {needle, 0};
|
||||
matcher_t matcher_end = {endmatch, 0};
|
||||
char c;
|
||||
|
||||
for (int i = 0;; i++, haystack++) {
|
||||
c = *haystack;
|
||||
if (c == 0) break;
|
||||
|
||||
// match
|
||||
if (matcher_test(&matcher_needle, (uint8_t)c)) {
|
||||
return i - strlen(needle) + 1; // match occured on the last needle char
|
||||
}
|
||||
|
||||
// end
|
||||
if (matcher_test(&matcher_end, (uint8_t)c)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t str_copy(char * dest, const char *src)
|
||||
{
|
||||
char c;
|
||||
size_t i = 0;
|
||||
while ((c = *src++) != 0) {
|
||||
*dest++ = c;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode URL-encoded string in place.
|
||||
*/
|
||||
void urldecode_ip(char *str)
|
||||
{
|
||||
unsigned int x;
|
||||
|
||||
for (size_t i = 0; str[i] != 0; i++) {
|
||||
char c = str[i];
|
||||
if (c == '+') {
|
||||
str[i] = ' ';
|
||||
} else if (c == '%') {
|
||||
// decode the byte
|
||||
sscanf(&str[i + 1], "%02x", &x);
|
||||
str[i] = (char)x;
|
||||
|
||||
// shift following chars
|
||||
for (size_t a = i + 3, b = i + 1;; a++, b++) {
|
||||
str[b] = str[a]; // move
|
||||
if (str[a] == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* url-decode string, put output in a buffer.
|
||||
*/
|
||||
void urldecode(char *dest, const char *src)
|
||||
{
|
||||
unsigned int x;
|
||||
size_t si = 0, di = 0;
|
||||
|
||||
for (; src[si] != 0; si++) {
|
||||
char c = src[si];
|
||||
if (c == '+') {
|
||||
dest[di++] = ' ';
|
||||
} else if (c == '%') {
|
||||
// decode the byte
|
||||
sscanf(&src[si + 1], "%02x", &x);
|
||||
dest[di++] = (char)x;
|
||||
|
||||
si += 2;
|
||||
} else {
|
||||
dest[di++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// add terminator
|
||||
dest[di] = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* url-decode string, put output in a buffer.
|
||||
* Limit operation to N chars in input string
|
||||
*/
|
||||
void urldecode_n(char *dest, const char *src, size_t count)
|
||||
{
|
||||
unsigned int x;
|
||||
size_t si = 0, di = 0;
|
||||
|
||||
for (; src[si] != 0 && si < count; si++) {
|
||||
char c = src[si];
|
||||
if (c == '+') {
|
||||
dest[di++] = ' ';
|
||||
} else if (c == '%') {
|
||||
// decode the byte
|
||||
sscanf(&src[si + 1], "%02x", &x);
|
||||
dest[di++] = (char)x;
|
||||
|
||||
si += 2;
|
||||
} else {
|
||||
dest[di++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// add terminator
|
||||
dest[di] = 0;
|
||||
}
|
||||
|
||||
|
||||
bool get_query_value(char *buffer, const char *querystring, const char *key, size_t buf_len)
|
||||
{
|
||||
bool retval;
|
||||
|
||||
size_t qs_len = strlen(querystring);
|
||||
|
||||
char *ptrn = malloc_s(strlen(key) + 3); // &key=\0
|
||||
sprintf(ptrn, "&%s=", key);
|
||||
matcher_t m = {ptrn, 1}; // pretend ampersand was already matched
|
||||
|
||||
for (size_t i = 0; i < qs_len; i++) {
|
||||
char c = querystring[i];
|
||||
if (matcher_test(&m, (uint8_t)c)) {
|
||||
// found the match
|
||||
i++; // advance past the equals sign
|
||||
|
||||
size_t seg_end = i;
|
||||
while (seg_end < qs_len && querystring[seg_end] != '&') {
|
||||
seg_end++;
|
||||
}
|
||||
|
||||
if (seg_end - i > buf_len) seg_end = i + buf_len;
|
||||
|
||||
if (seg_end == i) {
|
||||
buffer[0] = 0; // strncpy behaves strange with length 0
|
||||
} else {
|
||||
urldecode_n(buffer, querystring + i, seg_end - i);
|
||||
}
|
||||
|
||||
retval = true;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
// not found
|
||||
retval = false;
|
||||
done:
|
||||
free(ptrn);
|
||||
return retval;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#define streq(a, b) (strcmp((a), (b)) == 0)
|
||||
#define streqi(a, b) (strcasecmp((a), (b)) == 0)
|
||||
|
||||
/**
|
||||
* Escape string, storing result in a buffer.
|
||||
*/
|
||||
void str_escape(char *dest, const char *src, size_t dest_len);
|
||||
|
||||
|
||||
/**
|
||||
* Escape special chars in a string, IN PLACE.
|
||||
*
|
||||
* If string is too long after escaping, last chars are dropped.
|
||||
*
|
||||
* @param buf the buffer, containing 0-terminated string.
|
||||
* @param buflen buffer length
|
||||
*/
|
||||
void str_escape_ip(char *buf, size_t buf_len);
|
||||
|
||||
|
||||
/**
|
||||
* Get position of needle in a haystack.
|
||||
* -1 if not found.
|
||||
*/
|
||||
int32_t strpos(const char *haystack, const char *needle);
|
||||
|
||||
|
||||
/**
|
||||
* Find substring position, ending at index 'limit'.
|
||||
* Limit <= 0 means no limit.
|
||||
* Returns index of the first character of needle in haystack.
|
||||
*/
|
||||
int32_t strpos_upto(const char *haystack, const char *needle, size_t limit);
|
||||
|
||||
|
||||
/**
|
||||
* Find substring position, ending when endmatch is encountered. (Substring within endmatch *can* be reported).
|
||||
* Returns index of the first character of needle in haystack.
|
||||
*/
|
||||
int32_t strpos_upto_match(const char *haystack, const char *needle, const char *endmatch);
|
||||
|
||||
|
||||
/**
|
||||
* Like sprintf, except without formatting
|
||||
*/
|
||||
size_t str_copy(char * dest, const char *src);
|
||||
|
||||
|
||||
/**
|
||||
* Decode url-encoded string, store result in dest.
|
||||
*/
|
||||
void urldecode(char *dest, const char *src);
|
||||
|
||||
|
||||
/**
|
||||
* Decode url-encoded string in place.
|
||||
*/
|
||||
void urldecode_ip(char *str);
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve & url-decode a query string value by name.
|
||||
*
|
||||
* @param buffer - target buffer
|
||||
* @param querystring - string (foo=bar&baz=moi)
|
||||
* @param key - key to retrieve
|
||||
* @param buf_len - length of the target buffer
|
||||
* @return true if found.
|
||||
*/
|
||||
bool get_query_value(char *buffer, const char *querystring, const char *key, size_t buf_len);
|
||||
@@ -0,0 +1,331 @@
|
||||
#include "timebase.h"
|
||||
#include "bus/event_queue.h"
|
||||
#include "com/debug.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, FunctionalState 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;
|
||||
}
|
||||
|
||||
|
||||
/** 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
|
||||
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
|
||||
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,159 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* 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 "main.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, FunctionalState 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);
|
||||
|
||||
|
||||
// --- 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);
|
||||
Reference in New Issue
Block a user