timers working!

keyboard scanning at almost exactly 200Hz (or maybe exactly... as close
as i can measure)

possible to schedule things to run in 'n' scan cycles!  haven't tested
scheduling things to run in 'n' milliseconds, but the code is so similar
that it should work too.  :D
partial-rewrite
Ben Blazak 2013-05-09 20:41:40 -07:00
parent 1deed9d9ac
commit 203aaf415b
8 changed files with 417 additions and 108 deletions

View File

@ -37,9 +37,6 @@ CURDIRS := $(CURDIR) $(CURDIRS)
CURDIR := $(ROOTDIR)/lib/twi
include $(CURDIR)/options.mk
# -------
CURDIR := $(ROOTDIR)/lib/data-types/list
include $(CURDIR)/options.mk
# -------
CURDIR := $(ROOTDIR)/lib/layout/eeprom-macro
include $(CURDIR)/options.mk
# -------

View File

@ -10,11 +10,50 @@
*
* Prefix: `list__`
*
* Implementation notes:
*
* Usage notes:
*
* - All functions that accept an `index` set `index %= list->length` before
* using it. This will make all passed indices valid. It will also provide
* a convenient way to reference the last element of a list, by passing `-1`
* as the index (as in Python).
*
* - All pointers to `list__node_t` are stored, returned, etc. as as `void`
* pointers until use, so that using files won't have to do so much work
* casting things. They are converted internally to `list__node_t` before
* use, and should be stored by using code in pointers of appropriate (non
* `void *`) type.
*
* - Full node types should be defined in the using '.c' or '.h' file, with
* something like
*
* typedef struct {
* list__node_t _private; // must be the first element
* uint8_t data;
* } node_t;
*
* - If you want to iterate through a list, use something like
*
* for (node_t * node = list->head; node; node = node->_private.next) {
* // do stuff
* }
*
* - If you want to insert a new node at the end of `list` (for example), use
* something like
*
* node_t * node = list__insert(list, -1, malloc(sizeof(node_t)));
* if (!node) return 1; // error
*
* Keep in mind that the initialization of the data stored in the node is the
* calling function's responsibility.
*
*
* Assumptions:
*
* - Lists will never contain more elements than can be indexed by an `int8_t`
*
*
* TODO: go over this again, in a little while, to make sure i like it
*/
@ -29,24 +68,26 @@
// ----------------------------------------------------------------------------
typedef struct list__node_t {
struct list__node_t * next;
void * next; // will be cast to `list__node_t` before use
} list__node_t;
typedef struct list__list_t {
list__node_t * head;
list__node_t * tail;
uint8_t length;
void * head; // will be cast to `list__node_t` before use
void * tail; // will be cast to `list__node_t` before use
uint8_t length;
} list__list_t;
// ----------------------------------------------------------------------------
list__list_t * list__new (void);
void * list__insert ( list__list_t * list,
int8_t index,
uint8_t size );
void * list__peek (list__list_t * list, int8_t index);
void * list__pop__no_free (list__list_t * list, int8_t index);
void list__free (list__list_t * list);
list__list_t * list__new (void);
void * list__insert ( list__list_t * list,
int8_t index,
void * node );
void * list__peek (list__list_t * list, int8_t index);
void * list__pop_index (list__list_t * list, int8_t index);
void * list__pop_node (list__list_t * list, void * node);
void * list__pop_node_next (list__list_t * list, void * node);
void list__free (list__list_t * list);
// ----------------------------------------------------------------------------
@ -67,18 +108,6 @@ void list__free (list__list_t * list);
// === list__node_t ===
/** typedefs/list__node_t/description
* The type of a "node", for the purposes of this library
*
* Full node types should be defined in the using '.c' or '.h' file, with
* something like
*
* typedef struct {
* list__node_t _private;
* uint8_t data;
* } node_t;
*
* The functions that return pointers to nodes will return `void *` pointers,
* so functions in the using '.c' file also need to cast these return values to
* the appropriate type before use.
*/
// === list__list_t ===
@ -101,26 +130,17 @@ void list__free (list__list_t * list);
*/
// === list__insert() ===
/** functions/list__insert/description
/** functions/list__insert/description
* Insert `node` at position `index % list->length`
*
* Arguments:
* - `list`: A pointer to the list to be operated on
* - `index`: An `int8_t` indicating the position the new node will occupy
* - `size`: The size of the full node type (as in `sizeof(node_t)`) defined in
* the using '.c' or '.h' file, so we know how much memory to allocate
* - `node`: A pointer to the node to insert
*
* Returns:
* - success: A `void *` pointer to the new node
* - failure: `NULL`
*
* Warnings:
* - For any given list, the `size` passed to this function should always be
* the same.
*
* Cautions:
* - Initialization of the data to be stored in the node is the calling
* function's responsibility.
*/
// === list__peek() ===
@ -136,15 +156,14 @@ void list__free (list__list_t * list);
* - failure: `NULL`
*/
// === list__pop__no_free() ===
/** functions/list__pop__no_free/description
// === list__pop_index() ===
/** functions/list__pop_index/description
* Return a pointer to the node at position `index % list->length`, and remove
* the node from the list
*
* Warnings:
* - Does not free the node's memory - this is the calling function's
* responsibility. If you want to pop the node and free its memory without
* looking at it, call `free( list__pop__no_free( node ) )`.
* responsibility.
*
* Arguments:
* - `list`: A pointer to the list to be operated on
@ -155,6 +174,36 @@ void list__free (list__list_t * list);
* - failure: `NULL`
*/
// === list__pop_node() ===
/** functions/list__pop_node/description
* Remove `node` from the list, and return a pointer to it
*
* Warnings:
* - Does not free the node's memory - this is the calling function's
* responsibility.
*
* Arguments:
* - `list`: A pointer to the list to be operated on
* - `node`: A pointer to the node to pop (and free)
*
* Returns:
* - success: A `void *` pointer to `node`
*/
// === list__pop_node_next() ===
/** functions/list__pop_node_next/description
* Remove `node` from the list, free its memory, and return a pointer to the
* next element, if the node exists
*
* Arguments:
* - `list`: A pointer to the list to be operated on
* - `node`: A pointer to the node to pop (and free)
*
* Returns:
* - success: A `void *` pointer to the next node in the list
* - failure: `NULL`
*/
// === list__free() ===
/** functions/list__free/description
* Free all node pointers in `list`, then free `list`

View File

@ -6,6 +6,9 @@
/** description
* Implements the list type in "../list.h"
*
*
* TODO: go over this again, in a little while, to make sure i like it
*/
@ -15,6 +18,10 @@
// ----------------------------------------------------------------------------
#define N(name) ((list__node_t *)name)
// ----------------------------------------------------------------------------
list__list_t * list__new(void) {
list__list_t * list = malloc( sizeof(list__list_t) );
if (!list) return NULL;
@ -26,8 +33,7 @@ list__list_t * list__new(void) {
return list;
}
void * list__insert(list__list_t * list, int8_t index, uint8_t size) {
list__node_t * node = malloc(size);
void * list__insert(list__list_t * list, int8_t index, void * node) {
if (!node) return NULL;
list->length++;
@ -36,29 +42,29 @@ void * list__insert(list__list_t * list, int8_t index, uint8_t size) {
// insert as only node (no others exist yet)
list->head = node;
list->tail = node;
node->next = NULL;
N(node)->next = NULL;
} else {
index %= list->length;
if (index == 0) {
// insert as first node
node->next = list->head;
N(node)->next = list->head;
list->head = node;
} else if (index == list->length-1) {
// insert as last node
list->tail->next = node;
N(list->tail)->next = node;
list->tail = node;
node->next = NULL;
N(node)->next = NULL;
} else {
// insert as other node
list__node_t * previous = list->head;
void * previous = list->head;
for (uint8_t i=1; i<index; i++)
previous = previous->next;
node->next = previous->next;
previous->next = node;
previous = N(previous)->next;
N(node)->next = N(previous)->next;
N(previous)->next = node;
}
}
@ -77,39 +83,39 @@ void * list__peek(list__list_t * list, int8_t index) {
return list->tail;
// else
list__node_t * node = list->head;
void * node = list->head;
for (uint8_t i=0; i<index; i++)
node = node->next;
node = N(node)->next;
return node;
}
void * list__pop__no_free(list__list_t * list, int8_t index) {
void * list__pop_index(list__list_t * list, int8_t index) {
// if no nodes exist
if (list->length == 0)
return NULL;
index %= list->length;
list__node_t * node;
void * node;
if (index == 0) {
// pop first node
node = list->head;
list->head = node->next;
list->head = N(node)->next;
} else {
// find the `index-1`th node
list__node_t * previous = list->head;
void * previous = list->head;
for (uint8_t i=1; i<index; i++)
previous = previous->next;
previous = N(previous)->next;
// if last node
if (index == list->length-1)
list->tail = previous;
// pop the node at `index`
node = previous->next;
previous->next = node->next;
node = N(previous)->next;
N(previous)->next = N(node)->next;
}
list->length--;
@ -117,11 +123,50 @@ void * list__pop__no_free(list__list_t * list, int8_t index) {
return node;
}
void * list__pop_node(list__list_t * list, void * node) {
// if `node` is `NULL` or no nodes exist
if (!node || !list->head)
return NULL;
void * previous = list->head;
if (node == list->head) {
// pop first node
list->head = N(node)->next;
} else {
// find the previous node (if `node` is in `list`)
while (N(previous)->next != node) {
previous = N(previous)->next;
if (!previous)
return NULL; // `node` not found
}
// if last node
if (node == list->tail)
list->tail = previous;
// pop the node
N(previous)->next = N(node)->next;
}
list->length--;
return node;
}
void * list__pop_node_next(list__list_t * list, void * node) {
list__pop_node(list, node);
void * next = N(node)->next;
free(node);
return next;
}
void list__free(list__list_t * list) {
list__node_t * node;
void * node;
while (list->head) {
node = list->head;
list->head = list->head->next;
list->head = N(list->head)->next;
free(node);
}
free(list);

View File

@ -23,19 +23,6 @@ uint16_t timer__get_milliseconds (void);
uint8_t timer__schedule ( uint16_t milliseconds,
void(*function)(void) );
// TODO: document and implement `timer__schedule()`, to schedule `function` to
// run in `milliseconds`
// - will need to be careful with this function, as everything called by it
// will be executing within an interrupt vector.
// - if functions need a longer amount of time than is possible with a 16-bit
// millisecond resolution counter, they can repeatedly schedule themselves to
// run in, say, 1 minute, increment a counter each time, and then only
// execute their body after, say, 5 calls (for a 5 minute delay)
//
// TODO: should probably make a corresponding `main__schedule()`, and
// `main__get_cycles()` too, to operate at the resolution of cycles (and
// outside any interrupt vectors)
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@ -80,6 +67,11 @@ uint8_t timer__schedule ( uint16_t milliseconds,
* note: 32-bit values given for reference only
*
*
* Returns:
* - success: The number of milliseconds since the timer was initialized (mod
* 2^16)
*
*
* Usage notes:
*
* - It's unnecessary to keep 16-bit resolution when storing the value returned
@ -130,3 +122,26 @@ uint8_t timer__schedule ( uint16_t milliseconds,
* except within the first 255 milliseconds of the timer being initialized.
*/
// === timer__schedule() ===
/** functions/timer__schedule/description
* Schedule `function` to run in the given number of milliseconds
*
* Arguments:
* - `milliseconds`: The number of milliseconds to wait
* - `function`: A pointer to the function to run
*
* Returns:
* - success: `0`
* - failure: [other]
*
* Warnings:
* - Be careful when scheduling using this function. Keep in mind that the
* function that is scheduled will be run within an interrupt vector.
*
* Usage notes:
* - If a function needs a longer wait time than is possible with a 16-bit
* millisecond resolution counter, it can repeatedly schedule itself to run
* in, say, 1 minute, increment a counter each time, and then only execute
* its body code after, say 5 calls (for a 5 minute delay).
*/

View File

@ -7,12 +7,15 @@
/** description
* Implements the timer functions defined in "../timer.h" for the ATMega32U4
*
* See the accompanying '.md' file for documentation.
* See the accompanying '.md' file for further documentation.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include "../../../firmware/lib/data-types/list.h"
// ----------------------------------------------------------------------------
@ -22,15 +25,39 @@
// ----------------------------------------------------------------------------
typedef struct {
list__node_t _private;
uint16_t milliseconds;
void(*function)(void);
} event_t;
// ----------------------------------------------------------------------------
static volatile uint16_t _milliseconds;
// use `_to_schedule__lock` to make sure that only one function at a time is
// accessing `_to_schedule`, even if that function is preempted in the middle
// of its execution by an interrupt
static list__list_t * _to_schedule;
static bool _to_schedule__lock;
// for use *only* in `ISR(TIMER0_COMPA_vect)` (after initialization)
static list__list_t * _scheduled;
// ----------------------------------------------------------------------------
uint8_t timer__init(void) {
TCCR0A = 0b10000010;
TCCR0B = 0b00000011;
TIMSK0 = 0b00000010;
OCR0A = 250;
_scheduled = list__new();
_to_schedule = list__new();
if (!_scheduled || !_to_schedule)
return 1; // error
OCR0A = 250; // (ticks per millisecond)
TCCR0A = 0b00000010; // (configure Timer/Counter 0)
TCCR0B = 0b00000011; // (configure Timer/Counter 0)
TIMSK0 = 0b00000010; // (enable interrupt vector)
return 0; // success
}
@ -39,9 +66,49 @@ uint16_t timer__get_milliseconds(void) {
return _milliseconds;
}
uint8_t timer__schedule(uint16_t milliseconds, void(*function)(void)) {
if (!function) return 0; // success: there is no function to add
if (_to_schedule__lock) return 1; // error
_to_schedule__lock = true;
event_t * event = list__insert(_to_schedule, -1, malloc(sizeof(event_t)));
if (!event) {
_to_schedule__lock = false;
return 2; // error
}
event->milliseconds = milliseconds;
event->function = function;
_to_schedule__lock = false;
return 0; // success
}
// ----------------------------------------------------------------------------
ISR(TIMER0_COMPA_vect) {
_milliseconds++;
if (!_to_schedule__lock) {
_to_schedule__lock = true;
// pop events from `_to_schedule` and insert them into `_scheduled`
// until there are no more events in `_to_schedule`
while ( list__insert( _scheduled, -1,
list__pop_index(_to_schedule, 0) ) );
_to_schedule__lock = false;
}
for (event_t * event = _scheduled->head; event;) {
if (event->milliseconds == 0) {
(*event->function)();
event = list__pop_node_next(_scheduled, event);
} else {
event->milliseconds--;
event = event->_private.next;
}
}
}

View File

@ -11,9 +11,11 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "../firmware/keyboard.h"
#include "../firmware/lib/timer.h"
#include "../firmware/lib/usb.h"
#include "../firmware/lib/data-types/list.h"
#include "./main.h"
// ----------------------------------------------------------------------------
@ -38,6 +40,8 @@
// ----------------------------------------------------------------------------
// --- for main() loop ---
static bool _pressed_1[OPT__KB__ROWS][OPT__KB__COLUMNS];
static bool _pressed_2[OPT__KB__ROWS][OPT__KB__COLUMNS];
@ -46,19 +50,30 @@ bool (* was_pressed) [OPT__KB__ROWS][OPT__KB__COLUMNS] = &_pressed_2;
uint8_t row;
uint8_t col;
// --- for `main__timer__` functions ---
typedef struct {
list__node_t _private;
uint16_t cycles;
void(*function)(void);
} event_t;
static uint16_t _cycles;
static list__list_t * _scheduled;
// ----------------------------------------------------------------------------
// --- main() loop ------------------------------------------------------------
// ----------------------------------------------------------------------------
/** functions/main/description
* Initialize things, then loop forever
*
* Mostly all that happens here after initialization is that current and
* previous key states are tracked, keys that change state are "executed", the
* USB report is sent, and the LEDs are updated. We also have a delay, to make
* sure we don't detect switch bounce from the keys. Almost everything
* interesting happens somewhere else (especially in
* ".../firmware/keyboard/.../layout"); have a look through the source,
* especially the documentation, to see how things are defined and what's
* actually happening.
* There are a few interesting things that happen here, but most things happen
* elsewhere. Have a look through the source, especially the documentation,
* and especially in ".../firmware/keyboard/.../layout", to see what's going
* on.
*/
int main(void) {
static bool (*temp)[OPT__KB__ROWS][OPT__KB__COLUMNS]; // for swapping below
@ -76,6 +91,7 @@ int main(void) {
kb__led__delay__usb_init(); // give the OS time to load drivers, etc.
timer__init();
main__timer__init();
kb__led__state__ready();
@ -119,8 +135,49 @@ int main(void) {
#undef read
#undef on
#undef off
// take care of `main__timer__` stuff
_cycles++;
for (event_t * event = _scheduled->head; event;) {
if (event->cycles == 0) {
(*event->function)();
event = list__pop_node_next(_scheduled, event);
} else {
event->cycles--;
event = event->_private.next;
}
}
}
return 0;
}
// ----------------------------------------------------------------------------
// --- `main__timer__` functions ----------------------------------------------
// ----------------------------------------------------------------------------
uint8_t main__timer__init(void) {
_scheduled = list__new();
if (!_scheduled) return 1; // error
return 0; // success
}
uint16_t main__timer__cycles(void) {
return _cycles;
}
uint8_t main__timer__schedule(uint16_t cycles, void(*function)(void)) {
if (!function) return 0; // success: there is no function to add
event_t * event = list__insert(_scheduled, -1, malloc(sizeof(event_t)));
if (!event) return 1; // error
event->cycles = cycles;
event->function = function;
return 0; // success
}

View File

@ -11,9 +11,17 @@
*
* Certain variables are declared here so that other functions can see (and
* perhaps modify) them, to accomplish things that may be difficult otherwise.
* If, for instance, a key-function needed to be called on every scan while
* they key was pressed instead of just on state-change: it could set its entry
* in `main__was_pressed` to `false` on every run.
*
* The `main__timer__` functions implement a sort of pseudo-timer, counting the
* number of scan cycles instead of a set amount of real time. They are here
* because there isn't really a better place than `main()` to do that. If you
* need a real timer, take a look in ".../firmware/lib/timer". If not, this is
* probably a better set of functions to work with, since lower timer
* resolution and not having to deal with an interrupt vector means lower
* overhead and less overall to worry about.
*
* See ".../firmware/lib/timer.h" for more information on using timers in
* general.
*/
@ -28,30 +36,98 @@
// ----------------------------------------------------------------------------
/** variables/main__is_pressed/description
* A matrix of `bool`s indicating whether the key at a given position is
* currently pressed
*/
extern bool (* main__is_pressed) [OPT__KB__ROWS][OPT__KB__COLUMNS];
/** variables/main__was_pressed/description
* A matrix of `bool`s indicating whether the key at a given position was
* pressed on the previous scan
*/
extern bool (* main__was_pressed) [OPT__KB__ROWS][OPT__KB__COLUMNS];
/** variables/main__row/description
* Indicates which row is currently being tested for changes of key state
*/
extern uint8_t main__row;
/** variables/main__col/description
* Indicates which column is currently being tested for changes of key state
*/
extern uint8_t main__col;
// ----------------------------------------------------------------------------
uint8_t main__timer__init (void);
uint16_t main__timer__cycles (void);
uint8_t main__timer__schedule (uint16_t cycles, void(*function)(void));
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
#endif // ERGODOX_FIRMWARE__FIRMWARE__MAIN__H
// ============================================================================
// === documentation ==========================================================
// ============================================================================
// ----------------------------------------------------------------------------
// variables ------------------------------------------------------------------
// ----------------------------------------------------------------------------
// === main__is_pressed ===
/** variables/main__is_pressed/description
* A matrix of `bool`s indicating whether the key at a given position is
* currently pressed
*/
// === main__was_pressed ===
/** variables/main__was_pressed/description
* A matrix of `bool`s indicating whether the key at a given position was
* pressed on the previous scan
*/
// === main__row ===
/** variables/main__row/description
* Indicates which row is currently being tested for changes of key state
*/
// === main__col ===
/** variables/main__col/description
* Indicates which column is currently being tested for changes of key state
*/
// ----------------------------------------------------------------------------
// functions ------------------------------------------------------------------
// ----------------------------------------------------------------------------
// === main__timer__init() ===
/** functions/main__timer__init/description
* Initialize the "timer"
*
* Returns:
* - success: `0`
* - failure: [other]
*
* Notes:
* - Should be called by `main()` exactly once before entering the run loop.
*/
// === main__timer__cycles() ===
/** functions/main__timer__cycles/description
* Return the number of cycles since the timer was initialized (mod 2^16)
*
* Returns:
* - success: The number of cycles since the timer was initialized (mod 2^16)
*/
// === main__timer__schedule() ===
/** functions/main__timer__schedule/description
* Schedule `function` to run in the given number of cycles
*
* Arguments:
* - `cycles`: The number of cycles to wait
* - `function`: A pointer to the function to run
*
* Returns:
* - success: `0`
* - failure: [other]
*
* Notes:
* - If possible, prefer scheduling using this function over scheduling using
* `timer__schedule()`. Functions run by this scheduler don't have to be
* quite as careful about finishing quickly, repeating too soon, or modifying
* shared variables, since they will be executing inside the `main()` loop
* instead of inside an interrupt vector.
*/

View File

@ -54,6 +54,9 @@ include $(CURDIR)/options.mk
CURDIR := $(ROOTDIR)/lib/timer
include $(CURDIR)/options.mk
# -------
CURDIR := $(ROOTDIR)/lib/data-types/list
include $(CURDIR)/options.mk
# -------
CURDIR := $(firstword $(CURDIRS))
CURDIRS := $(wordlist 2,$(words $(CURDIRS)),$(CURDIRS))