finished messing with timer :) (for now)

main.c is now out of the timer business (except for "ticking" cycles...
since there really is no other way to guarantee something runs exactly
once per scan cycle)
partial-rewrite
Ben Blazak 2013-05-17 03:02:20 -07:00
parent 7371362576
commit 0b6103d6c3
10 changed files with 234 additions and 403 deletions

View File

@ -10,9 +10,9 @@
*/
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>
// ----------------------------------------------------------------------------

View File

@ -30,7 +30,7 @@ uint8_t timer__schedule__milliseconds ( uint16_t milliseconds,
// ----------------------------------------------------------------------------
// private
void timer___increment_cycles (void);
void timer___tick_cycles (void);
// ----------------------------------------------------------------------------
@ -189,13 +189,14 @@ void timer___increment_cycles (void);
// ----------------------------------------------------------------------------
// private
// === timer___increment_cycles() ===
/** functions/timer___increment_cycles/description
// === timer___tick_cycles() ===
/** functions/timer___tick_cycles/description
* Increment the counter for the number of cycles, and perform scheduled tasks
*
* Meant to be used only by `main()`
*
* Notes:
* - See "./event-list.h" regarding the function name.
* - The corresponding real-time function (dealing with milliseconds instead of
* cycles) will be in an interrupt vector, and not explicitly called anywhere
* in the code.

View File

@ -1,122 +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 non device specific functionality of
* ".../firmware/lib/timer", and all the functions defined in "./common.h"
*/
#include "./common.h"
// ----------------------------------------------------------------------------
uint8_t timer__init(void) {
_cycles_scheduled = list__new();
_milliseconds_scheduled = list__new();
_milliseconds_to_schedule = list__new();
if ( !_cycles_scheduled ||
!_milliseconds_scheduled ||
!_milliseconds_to_schedule ) {
list__free(_cycles_scheduled);
list__free(_milliseconds_scheduled);
list__free(_milliseconds_to_schedule);
return 1; // error
}
return _device__init();
}
uint16_t timer__get_cycles(void) {
return _cycles;
}
uint16_t timer__get_milliseconds(void) {
return _milliseconds;
}
uint8_t timer__schedule_cycles(uint16_t cycles, void(*function)(void)) {
// - there is the possibility that this function could be called by an
// event executing within an interrupt vector. if that happens, and the
// interrupt originally interrupted this same function, it wouldn't be
// safe to operate on `_cycles_scheduled`
if (lock) return 1; // error
lock = true;
uint8_t ret = _event_list__append(_cycles_scheduled, cycles, function);
lock = false;
return ret;
}
uint8_t timer__schedule_milliseconds( uint16_t milliseconds,
void(*function)(void) ) {
// - even though the only other place `...to_schedule` should be referenced
// is within an interrupt vector (which this function can't interrupt),
// we have to be careful: what if this function is interrupted, and an
// event executes that calls this function? this function would
// essentially be interrupting itself. we must avoid that.
if (_milliseconds_to_schedule_lock) return 1; // error
_milliseconds_to_schedule_lock = true;
uint8_t ret = _event_list__append( _milliseconds_to_schedule,
milliseconds,
function );
_milliseconds_to_schedule_lock = false;
return ret;
}
// ----------------------------------------------------------------------------
void timer___increment_cycles (void) {
_cycles++;
_event_list__update(_cycles_scheduled);
}
// ----------------------------------------------------------------------------
uint8_t _event_list__append( list__list_t * list,
uint16_t ticks,
void(*function)(void) ) {
if (!function) return 0; // success: there is no function to add
_event_t * event = list__insert(list, -1, malloc(sizeof(_event_t)) );
if (!event) return 1; // error
event->ticks = ticks;
event->function = function;
return 0; // success
}
void _event_list__update(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->_private.next;
}
}
}
void _event_list__move(list__list_t * to, list__list_t * from) {
if (from->length == 0) return; // nothing to move
((_event_t *)to->tail)->_private.next = from->head;
to->tail = from->tail;
to->length += from->length;
from->head = NULL;
from->tail = NULL;
from->length = 0;
}

View File

@ -1,218 +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
* A central place for all `#include`s and common definitions relevant to this
* timer library
*
* Prefix: `_`
*/
#ifndef ERGODOX_FIRMWARE__LIB__TIMER__COMMON__H
#define ERGODOX_FIRMWARE__LIB__TIMER__COMMON__H
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "../../../firmware/lib/data-types/list.h"
// ----------------------------------------------------------------------------
typedef struct {
list__node_t _private; // "subclass" `list__node_t`
uint16_t ticks; // cycles or milliseconds
void(*function)(void);
} _event_t;
// ----------------------------------------------------------------------------
uint16_t _cycles;
list__list_t * _cycles_scheduled;
list__list_t * _cycles_to_schedule;
list__list_t * _cycles_to_schedule_lock;
volatile uint16_t _milliseconds;
list__list_t * _milliseconds_scheduled;
list__list_t * _milliseconds_to_schedule;
bool _milliseconds_to_schedule_lock;
// ----------------------------------------------------------------------------
uint8_t _device__init (void);
uint8_t _event_list__append ( list__list_t * list,
uint16_t ticks,
void(*function)(void) );
void _event_list__update (list__list_t * list);
void _event_list__move (list__list_t * to, list__list_t * from);
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
#endif // ERGODOX_FIRMWARE__LIB__TIMER__COMMON__H
// ============================================================================
// === Documentation ==========================================================
// ============================================================================
// ----------------------------------------------------------------------------
// typedefs -------------------------------------------------------------------
// ----------------------------------------------------------------------------
// === _event_t ===
/** typedefs/_event_t/description
* A struct to represent a scheduled event
*
* Members:
* - `_private`: The C way of "subclassing" another type
* - `ticks`: The number of cycles or milliseconds in which the given event
* should run
* - `function`: A pointer to the function to run
*/
// ----------------------------------------------------------------------------
// variables ------------------------------------------------------------------
// ----------------------------------------------------------------------------
// === _cycles ===
/** variables/_cycles/description
* The number of scan cycles since the timer was initialized (mod 2^16)
*/
// === _cycles_scheduled ===
/** variables/_cycles_scheduled/description
* A list of scheduled events, to be run in a given number of cycles
*
* Meant to be used only by `timer___increment_cycles()` (after initialization)
*/
// === _cycles_to_schedule ===
//
/** variables/_cycles_to_schedule/description
* A list of events to schedule
*
* Meant to be used only by `timer__schedule_cycles()` and
* `timer___increment_cycles()` (after initialization)
*
* `timer___increment_cycles()` will move the events in this list to
* `_cycles_scheduled` the next time it runs and the latter list is not locked.
*
* The reason for having both `_cycles_scheduled` and `_cycles_to_schedule` is
* that this allows us to only reference `_cycles_scheduled` from place, which
* in turn allows us not to have to worry about whether that function will have
* access to it when it runs.
*/
// === _cycles_to_schedule_lock ===
//
/** variables/_milliseconds_to_schedule_lock/description
* A lock for `_milliseconds_to_schedule`
*
* To make sure that if `timer__schedule_milliseconds()` is interrupted by the
* interrupt vector that may touch `_milliseconds_to_schedule`, the interrupt
* leaves the variable alone until the function is done with it.
*/
// === _milliseconds ===
/** variables/_milliseconds/description
* The number of milliseconds since the timer was initialized (mod 2^16)
*/
// === _milliseconds_scheduled ===
/** variables/_milliseconds_scheduled/description
* A list of scheduled events, to be run in a given number of milliseconds
*
* Meant to be used only in the interrupt vector responsible for updating
* `_milliseconds` (after initialization)
*/
// === _milliseconds_to_schedule ===
/** variables/_milliseconds_to_schedule/description
* A list of events to schedule
*
* Meant to be used only by `timer__schedule_milliseconds()` and the interrupt
* vector that updates `_milliseconds` (after initialization)
*
* The responsible interrupt vector will move the events in this list to
* `_milliseconds_scheduled` the next time it runs and the latter list is not
* locked.
*
* The reason for having both `_milliseconds_scheduled` and
* `_milliseconds_to_schedule` is that this way only the events that have not
* yet been scheduled are effected if `timer__schedule_milliseconds()` is
* interrupted by the interrupt vector. In this case, time for these
* unscheduled events will not start being counted until they are scheduled,
* hopefully on the interrupt vector's next run. If, on the other hand, there
* were only one list and the function was interrupted, every event in the list
* would be inaccessible to the interrupt vector, and we would not be able to
* count time for *any* of them during that run.
*/
// === _milliseconds_to_schedule_lock ===
/** variables/_milliseconds_to_schedule_lock/description
* A lock for `_milliseconds_to_schedule`
*
* To make sure that if `timer__schedule_milliseconds()` is interrupted by the
* interrupt vector that may touch `_milliseconds_to_schedule`, the interrupt
* leaves the variable alone until the function is done with it.
*/
// ----------------------------------------------------------------------------
// functions ------------------------------------------------------------------
// ----------------------------------------------------------------------------
// === _device__init() ===
/** functions/_device__init/description
* Initialize the device specific portions of the timer
*
* Meant to be called only by `timer__init()`
*
* Returns:
* - success: `0`
* - failure: [other]
*/
// === _event_list__append() ===
/** functions/_event_list__append/description
* Append an event with the given attributes to `list`
*
* Arguments:
* - `list`: The list to add the new event to
* - `ticks`: The number of ticks to assign to the event
* - `function`: A pointer to the function to assign to the event
*
* Returns:
* - success: `0`
* - failure: [other]
*/
// === _event_list__update() ===
/** functions/_event_list__update/description
* Decrement the `ticks` of every event in `list` by `1`, and run any event
* with `ticks == 0`
*
* Arguments:
* - `list`: The list to update
*/
// === _event_list__move() ===
/** functions/_event_list__move/description
* Move all events in `from` to `to`, and clear `to`
*
* Arguments:
* - `to`: The list into which to move all elements
* - `from`: The list to move all elements from
*/

View File

@ -1,45 +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
* Timer interface for device functions
*
* Prefix: `timer___device__`
*/
#ifndef ERGODOX_FIRMWARE__LIB__TIMER__DEVICE__H
#define ERGODOX_FIRMWARE__LIB__TIMER__DEVICE__H
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// private
void timer___device__init (void);
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
#endif // ERGODOX_FIRMWARE__LIB__TIMER__DEVICE__H
// ============================================================================
// === documentation ==========================================================
// ============================================================================
// ----------------------------------------------------------------------------
// functions ------------------------------------------------------------------
// ----------------------------------------------------------------------------
// === timer___device__init() ===
/** functions/timer___device__init/description
* Initialize the device specific portions of the timer
*/

View File

@ -5,16 +5,22 @@
* ------------------------------------------------------------------------- */
/** description
* Implements the device specific functionality of ".../firmware/lib/timer" for
* the ATMega32U4
* Implements the timer interface defined in ".../firmware/lib/timer.h" for the
* ATMega32U4
*
* See the accompanying '.md' file for further documentation.
*
* Notes:
* - Not all of this code is device-specific, but the parts that aren't are
* trivial enough that it's not worth separating them.
*/
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include "../common.h"
#include "../../../../firmware/lib/data-types/list.h"
#include "../event-list.h"
// ----------------------------------------------------------------------------
@ -24,7 +30,44 @@
// ----------------------------------------------------------------------------
uint8_t _device__init(void) {
/** variables/_cycles/description
* The number of scan cycles since the timer was initialized (mod 2^16)
*/
static uint16_t _cycles;
/** variables/_cycles__scheduled/description
* A list of scheduled events, to be run in a given number of cycles
*
* After initialization, this should *only* be used as an argument to an
* `event_list__...()` function.
*/
static list__list_t * _cycles__scheduled;
/** variables/_milliseconds/description
* The number of milliseconds since the timer was initialized (mod 2^16)
*/
static volatile uint16_t _milliseconds;
/** variables/_milliseconds__scheduled/description
* A list of scheduled events, to be run in a given number of milliseconds
*
* After initialization, this should *only* be used as an argument to an
* `event_list__...()` function.
*/
static list__list_t * _milliseconds__scheduled;
// ----------------------------------------------------------------------------
uint8_t timer__init(void) {
_cycles__scheduled = list__new();
_milliseconds__scheduled = list__new();
if (!_cycles__scheduled || !_milliseconds__scheduled) {
list__free(_cycles__scheduled);
list__free(_milliseconds__scheduled);
return 1; // error
}
OCR0A = 250; // (ticks per millisecond)
TCCR0A = 0b00000010; // (configure Timer/Counter 0)
TCCR0B = 0b00000011; // (configure Timer/Counter 0)
@ -34,17 +77,36 @@ uint8_t _device__init(void) {
return 0; // success
}
uint16_t timer__get_cycles(void) {
return _cycles;
}
uint16_t timer__get_milliseconds(void) {
return _milliseconds;
}
uint8_t timer__schedule_cycles(uint16_t cycles, void(*function)(void)) {
return event_list__append(_cycles__scheduled, cycles, function);
}
uint8_t timer__schedule_milliseconds( uint16_t milliseconds,
void(*function)(void) ) {
return event_list__append( _milliseconds__scheduled,
milliseconds,
function );
}
// ----------------------------------------------------------------------------
void timer___tick_cycles (void) {
_cycles++;
event_list__tick(_cycles__scheduled);
}
// ----------------------------------------------------------------------------
ISR(TIMER0_COMPA_vect) {
_milliseconds++;
// if possible, move all events from `...to_schedule` into `...scheduled`
// - interrupts are disabled here, so it's safe to operate on the list
// without locking it, as long as it's not already locked
if (!_milliseconds_to_schedule_lock)
_event_list__move(_milliseconds_scheduled, _milliseconds_to_schedule);
_event_list__update(_milliseconds_scheduled);
event_list__tick(_milliseconds__scheduled);
}

View File

@ -0,0 +1,90 @@
/* ----------------------------------------------------------------------------
* 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 only within ".../firmware/lib/timer"
*
*
* Usage notes:
*
* - A "tick" is an ill-defined unit of time. It may represent any occurrence,
* even a randomly timed one; but it makes the most sense for it to count
* something that occurs predictably, like the passing of a millisecond, or
* the completion of a scan cycle.
*
* - To "tick" (as a verb) is to denote the passage of a "tick" of time by
* performing the actions corresponding thereto.
*
* - All functions declared here should be safe for use within interrupt
* routines, as long as you pay attention to the warnings below.
*
*
* Warnings:
*
* - Any list passed as an argument to one of these functions must not be used
* *anywhere* except as an argument to one of these functions.
*/
#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`: 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`: The list to "tick"
*/

View File

@ -0,0 +1,63 @@
/* ----------------------------------------------------------------------------
* 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" for the
* ATMega32U4 (though, the code should be the same for anything in the AVR
* family)
*/
#include <stdint.h>
#include <util/atomic.h>
#include "../../../../firmware/lib/data-types/list.h"
// ----------------------------------------------------------------------------
typedef struct {
list__node_t _private; // "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 || !list) return 0; // success: nothing to do
event_t * event = malloc(sizeof(event_t));
if (!event) return 1; // error
event->ticks = ticks;
event->function = function;
ATOMIC_BLOCK( ATOMIC_RESTORESTATE ) {
list__insert(list, -1, event);
}
return 0; // success
}
void event_list__tick(list__list_t * list) {
if (!list) return;
ATOMIC_BLOCK( ATOMIC_RESTORESTATE ) {
for (event_t * event = list->head; event;) {
if (event->ticks == 0) {
NONATOMIC_BLOCK( NONATOMIC_RESTORESTATE ) {
(*event->function)();
}
event = list__pop_node_next(list, event);
} else {
event->ticks--;
event = event->_private.next;
}
}
}
}

View File

@ -11,6 +11,6 @@
#
SRC += $(wildcard $(CURDIR)/*.c)
SRC += $(wildcard $(CURDIR)/device/$(MCU).c)
SRC += $(wildcard $(CURDIR)/event-list/$(MCU).c)

View File

@ -129,7 +129,7 @@ int main(void) {
#undef off
}
timer___increment_cycles();
timer___tick_cycles();
}
return 0;