working on eemprom-macro :)
parent
f0fcfca4ef
commit
b41a716631
|
@ -237,8 +237,8 @@ void eeprom_macro__clear_all (void);
|
|||
* - `column`: The column of the key action
|
||||
*
|
||||
* Returns:
|
||||
* - `true`: if a macro with the given UID exists
|
||||
* - `false`: if a macro with the given UID does not exist
|
||||
* - `true`: if a macro remapping the given key-action exists
|
||||
* - `false`: if a macro remapping the given key-action does not exist
|
||||
*/
|
||||
|
||||
// === eeprom_macro__clear() ===
|
||||
|
|
|
@ -20,11 +20,10 @@
|
|||
* primarily little endian: in avr-gcc, multi-byte data types are allocated
|
||||
* with the least significant byte occupying the lowest address. Protocols,
|
||||
* data formats (including UTF-8), and such are primarily big endian. I like
|
||||
* little endianness better -- it feels more mathematically consistent to me
|
||||
* -- but after writing a bit of code, it seems that big endian
|
||||
* serializations are easier to work with, at least in C. For that reason,
|
||||
* this code organizes bytes in a big endian manner whenever it has a choice
|
||||
* between the two.
|
||||
* little endianness better -- it just feels nicer to me -- but after writing
|
||||
* a bit of code, it seems that big endian serializations are easier to work
|
||||
* with, at least in C. For that reason, this code organizes bytes in a big
|
||||
* endian manner whenever it has a choice between the two.
|
||||
*
|
||||
* - For a long time, I was going to try to make this library robust in the
|
||||
* event of power loss, but in the end I decided not to. This feature is
|
||||
|
@ -52,6 +51,10 @@
|
|||
#error "OPT__EEPROM__EEPROM_MACRO__END must not be greater than 1023"
|
||||
#endif
|
||||
|
||||
#if EEMEM_END - EEMEM_START < 300
|
||||
#warn "Only a small space has been allocated for macros"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// macros ---------------------------------------------------------------------
|
||||
|
||||
|
@ -81,20 +84,97 @@
|
|||
* - `EEMEM_MACROS_END`
|
||||
* - `EEMEM_END`: The address of the last byte of our block of EEMEM
|
||||
*
|
||||
*
|
||||
* EEMEM sections:
|
||||
*
|
||||
* - START_ADDRESS:
|
||||
* - byte 0: MSB of `EEMEM_START`
|
||||
* - byte 1: LSB of `EEMEM_START`
|
||||
*
|
||||
* - END_ADDRESS:
|
||||
* - byte 0: MSB of `EEMEM_END`
|
||||
* - byte 1: LSB of `EEMEM_END`
|
||||
*
|
||||
* - VERSION:
|
||||
* - This byte will all be set to `VERSION` as the last step of
|
||||
* initializing our portion of the EEPROM.
|
||||
* - Upon initialization, if this value is not equal to the current
|
||||
* `VERSION`, our portion of the EEPROM should be reinitialized.
|
||||
* - MACROS:
|
||||
* - byte 0..`(EEMEM_END - EEMEM_VERSION_END - 1)`: TODO
|
||||
* - byte 0:
|
||||
* - This byte will all be set to `VERSION` as the last step of
|
||||
* initializing our portion of the EEPROM.
|
||||
* - Upon initialization, if this value is not equal to the current
|
||||
* `VERSION`, our portion of the EEPROM should be reinitialized.
|
||||
*
|
||||
* - MACROS: byte 0..`(EEMEM_END - EEMEM_VERSION_END - 1)`:
|
||||
* - This section will contain a series of zero or more macros, each with
|
||||
* the following format:
|
||||
* - byte 0: `type == TYPE_DELETED`
|
||||
* - byte 1: `length`: the total number of bytes used by this
|
||||
* macro, including the bytes for `type` and `length`
|
||||
* - byte 2...: (optional) undefined
|
||||
* - byte 0: `type == TYPE_VALID_MACRO`
|
||||
* - byte 1: `length`: the total number of bytes used by this
|
||||
* macro, including the bytes for `type` and `length`
|
||||
* - `key-action` 0: the key-action which this macro remaps
|
||||
* - See below for a description of the bytes making up a
|
||||
* key-action
|
||||
* - `key-action` 1...: (optional) the key-actions to which
|
||||
* `key-action` 0 is remapped
|
||||
* - byte 0: `type == TYPE_END`
|
||||
* - byte 1...: (optional) undefined
|
||||
*
|
||||
* - The last key-action in this series will have `type == TYPE_END`.
|
||||
*
|
||||
* - A key-action is a variable length encoding of the information in a
|
||||
* `key_action_t`, with the following format:
|
||||
*
|
||||
* byte 0
|
||||
* .----------------------------------------------.
|
||||
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
* |----------------------------------------------|
|
||||
* | continued | pressed | layer | row | column |
|
||||
* '----------------------------------------------'
|
||||
*
|
||||
* byte 1..3 (optional)
|
||||
* .----------------------------------------------.
|
||||
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
* |----------------------------------------------|
|
||||
* | continued | n/a | layer | row | column |
|
||||
* '----------------------------------------------'
|
||||
*
|
||||
* - `continued`:
|
||||
* - `1`: The next byte is part of this key-action
|
||||
* - `0`: The next byte is not part of this key-action (i.e. this
|
||||
* is the last byte in this key-action)
|
||||
*
|
||||
* - `layer`, `row`, `column`:
|
||||
* - In the first byte of this key-action, these fields contain the
|
||||
* two most significant bits of their respective values such that
|
||||
* these bits are nonzero in *any* of `layer`, `row`, or
|
||||
* `column`. In subsequent bytes of this key-action, these
|
||||
* fields contain the pair of bits to the right of the pair of
|
||||
* bits in the previous key-action byte (the next less
|
||||
* significant pair of bits). If `layer`, `row`, and `column`
|
||||
* all equal `0`, then these three fields will all equal `0`, and
|
||||
* there will only be 1 byte written for this key-action.
|
||||
*
|
||||
* - Example of an encoded key-action (where `*` means "undefined"):
|
||||
*
|
||||
* --- as a key_action_t ---
|
||||
* pressed = true
|
||||
* layer = 0 b 00 00 01 00
|
||||
* row = 0 b 00 01 10 01
|
||||
* column = 0 b 00 10 00 11
|
||||
* | '- least significant pair of bits
|
||||
* '- most significant pair of bits
|
||||
*
|
||||
* --- in EEMEM ---
|
||||
* byte 0 = 0 b 1 1 00 01 10
|
||||
* byte 1 = 0 b 1 * 01 10 00
|
||||
* byte 2 = 0 b 0 * 00 01 11
|
||||
* | | | | '- column bit pair
|
||||
* | | | '- row bit pair
|
||||
* | | '- layer bit pair
|
||||
* | '- pressed / n/a
|
||||
* '- continued
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
* - `START_ADDRESS` and `END_ADDRESS` are written as part of our effort to
|
||||
|
@ -105,14 +185,14 @@
|
|||
* of the EEPROM have the same values for each.
|
||||
*/
|
||||
#define EEMEM_START ((void *)OPT__EEPROM__EEPROM_MACRO__START)
|
||||
#define EEMEM_START_ADDRESS_START EEMEM_START
|
||||
#define EEMEM_START_ADDRESS_START EEMEM_START + 0
|
||||
#define EEMEM_START_ADDRESS_END EEMEM_START_ADDRESS_START + 1
|
||||
#define EEMEM_END_ADDRESS_START EEMEM_START_ADDRESS_END + 1
|
||||
#define EEMEM_END_ADDRESS_END EEMEM_END_ADDRESS_START + 1
|
||||
#define EEMEM_VERSION_START EEMEM_END_ADDRESS_END + 1
|
||||
#define EEMEM_VERSION_END EEMEM_VERSION_START + 0
|
||||
#define EEMEM_MACROS_START EEMEM_VERSION_END + 1
|
||||
#define EEMEM_MACROS_END EEMEM_END
|
||||
#define EEMEM_MACROS_END EEMEM_END - 0
|
||||
#define EEMEM_END ((void *)OPT__EEPROM__EEPROM_MACRO__END)
|
||||
|
||||
/** macros/(group) type/description
|
||||
|
@ -131,7 +211,18 @@
|
|||
// types ----------------------------------------------------------------------
|
||||
|
||||
/** types/key_action_t/description
|
||||
* TODO
|
||||
* To hold everything needed to represent a single key-action (the press or
|
||||
* release of a specific key on a specific layer of the layout matrix).
|
||||
*
|
||||
* Struct members:
|
||||
* - `pressed`: Whether the key is pressed (`true`) or not (`false`)
|
||||
* - `layer`: The layer of the key, in the layout matrix
|
||||
* - `row`: The row of the key, in the layout matrix
|
||||
* - `column`: The column of the key, in the layout matrix
|
||||
*
|
||||
* Notes:
|
||||
* - Since these fields together can reference any key (on any layer)
|
||||
* unambiguously, a `key_action_t` may also serve as a UID for a key.
|
||||
*/
|
||||
typedef struct {
|
||||
bool pressed;
|
||||
|
@ -143,23 +234,29 @@ typedef struct {
|
|||
// ----------------------------------------------------------------------------
|
||||
// local functions ------------------------------------------------------------
|
||||
|
||||
// - if we go back to little endian, need to modify the comments at the top of
|
||||
// the file, as well as the documented order of the bytes for the stored
|
||||
// start and end addresses of the EEPROM
|
||||
//
|
||||
// - sizes (in bytes) (with optimizations on):
|
||||
//
|
||||
// function frame stack
|
||||
// read big 154 3 10
|
||||
// read little 200 2 10
|
||||
// write big 160 0 6
|
||||
// write little 172 0 5
|
||||
|
||||
/**
|
||||
* TODO
|
||||
/** functions/read_key_action/description
|
||||
* Read and return the key-action beginning at `*from` in the EEPROM, and
|
||||
* advance `*from` to one byte past the key-action.
|
||||
*
|
||||
* Arguments:
|
||||
* - `from`: A pointer to a pointer to the location in EEPROM from which to
|
||||
* begin reading
|
||||
*
|
||||
* Returns:
|
||||
* - success: The key-action, as a `key_action_t`
|
||||
*
|
||||
* Notes:
|
||||
* - See the documentation for "(group) EEMEM layout" above for a description
|
||||
* of the layout of key-actions in EEMEM.
|
||||
*/
|
||||
key_action_t read_key_action_big_endian(void * from) {
|
||||
uint8_t byte = eeprom__read(from++);
|
||||
key_action_t read_key_action(void ** from) {
|
||||
uint8_t byte;
|
||||
|
||||
// handle the first byte
|
||||
// - since this byte (and no others) stores the value of `k.pressed`
|
||||
// - also, this allows us to avoid `|=` in favor of `=` for this byte
|
||||
|
||||
byte = eeprom__read((*from)++);
|
||||
|
||||
key_action_t k = {
|
||||
.pressed = byte >> 6 & 0b01,
|
||||
|
@ -168,13 +265,21 @@ key_action_t read_key_action_big_endian(void * from) {
|
|||
.column = byte >> 0 & 0b11,
|
||||
};
|
||||
|
||||
while (byte >> 7) {
|
||||
byte = eeprom__read(from++);
|
||||
// handle all subsequent bytes
|
||||
// - we assume the stream is valid. especially, we do not check to make
|
||||
// sure that the key-action is no more than 4 bytes long.
|
||||
|
||||
while (byte >> 7) {
|
||||
byte = eeprom__read((*from)++);
|
||||
|
||||
// shift up (make more significant) the bits we have so far, to make
|
||||
// room for the bits we just read
|
||||
k.layer <<= 2;
|
||||
k.row <<= 2;
|
||||
k.column <<= 2;
|
||||
|
||||
// logical or the bits we just read into the lowest (least significant)
|
||||
// positions
|
||||
k.layer |= byte >> 4 & 0b11;
|
||||
k.row |= byte >> 2 & 0b11;
|
||||
k.column |= byte >> 0 & 0b11;
|
||||
|
@ -182,46 +287,54 @@ key_action_t read_key_action_big_endian(void * from) {
|
|||
|
||||
return k; // success
|
||||
}
|
||||
key_action_t read_key_action_little_endian(void * from) {
|
||||
uint8_t byte = eeprom__read(from++);
|
||||
|
||||
key_action_t k = {
|
||||
.pressed = ( byte & 0x40 ),
|
||||
.layer = ( byte & 0x30 ) << 2,
|
||||
.row = ( byte & 0x0C ) << 4,
|
||||
.column = ( byte & 0x03 ) << 6,
|
||||
};
|
||||
|
||||
uint8_t i = 0;
|
||||
|
||||
for (; byte>>7; i++) {
|
||||
byte = eeprom__read(from++);
|
||||
|
||||
k.layer >>= 2;
|
||||
k.row >>= 2;
|
||||
k.column >>= 2;
|
||||
|
||||
k.layer |= ( byte & 0x30 ) << 2;
|
||||
k.row |= ( byte & 0x0C ) << 4;
|
||||
k.column |= ( byte & 0x03 ) << 6;
|
||||
}
|
||||
|
||||
for (; i<4; i++) {
|
||||
k.layer >>= 2;
|
||||
k.row >>= 2;
|
||||
k.column >>= 2;
|
||||
}
|
||||
|
||||
return k; // success
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
/** functions/write_key_action/description
|
||||
* Write the given information to a key-action beginning at `*to` in the
|
||||
* EEPROM, and advance `*to` to one byte past the newly written key-action.
|
||||
*
|
||||
* Arguments:
|
||||
* - `to`: A pointer to a pointer to the location in EEPROM at which to begin
|
||||
* writing
|
||||
* - `k`: The key-action to write
|
||||
*
|
||||
* Returns:
|
||||
* - success: `0`
|
||||
* - failure: [other]
|
||||
*
|
||||
* Notes:
|
||||
* - See the documentation for "(group) EEMEM layout" above for a description
|
||||
* of the layout of key-actions in EEMEM.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - We handle the `layer`, `row`, and `column` variables (inside `k`) as being
|
||||
* made up of 4 pairs of bits.
|
||||
* - We deal with these bits beginning with the high (most significant) pair,
|
||||
* and shifting left (towards the most significant end of the byte) to
|
||||
* discard bit pairs we're done with.
|
||||
* - This method seemed faster (i.e. generated less assembly code) when I
|
||||
* was testing than leaving the `layer`, `row`, and `column` bytes as
|
||||
* they were and using a variable mask (as in `k.layer & 0b11 << i*2`).
|
||||
* It's probably worthwhile to note that I was looking at the assembly
|
||||
* (though not closely) and function size with optimizations turned on.
|
||||
*/
|
||||
uint8_t write_key_action_big_endian(void * to, key_action_t k) {
|
||||
if (to > EEMEM_END-3)
|
||||
uint8_t write_key_action(void ** to, key_action_t k) {
|
||||
|
||||
// - we need to leave room after this macro (and therefore after this
|
||||
// key-action) for the `TYPE_END` byte
|
||||
if ((*to) > EEMEM_END-4)
|
||||
return 1; // error: might not be enough space
|
||||
|
||||
// ignore the bits we don't need to write
|
||||
// - if the leading two bits of all three variables are `0b00`, we don't
|
||||
// need to write a key-action byte containing that pair of bits
|
||||
// - the maximum number of pairs of bits we can ignore is 3; the last pair
|
||||
// (the least significant) must be written to the EEPROM regardless of
|
||||
// its value
|
||||
// - we set `i` here (and make it global to the function) because we need
|
||||
// to make sure to *consider writing* exactly 4 pairs of bits. some may
|
||||
// be skipped, some or all may be written, but the total of both must be
|
||||
// 4.
|
||||
|
||||
uint8_t i = 0;
|
||||
|
||||
for (; i<3 && !((k.layer|k.row|k.column) & 0xC0); i++) {
|
||||
|
@ -230,6 +343,11 @@ uint8_t write_key_action_big_endian(void * to, key_action_t k) {
|
|||
k.column <<= 2;
|
||||
}
|
||||
|
||||
// write key-action bytes for all bit pairs that weren't ignored
|
||||
// - the first byte contains the value of `k.pressed`
|
||||
// - all bytes except the last one written (containing the least
|
||||
// significant bits) have their first bit set to `1`
|
||||
|
||||
uint8_t byte = (k.pressed ? 1 : 0) << 6;
|
||||
|
||||
for (; i<4; i++) {
|
||||
|
@ -237,8 +355,9 @@ uint8_t write_key_action_big_endian(void * to, key_action_t k) {
|
|||
| ( k.layer & 0xC0 ) >> 2
|
||||
| ( k.row & 0xC0 ) >> 4
|
||||
| ( k.column & 0xC0 ) >> 6 ;
|
||||
eeprom__write(to++, byte);
|
||||
eeprom__write((*to)++, byte);
|
||||
byte = 0;
|
||||
|
||||
k.layer <<= 2;
|
||||
k.row <<= 2;
|
||||
k.column <<= 2;
|
||||
|
@ -246,36 +365,6 @@ uint8_t write_key_action_big_endian(void * to, key_action_t k) {
|
|||
|
||||
return 0; // success
|
||||
}
|
||||
uint8_t write_key_action_little_endian(void * to, key_action_t k) {
|
||||
if (to > EEMEM_END-3)
|
||||
return 1; // error: might not be enough space
|
||||
|
||||
uint8_t byte = ( k.pressed ? 1 : 0 ) << 6
|
||||
| ( k.layer & 0b11 ) << 4
|
||||
| ( k.row & 0b11 ) << 2
|
||||
| ( k.column & 0b11 ) << 0 ;
|
||||
|
||||
k.layer >>= 2;
|
||||
k.row >>= 2;
|
||||
k.column >>= 2;
|
||||
|
||||
while (k.layer|k.row|k.column) {
|
||||
byte |= 1 << 7;
|
||||
eeprom__write(to++, byte);
|
||||
|
||||
byte = ( k.layer & 0b11 ) << 4
|
||||
| ( k.row & 0b11 ) << 2
|
||||
| ( k.column & 0b11 ) << 0 ;
|
||||
|
||||
k.layer >>= 2;
|
||||
k.row >>= 2;
|
||||
k.column >>= 2;
|
||||
}
|
||||
|
||||
eeprom__write(to, byte);
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
// TODO: rewriting (yet again) - stopped here
|
||||
#if 0
|
||||
|
|
Loading…
Reference in New Issue