messing with timers again; working on sticky keys

partial-rewrite
Ben Blazak 2013-05-24 21:10:03 -07:00
parent f2acad70a4
commit 58255dc29b
13 changed files with 207 additions and 218 deletions

View File

@ -20,7 +20,7 @@
#include <stddef.h>
#include <stdint.h>
#include <avr/pgmspace.h>
#include "../../../../../firmware/lib/data-types/list.h"
#include "../../../../../firmware/lib/timer.h"
#include "../../../../../firmware/lib/usb.h"
#include "../../../../../firmware/lib/usb/usage-page/keyboard.h"
#include "../../../../../firmware/lib/layout/key-functions.h"
@ -113,13 +113,15 @@ static _layout_t PROGMEM _layout;
* A collection of flags pertaining to the operation of `...exec_key()`
*
* Struct members:
* - stick_current: TODO: probably just want 'tick_keypresses'?
* - `tick_keypresses`: A predicate indicating whether or not to "tick"
* keypresses on this run of the function (see the documentation in
* ".../firmware/lib/timer.h" for more precisely what this means)
* - This is useful for defining things like sticky keys, if, e.g., you
* want to make it so that you can press more than one and have none of
* them release until the press of the next normal key.
*/
static struct {
bool stick_current : 1;
bool stick_press : 1;
bool stick_release : 1;
bool unstick_all : 1;
bool tick_keypresses : 1;
} _flags;

View File

@ -31,11 +31,14 @@ void kb__layout__exec_key(bool pressed, uint8_t row, uint8_t column) {
// we've previously set
static uint8_t pressed_layer[OPT__KB__ROWS][OPT__KB__COLUMNS];
// TODO: keep track of sticky keys
void (*function)(void);
uint8_t layer;
// - add 1 to the stack size because we spend the first iteration checking
// to see if we need to release on a previously stored layer
// - add 1 to the stack size in order to peek out of bounds on the last
// iteration (if we get that far), so that layer 0 is our default (see
// the documentation for ".../firmware/lib/layout/layer-stack.h")
for (uint8_t i=0; i < layer_stack__size()+1+1; i++) { // i = offset+1
if (i == 0)
if (!pressed)
@ -60,11 +63,10 @@ void kb__layout__exec_key(bool pressed, uint8_t row, uint8_t column) {
(*function)();
// TODO: implement sticky keys
// if (pressed && _sticky_key) {
// (*_sticky_key)();
// _sticky_key = NULL;
// }
if (_flags.tick_keypresses)
timer___tick_keypresses();
_flags.tick_keypresses = true; // restore default
return;
}

View File

@ -97,8 +97,10 @@
* #define keys__release__lpo1l1 KF(nop)
*/
#define KEYS__LAYER__PUSH_POP(ID, LAYER) \
void P(lpupo##ID##l##LAYER) (void) { layer_stack__push(0, ID, LAYER); } \
void R(lpupo##ID##l##LAYER) (void) { layer_stack__pop_id(ID); }
void P(lpupo##ID##l##LAYER) (void) { layer_stack__push(0, ID, LAYER); \
_flags.tick_keypresses = false; } \
void R(lpupo##ID##l##LAYER) (void) { layer_stack__pop_id(ID); \
_flags.tick_keypresses = false; }
/** macros/(group) layer : number pad/description
* Define functions for pushing and popping the number pad (namely `numPush`,
@ -117,22 +119,28 @@
KF(press)(KEYBOARD__LockingNumLock); \
usb__kb__send_report(); \
KF(release)(KEYBOARD__LockingNumLock); \
usb__kb__send_report(); } \
usb__kb__send_report(); \
_flags.tick_keypresses = false; } \
void R(numPuPo) (void) { layer_stack__pop_id(ID); \
KF(press)(KEYBOARD__LockingNumLock); \
usb__kb__send_report(); \
KF(release)(KEYBOARD__LockingNumLock); \
usb__kb__send_report(); }
usb__kb__send_report(); \
_flags.tick_keypresses = false; }
#define KEYS__LAYER__NUM_PUSH(ID, LAYER) \
void P(numPush) (void) { layer_stack__push(0, ID, LAYER); \
KF(press)(KEYBOARD__LockingNumLock); } \
void R(numPush) (void) { KF(release)(KEYBOARD__LockingNumLock); }
KF(press)(KEYBOARD__LockingNumLock); \
_flags.tick_keypresses = false; } \
void R(numPush) (void) { KF(release)(KEYBOARD__LockingNumLock); \
_flags.tick_keypresses = false; }
#define KEYS__LAYER__NUM_POP(ID) \
void P(numPop) (void) { layer_stack__pop_id(ID); \
KF(press)(KEYBOARD__LockingNumLock); } \
void R(numPop) (void) { KF(release)(KEYBOARD__LockingNumLock); }
KF(press)(KEYBOARD__LockingNumLock); \
_flags.tick_keypresses = false; } \
void R(numPop) (void) { KF(release)(KEYBOARD__LockingNumLock); \
_flags.tick_keypresses = false; }
// ----------------------------------------------------------------------------

View File

@ -28,13 +28,13 @@
* something like
*
* typedef struct {
* list__node_t _private; // must be the first element
* list__node_t _super; // 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) {
* for (node_t * node = list.head; node; node = node->_super.next) {
* // do stuff
* }
*

View File

@ -10,7 +10,7 @@
* Prefix: `layer_stack__`
*
* This file is meant to be included and used by the keyboard layout
* implemenmtation.
* implementation.
*/

View File

@ -8,11 +8,36 @@
* Timer interface
*
* Prefixes: `timer__`, `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 (i.e. incrementing the
* counter and running any scheduled events).
*
* - A "timer" is a collection of related `...get...()`, `...schedule...()`,
* and `...tick...()` functions, all dealing with the same (not externally
* visible) variables.
*
* - For milliseconds
*
* ---------------------------------------------------------------------
* number highest value in in in in
* of bits (milliseconds) seconds minutes hours days
* --------- ---------------- ----------- --------- -------- ------
* 8 255 0.3 0.0 0.0 0.0
* 16 65535 65.5 1.1 0.0 0.0
* 32 4294967295 4294967.3 71582.8 1193.0 49.7
* ---------------------------------------------------------------------
*
* note: 32-bit values given for reference only
*/
// TODO: make macros for defining timers; make groups for function
// documentation; make notes for all extra information
#ifndef ERGODOX_FIRMWARE__LIB__TIMER__H
#define ERGODOX_FIRMWARE__LIB__TIMER__H
@ -23,17 +48,19 @@
uint8_t timer__init (void);
uint16_t timer__get_cycles (void);
uint16_t timer__get_keypresses (void);
uint16_t timer__get_milliseconds (void);
uint8_t timer__schedule_cycles ( uint16_t cycles,
void(*function)(void) );
uint8_t timer__schedule_milliseconds ( uint16_t milliseconds,
void(*function)(void) );
uint8_t timer__schedule_cycles (uint16_t ticks, void(*function)(void));
uint8_t timer__schedule_keypresses (uint16_t ticks, void(*function)(void));
uint8_t timer__schedule_milliseconds (uint16_t ticks, void(*function)(void));
// ----------------------------------------------------------------------------
// private
void timer___tick_cycles (void);
void timer___tick_cycles (void);
void timer___tick_keypresses (void);
void timer___tick_milliseconds (void);
// ----------------------------------------------------------------------------
@ -63,63 +90,44 @@ void timer___tick_cycles (void);
* - Should be called exactly once by `main()` before entering the run loop.
*/
// === timer__get_cycles() ===
/** functions/timer__get_cycles/description
* Return the number of scan cycles since the timer was initialized (mod 2^16)
// === (group) get() ===
/** functions/(group) get/description
* Return the number of "ticks" since the given timer was initialized
* (mod 2^16)
*
* Members:
* - `timer__get_cycles`
* - `timer__get_keypresses`
* - `timer__get_milliseconds`
*
* 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)
*
* ---------------------------------------------------------------------
* number highest value in in in in
* of bits (milliseconds) seconds minutes hours days
* --------- ---------------- ----------- --------- -------- ------
* 8 255 0.3 0.0 0.0 0.0
* 16 65535 65.5 1.1 0.0 0.0
* 32 4294967295 4294967.3 71582.8 1193.0 49.7
* ---------------------------------------------------------------------
*
* note: 32-bit values given for reference only
*
*
* Returns:
* - success: The number of milliseconds since the timer was initialized (mod
* 2^16)
* - success: The number of "ticks" since the timer was initialized (mod 2^16)
*
*
* Usage notes:
*
* - It's unnecessary to keep 16-bit resolution when storing the value returned
* by `timer__get_milliseconds()` if you don't need it. Use variables of the
* smallest type that can (*always*) hold the amount of time you'll be
* dealing with.
* by a get function if you don't need it. Use variables of the smallest
* type that can (*always*) hold the amount of time you'll be dealing with.
*
* - Use `end_time - start_time` for determining time difference. Since the
* returned values are unsigned (and you should be storing them in unsigned
* variables as well) this will work across overflows, for up to the maximum
* amount of milliseconds representable by the type you're using. (See [this
* answer] (http://stackoverflow.com/a/50632) on <http://stackoverflow.com/>
* if you're curious as to why this workes across overflows.)
* if you're curious as to why this works across overflows.)
*
*
* Warnings:
*
* - Do not cast the return value of `timer__get_milliseconds()` directly.
* Instead, store the return value in a smaller variable
* - Do not cast the return value of a get function directly. Instead, store
* the return value in a smaller variable
*
* uint8_t start_time = timer__get_milliseconds()
* uint8_t start_time = timer__get_cycles()
*
* or cast the expression as a whole
*
* (uint8_t)( timer__get_milliseconds() - start_time )
* (uint8_t)( timer__get_cycles() - start_time )
*
* Casting directly within the end condition check of a `while` or `for` loop
* does not produce the desired behavior. I don't know assembly well enough
@ -145,53 +153,38 @@ void timer___tick_cycles (void);
* except within the first 2^8 milliseconds of the timer being initialized.
*/
// === timer__schedule_cycles() ===
/** functions/timer__schedule_cycles/description
* Schedule `function` to run in the given number of cycles
// === (group) schedule() ===
/** functions/(group) schedule/description
* Schedule `function` to run in the given number of "ticks"
*
* Members:
* - `timer__schedule_cycles`
* - `timer__schedule_keypresses`
* - `timer__schedule_milliseconds`
*
* Arguments:
* - `cycles`: The number of cycles to wait
* - `ticks`: The number of ticks to wait
* - `function`: A pointer to the function to run
*
* Returns:
* - success: `0`
* - failure: [other]
*
* Usage notes:
* - If possible, prefer this function to `timer__schedule_milliseconds()`: it
* has slightly lower overhead (since scan cycles are going to be longer than
* 1 millisecond); and since functions scheduled here are not executed inside
* an interrupt vector, you need not worry about any of them messing with
* access to a shared resource.
*/
// === timer__schedule_milliseconds() ===
/** functions/timer__schedule_milliseconds/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]
*
* Usage notes:
* - Functions will be run with interrupts enabled, so you need not worry more
* than usual about how long your function takes.
*
* - 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 (= 1000*60 milliseconds), increment a counter each time,
* and then only execute its body code after, say, 5 calls (for a 5 minute
* delay).
* resolution counter, it can repeatedly schedule itself to run in, say, 1
* minute (= 1000*60 milliseconds) (using the millisecond timer), increment a
* counter each time, and then only execute its body code after, say, 5 calls
* (for a 5 minute delay).
*
* Warnings:
* - Be *very* careful when using this to schedule functions that share
* resources: functions scheduled here are still called from within an
* interrupt vector, and the interrupt vector may have interrupted something
* else in the middle of an access to that resource. You must pay full
* attention to all the issues this could possibly cause.
* - The milliseconds timer is unique in that events may not be run as soon as
* one would expect: all events scheduled to be run sometime during a scan
* cycle should be run at the end of that scan cycle. This is done to avoid
* having scheduled functions executing within an interrupt vector, which is
* (in a case like this where everything has to be so generalized) really not
* worth the pain.
*/
// ----------------------------------------------------------------------------
@ -202,11 +195,28 @@ void timer___tick_cycles (void);
* 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.
*/
// === timer___tick_keypresses() ===
/** functions/timer___tick_keypresses/description
* Increment the counter for the number of keypresses, and perform scheduled
* tasks
*
* Meant to be used only by `kb__layout__exec_key()`
*/
// === timer___tick_milliseconds() ===
/** functions/timer___tick_milliseconds/description
* Perform scheduled tasks *only*
*
* Meant to be used only by `main()`
*
* The counter for this timer should be incremented within an interrupt vector
* (and so needs no `...tick...()` function), but in order to avoid the
* complications of having scheduled functions run within the interrupt vector
* as well, this function splits that portion of the timer's functionality out.
* It should be called by `main()` once per cycle, and should execute all tasks
* that were scheduled to be run between the last time it was called and the
* time of the current call.
*/

View File

@ -45,14 +45,22 @@ uint16_t timer__get_milliseconds(void) {
return _milliseconds__counter;
}
uint8_t timer__schedule_milliseconds( uint16_t milliseconds,
void(*function)(void)) {
uint8_t timer__schedule_milliseconds(uint16_t ticks, void(*function)(void)) {
return event_list__append(
_milliseconds__scheduled_events, milliseconds, function );
_milliseconds__scheduled_events, ticks, function );
}
void timer___tick_milliseconds(void) {
static uint8_t _milliseconds__last_ticked;
uint8_t elapsed = timer__get_milliseconds() - _milliseconds__last_ticked;
for (uint8_t i=0; i<elapsed; i++)
event_list__tick(_milliseconds__scheduled_events);
_milliseconds__last_ticked += elapsed;
}
ISR(TIMER0_COMPA_vect) {
_milliseconds__counter++;
event_list__tick(_milliseconds__scheduled_events);
}

View File

@ -0,0 +1,53 @@
/* ----------------------------------------------------------------------------
* 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"
// ----------------------------------------------------------------------------
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;
}
}
}

View File

@ -9,28 +9,11 @@
*
* 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 (i.e. incrementing the
* counter and running any scheduled events).
*
* - All functions declared here should be safe for use within interrupt
* routines, as long as you pay attention to the warnings below.
*
* Meant to be used within ".../firmware/lib/timer"
*
* 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.
* - These functions are not safe for use within interrupt vectors. Just in
* case you were tempted to try :)
*/
@ -87,5 +70,10 @@ void event_list__tick (list__list_t list);
*
* Arguments:
* - `list`: 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.
*/

View File

@ -1,82 +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" for the
* ATMega32U4
*
* Notes:
* - The code should be the same for anything in the AVR family. It's the
* `<util/atomic>` macros that make this non-universal.
*/
#include <stdint.h>
#include <stdlib.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) 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) {
event_t * next;
event_t * run = NULL; // for keeping track of events to run
// go through the list
// - keep track of the events that need to be run this "tick"
// - note that every other event is one "tick" closer to being run
ATOMIC_BLOCK( ATOMIC_RESTORESTATE ) {
for (event_t * event = list.head; event;) {
if (event->ticks == 0) {
next = event->_private.next;
list__pop_node(list, event);
event->_private.next = run;
run = event;
event = next;
} else {
event->ticks--;
event = event->_private.next;
}
}
}
// run all the scheduled events, with interrupts enabled
NONATOMIC_BLOCK( NONATOMIC_RESTORESTATE ) {
while (run) {
next = run->_private.next;
(*run->function)();
free(run);
run = next;
}
}
}

View File

@ -35,6 +35,5 @@
// ----------------------------------------------------------------------------
DEFINE_TIMER(cycles);
// TODO: make a timer for keypresses?
DEFINE_TIMER(keypresses);

View File

@ -129,6 +129,7 @@ int main(void) {
}
timer___tick_cycles();
timer___tick_milliseconds();
}
return 0;

View File

@ -92,7 +92,7 @@ extern struct main__flags_t main__flags;
* A collection of flags pertaining to the operation of `main()`
*
* Struct members:
* - update_leds: A predicate indicating whether to update the keyboard LED
* - `update_leds`: A predicate indicating whether to update the keyboard LED
* state based on the USB LED state.
* - 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