intermediate checkin: working on timer
was about to start handling the scheduling for cycle counted events the same way as for real time scheduled events, but i think i'll switch over to using `<util/atomic.h>`, if i canpartial-rewrite
parent
758e95b17e
commit
7371362576
|
@ -164,12 +164,14 @@ void * list__pop_node_next(list__list_t * list, void * node) {
|
|||
}
|
||||
|
||||
void list__free(list__list_t * list) {
|
||||
void * node;
|
||||
while (list->head) {
|
||||
node = list->head;
|
||||
list->head = N(list->head)->next;
|
||||
free(node);
|
||||
if (list) {
|
||||
void * node;
|
||||
while (list->head) {
|
||||
node = list->head;
|
||||
list->head = N(list->head)->next;
|
||||
free(node);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
/** description
|
||||
* Timer interface
|
||||
*
|
||||
* Prefix: `timer__`
|
||||
* Prefixes: `timer__`, `timer___`
|
||||
*/
|
||||
|
||||
|
||||
|
@ -18,10 +18,19 @@
|
|||
|
||||
|
||||
uint8_t timer__init (void);
|
||||
|
||||
uint16_t timer__get_cycles (void);
|
||||
uint16_t timer__get_milliseconds (void);
|
||||
|
||||
uint8_t timer__schedule ( uint16_t milliseconds,
|
||||
void(*function)(void) );
|
||||
uint8_t timer__schedule__cycles ( uint16_t cycles,
|
||||
void(*function)(void) );
|
||||
uint8_t timer__schedule__milliseconds ( uint16_t milliseconds,
|
||||
void(*function)(void) );
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// private
|
||||
|
||||
void timer___increment_cycles (void);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -51,6 +60,17 @@ uint8_t timer__schedule ( uint16_t milliseconds,
|
|||
* - Should be called exactly once by `main()` before entering the run loop.
|
||||
*/
|
||||
|
||||
// === timer__get_cycles() ===
|
||||
/** functions/timer__get_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)
|
||||
*
|
||||
* Usage notes:
|
||||
* - See the documentation for `timer__get_milliseconds()`
|
||||
*/
|
||||
|
||||
// === timer__get_milliseconds() ===
|
||||
/** functions/timer__get_milliseconds/description
|
||||
* Return the number of milliseconds since the timer was initialized (mod 2^16)
|
||||
|
@ -122,8 +142,28 @@ uint8_t timer__schedule ( uint16_t milliseconds,
|
|||
* except within the first 255 milliseconds of the timer being initialized.
|
||||
*/
|
||||
|
||||
// === timer__schedule() ===
|
||||
/** functions/timer__schedule/description
|
||||
// === timer__schedule__cycles() ===
|
||||
/** functions/timer__schedule__cycles/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]
|
||||
*
|
||||
* Usage notes:
|
||||
* - If possible, prefer scheduling using this function over scheduling using
|
||||
* `timer__schedule__milliseconds()`. 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 not be executing inside an
|
||||
* interrupt vector.
|
||||
*/
|
||||
|
||||
// === timer__schedule__milliseconds() ===
|
||||
/** functions/timer__schedule__milliseconds/description
|
||||
* Schedule `function` to run in the given number of milliseconds
|
||||
*
|
||||
* Arguments:
|
||||
|
@ -141,7 +181,23 @@ uint8_t timer__schedule ( uint16_t milliseconds,
|
|||
* 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).
|
||||
* in, say, 1 minute (= 1000*60 milliseconds), increment a counter each time,
|
||||
* and then only execute its body code after, say, 5 calls (for a 5 minute
|
||||
* delay).
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// private
|
||||
|
||||
// === timer___increment_cycles() ===
|
||||
/** functions/timer___increment_cycles/description
|
||||
* Increment the counter for the number of cycles, and perform scheduled tasks
|
||||
*
|
||||
* Meant to be used only by `main()`
|
||||
*
|
||||
* Notes:
|
||||
* - 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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,114 +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 timer functions defined in "../timer.h" for the ATMega32U4
|
||||
*
|
||||
* 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"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if F_CPU != 16000000
|
||||
#error "Expecting different CPU frequency"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
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) {
|
||||
_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
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* 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
|
||||
*/
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* 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
|
||||
*/
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* 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 device specific functionality of ".../firmware/lib/timer" for
|
||||
* the ATMega32U4
|
||||
*
|
||||
* See the accompanying '.md' file for further documentation.
|
||||
*/
|
||||
|
||||
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
#include "../common.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if F_CPU != 16000000
|
||||
#error "Expecting different CPU frequency"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t _device__init(void) {
|
||||
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
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -11,5 +11,6 @@
|
|||
#
|
||||
|
||||
|
||||
SRC += $(wildcard $(CURDIR)/$(MCU).c)
|
||||
SRC += $(wildcard $(CURDIR)/*.c)
|
||||
SRC += $(wildcard $(CURDIR)/device/$(MCU).c)
|
||||
|
||||
|
|
|
@ -54,18 +54,6 @@ uint8_t col;
|
|||
|
||||
bool update_leds = true;
|
||||
|
||||
// --- 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 ------------------------------------------------------------
|
||||
|
@ -95,7 +83,6 @@ 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();
|
||||
|
||||
|
@ -142,48 +129,9 @@ int main(void) {
|
|||
#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;
|
||||
}
|
||||
}
|
||||
timer___increment_cycles();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -11,17 +11,6 @@
|
|||
*
|
||||
* Certain variables are declared here so that other functions can see (and
|
||||
* perhaps modify) them, to accomplish things that may be difficult otherwise.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -44,12 +33,6 @@ extern uint8_t main__col;
|
|||
|
||||
extern bool main__update_leds;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint8_t main__timer__init (void);
|
||||
uint16_t main__timer__cycles (void);
|
||||
uint8_t main__timer__schedule (uint16_t cycles, void(*function)(void));
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -96,52 +79,7 @@ uint8_t main__timer__schedule (uint16_t cycles, void(*function)(void));
|
|||
* This is for taking over control the LEDs temporarily, as one may want to
|
||||
* do when in a special mode, etc. If you want to change the meaning of the
|
||||
* LEDs under normal use, the correct place to do that is in the layout file,
|
||||
* where the `kb__led__logical_*()` functions are defined (see the
|
||||
* where the `kb__led__logical_...()` functions are defined (see the
|
||||
* documentation in that and related files for more information).
|
||||
*/
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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.
|
||||
*/
|
||||
|
||||
|
|
Loading…
Reference in New Issue