lib/timer now using dynamic arrays instead of linked lists
parent
57c43089ec
commit
a72e6ba4bc
|
@ -201,6 +201,7 @@ static uint8_t resize_to_write(void) {
|
|||
queue.unused_back += new_allocated - queue.allocated;
|
||||
queue.allocated = new_allocated;
|
||||
queue.data = new_data;
|
||||
|
||||
return 0; // success: queue reallocated
|
||||
|
||||
#undef queue
|
||||
|
@ -247,6 +248,7 @@ static uint8_t resize_to_copy(void) {
|
|||
queue.unused_back += new_allocated - queue.allocated;
|
||||
queue.allocated = new_allocated;
|
||||
queue.data = new_data;
|
||||
|
||||
return 0; // success: queue reallocated
|
||||
|
||||
#undef queue
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
#include <stdint.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
#include "../../../../firmware/lib/data-types/list.h"
|
||||
#include "../event-list.h"
|
||||
#include "../../timer.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -27,7 +25,9 @@
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static volatile uint16_t _milliseconds__counter;
|
||||
static struct {
|
||||
volatile uint16_t counter;
|
||||
} milliseconds;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -42,10 +42,10 @@ uint8_t timer__init(void) {
|
|||
}
|
||||
|
||||
uint16_t timer__get_milliseconds(void) {
|
||||
return _milliseconds__counter;
|
||||
return milliseconds.counter;
|
||||
}
|
||||
|
||||
ISR(TIMER0_COMPA_vect) {
|
||||
_milliseconds__counter++;
|
||||
milliseconds.counter++;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Implements the event-list interface defined in "../event-list.h"
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "../../../firmware/lib/data-types/list.h"
|
||||
#include "./event-list.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
list__node_t _super; // "subclass" `list__node_t`
|
||||
uint16_t ticks; // our unit of time
|
||||
void(*function)(void);
|
||||
} event_t;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t event_list__append( list__list_t * list,
|
||||
uint16_t ticks,
|
||||
void(*function)(void) ) {
|
||||
if (!function) return 0; // success: nothing to do
|
||||
|
||||
event_t * event = malloc(sizeof(event_t));
|
||||
if (!event) return 1; // error
|
||||
|
||||
event->ticks = ticks;
|
||||
event->function = function;
|
||||
|
||||
list__insert(list, -1, event);
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
void event_list__tick(list__list_t * list) {
|
||||
for (event_t * event = list->head; event;) {
|
||||
if (event->ticks == 0) {
|
||||
(*event->function)();
|
||||
event = list__pop_node_next(list, event);
|
||||
} else {
|
||||
event->ticks--;
|
||||
event = event->_super.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2013 Ben Blazak <benblazak.dev@gmail.com>
|
||||
* Released under The MIT License (see "doc/licenses/MIT.md")
|
||||
* Project located at <https://github.com/benblazak/ergodox-firmware>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** description
|
||||
* Event-list interface, to handle lists of scheduled events
|
||||
*
|
||||
* Prefix: `event_list__`
|
||||
*
|
||||
* Meant to be used within ".../firmware/lib/timer"
|
||||
*
|
||||
* Warnings:
|
||||
* - These functions are not safe for use within interrupt vectors. Just in
|
||||
* case you were tempted to try :)
|
||||
* - It is safe to `...append()` while `...tick()`ing though (as when a
|
||||
* function reschedules itself)
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ERGODOX_FIRMWARE__LIB__TIMER__EVENT_LIST__H
|
||||
#define ERGODOX_FIRMWARE__LIB__TIMER__EVENT_LIST__H
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../../../firmware/lib/data-types/list.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t event_list__append ( list__list_t * list,
|
||||
uint16_t ticks,
|
||||
void(*function)(void) );
|
||||
void event_list__tick (list__list_t * list);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
#endif // ERGODOX_FIRMWARE__LIB__TIMER__EVENT_LIST__H
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// === Documentation ==========================================================
|
||||
// ============================================================================
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// functions ------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// === event_list__append() ===
|
||||
/** functions/event_list__append/description
|
||||
* Append an event with the given attributes to `list`
|
||||
*
|
||||
* Arguments:
|
||||
* - `list`: A pointer to the list to add the new event to
|
||||
* - `ticks`: The number of ticks to assign to the event
|
||||
* - `function`: A function pointer to assign to the event
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*/
|
||||
|
||||
// === event_list__tick() ===
|
||||
/** functions/event_list__tick/description
|
||||
* Perform any actions that should occur every "tick", and run functions as
|
||||
* scheduled
|
||||
*
|
||||
* Arguments:
|
||||
* - `list`: A pointer to the list to "tick"
|
||||
*
|
||||
* Implementation notes:
|
||||
* - In the case that more than one function is scheduled to run on a given
|
||||
* "tick", the functions should be run in the order in which they were
|
||||
* scheduled.
|
||||
*/
|
||||
|
|
@ -11,30 +11,213 @@
|
|||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../../../firmware/lib/data-types/list.h"
|
||||
#include "./event-list.h"
|
||||
#include <stdlib.h>
|
||||
#include "../timer.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// macros ---------------------------------------------------------------------
|
||||
|
||||
#define DEFINE_TIMER(name) \
|
||||
static uint16_t _##name##__counter; \
|
||||
static list__list_t _##name##__scheduled_events; \
|
||||
\
|
||||
uint16_t timer__get_##name(void) { \
|
||||
return _##name##__counter; \
|
||||
} \
|
||||
uint8_t timer__schedule_##name(uint16_t ticks, void(*function)(void)) { \
|
||||
return event_list__append( \
|
||||
&_##name##__scheduled_events, ticks, function ); \
|
||||
} \
|
||||
void timer___tick_##name(void) { \
|
||||
_##name##__counter++; \
|
||||
event_list__tick(&_##name##__scheduled_events); \
|
||||
}
|
||||
/** macros/MIN_UNUSED/description
|
||||
* The minimum number of elements to have unused after a resize
|
||||
*/
|
||||
#define MIN_UNUSED 0
|
||||
|
||||
/** macros/MAX_UNUSED/description
|
||||
* The maximum number of elements to have unused after a resize
|
||||
*/
|
||||
#define MAX_UNUSED 4
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// types ----------------------------------------------------------------------
|
||||
|
||||
DEFINE_TIMER(cycles);
|
||||
DEFINE_TIMER(keypresses);
|
||||
/** types/event_t/description
|
||||
* To hold an event that should be run at some point in the future
|
||||
*
|
||||
* Struct members:
|
||||
* - `ticks`: The number of units of time to wait until running this event
|
||||
* - `function`: The event (the function to run)
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t ticks;
|
||||
void (*function)(void);
|
||||
} event_t;
|
||||
|
||||
/** types/list_t/description
|
||||
* To hold a list of events, with corresponding metadata
|
||||
*
|
||||
* Struct members:
|
||||
* - `allocated`: The number of positions allocated
|
||||
* - `filled`: The number of positions filled
|
||||
* - `data`: A list of events to run at some point in the future
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t allocated;
|
||||
uint8_t filled;
|
||||
event_t * data;
|
||||
} list_t;
|
||||
|
||||
/** types/timer_t/description
|
||||
* To hold all the variables needed by a timer
|
||||
*
|
||||
* Struct members:
|
||||
* - `counter`: How many "ticks" of this timer have occurred since it was
|
||||
* initialized (mod 2^16)
|
||||
* - `scheduled`: The list of events to be eventually run by this timer, with
|
||||
* corresponding metadata
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t counter;
|
||||
list_t scheduled;
|
||||
} timer_t;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// variable manipulator functions ---------------------------------------------
|
||||
|
||||
/** functions/resize/description
|
||||
* Resize `list->data`, so that the number of unused elements is between
|
||||
* `MIN_UNUSED` and `MAX_UNUSED`, inclusive
|
||||
*
|
||||
* Arguments:
|
||||
* - `list`: A pointer to the list to operate on
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Implementation notes:
|
||||
* - We use a signed type for `unused` in case `filled` is greater than
|
||||
* `allocated`, as may happen when appending a new event. This number should
|
||||
* normally be much smaller than either `filled` or `allocated`, so there is
|
||||
* no need to worry about using a signed type (with a smaller maximum value)
|
||||
* rather than an unsigned one.
|
||||
* - The only time `realloc()` should fail is if we are trying to grow the
|
||||
* stack. See [the documentation]
|
||||
* (http://www.nongnu.org/avr-libc/user-manual/malloc.html)
|
||||
* for `malloc()` in avr-libc.
|
||||
*/
|
||||
static uint8_t resize(list_t * list) {
|
||||
int8_t unused = list->allocated - list->filled;
|
||||
|
||||
if (MIN_UNUSED <= unused && unused <= MAX_UNUSED)
|
||||
return 0; // nothing to do
|
||||
|
||||
uint8_t new_allocated;
|
||||
if (UINT8_MAX >= list->filled + (MIN_UNUSED+MAX_UNUSED)/2)
|
||||
new_allocated = list->filled + (MIN_UNUSED+MAX_UNUSED)/2;
|
||||
else if (UINT8_MAX >= list->filled + MIN_UNUSED)
|
||||
new_allocated = UINT8_MAX;
|
||||
else
|
||||
return 1; // unable to count the required number of elements
|
||||
|
||||
void * new_data = realloc( list->data, sizeof(event_t) * new_allocated );
|
||||
if (!new_data)
|
||||
return 1; // error: `realloc()` failed (unable to grow list)
|
||||
|
||||
list->allocated = new_allocated;
|
||||
list->data = new_data;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
/** functions/append/description
|
||||
* Append a new event containing the passed information to `list->data`
|
||||
*
|
||||
* Arguments:
|
||||
* - `list`: A pointer to the list to operate on
|
||||
* - `ticks`: The value to put into the event's corresponding field
|
||||
* - `function`: The value to put into the event's corresponding field
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*/
|
||||
static uint8_t append(list_t * list, uint16_t ticks, void(*function)(void)) {
|
||||
if (!function)
|
||||
return 0; // nothing to do
|
||||
|
||||
if (list->filled == UINT8_MAX)
|
||||
return 1; // error: list already full
|
||||
list->filled++;
|
||||
if(resize(list)) {
|
||||
list->filled--;
|
||||
return 1; // resize failed
|
||||
}
|
||||
|
||||
list->data[list->filled - 1].ticks = ticks;
|
||||
list->data[list->filled - 1].function = function;
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
/** function/pop/description
|
||||
* Remove the event at `index` from `list->data`
|
||||
*
|
||||
* Arguments:
|
||||
* - `list`: A pointer to the list to operate on
|
||||
* - `index`: The index of the event to pop
|
||||
*/
|
||||
static void pop(list_t * list, uint8_t index) {
|
||||
// shift down
|
||||
// - start with the element at `index`, and copy the element above it down
|
||||
// - continue until we copy the top element (which will then be duplicated
|
||||
// at indices `list->filled - 2` and `list->filled - 1`)
|
||||
// - if the top element is at `index` (or `index` is out of bounds), this
|
||||
// will do nothing
|
||||
for (uint8_t i = index; i < list->filled - 1; i++)
|
||||
list->data[i] = list->data[i+1];
|
||||
|
||||
// remove an element
|
||||
list->filled--;
|
||||
resize(list); // we're shrinking the list, so this should never fail
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// variables ------------------------------------------------------------------
|
||||
|
||||
static timer_t cycles;
|
||||
static timer_t keypresses;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// front end functions --------------------------------------------------------
|
||||
|
||||
uint16_t timer__get_cycles(void) {
|
||||
return cycles.counter;
|
||||
}
|
||||
|
||||
uint16_t timer__get_keypresses(void) {
|
||||
return keypresses.counter;
|
||||
}
|
||||
|
||||
uint8_t timer__schedule_cycles(uint16_t ticks, void(*function)(void)) {
|
||||
return append(&cycles.scheduled, ticks, function);
|
||||
}
|
||||
|
||||
uint8_t timer__schedule_keypresses(uint16_t ticks, void(*function)(void)) {
|
||||
return append(&keypresses.scheduled, ticks, function);
|
||||
}
|
||||
|
||||
void timer___tick_cycles(void) {
|
||||
cycles.counter++;
|
||||
for (uint8_t i = 0; i < cycles.scheduled.filled; i++) {
|
||||
if (cycles.scheduled.data[i].ticks == 0) {
|
||||
(*cycles.scheduled.data[i].function)();
|
||||
pop(&cycles.scheduled, i);
|
||||
} else {
|
||||
cycles.scheduled.data[i].ticks--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void timer___tick_keypresses(void) {
|
||||
keypresses.counter++;
|
||||
for (uint8_t i = 0; i < keypresses.scheduled.filled; i++) {
|
||||
if (keypresses.scheduled.data[i].ticks == 0) {
|
||||
(*keypresses.scheduled.data[i].function)();
|
||||
pop(&keypresses.scheduled, i);
|
||||
} else {
|
||||
keypresses.scheduled.data[i].ticks--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue