fix pepper programs, store / retrieve user config from flash

fix issues with button handler.
pepper programs are now selectable in sequence with the left pepper hat button.
This commit is contained in:
true 2024-08-06 00:06:06 -07:00
parent a6966fc32f
commit 6f9444ab5c
7 changed files with 223 additions and 79 deletions

View File

@ -17,7 +17,13 @@ struct Btn btn[BTN_COUNT] = {0};
void btn_init() void btn_init()
{ {
uint8_t i;
touch_init(); touch_init();
for (i = 0; i < BTN_COUNT; i++) {
btn[i]._mask = BTN_RELEASE;
}
} }
void btn_poll() void btn_poll()
@ -29,16 +35,16 @@ void btn_poll()
ignore = btn[i]._mask & BTN_IGNORE; ignore = btn[i]._mask & BTN_IGNORE;
if (touch_read_pushed(i)) { if (touch_read_pushed(i)) {
// is pushed
if (btn[i]._count < BTN_DEBOUNCE) continue;
// hold counter // hold counter
if (btn[i]._count < 0xffff) btn[i]._count++; if (btn[i]._count < 0xffff) btn[i]._count++;
// pushed long enough?
if (btn[i]._count < BTN_DEBOUNCE) continue;
// first push? // first push?
if (!btn[i]._mask & BTN_PUSH) { if (!(btn[i]._mask & BTN_PUSH)) {
btn[i]._mask = BTN_PUSH; btn[i]._mask = BTN_PUSH | ignore;
if (btn[i].cb_push) { if (btn[i].cb_push && !ignore) {
btn[i].cb_push(i); btn[i].cb_push(i);
btn[i]._mask |= (BTN_PUSH << 4); btn[i]._mask |= (BTN_PUSH << 4);
} }
@ -46,7 +52,7 @@ void btn_poll()
// held to count limit // held to count limit
// if button is not repeatable, do not retrigger // if button is not repeatable, do not retrigger
if ((btn[i]._mask && BTN_HOLD) && !btn[i].repeat) continue; if ((btn[i]._mask & BTN_HOLD) && !btn[i].repeat) continue;
btn[i]._mask |= BTN_HOLD; btn[i]._mask |= BTN_HOLD;
// call callback only if not in ignore state // call callback only if not in ignore state
@ -56,11 +62,14 @@ void btn_poll()
} }
// apply repeat rate to count // apply repeat rate to count
btn[i]._count -= btn[i].repeat; if (btn[i].repeat > btn[i]._count) {
btn[i]._count = 0;
} else btn[i]._count -= btn[i].repeat;
} }
} else { } else {
// is not pushed // is not pushed
if (!btn[i]._mask & BTN_RELEASE) { if (!(btn[i]._mask & BTN_RELEASE)) {
// note: release will remove ignore status
btn[i]._mask = BTN_RELEASE; btn[i]._mask = BTN_RELEASE;
btn[i]._count = 0; btn[i]._count = 0;
// call callback only if not in ignore state // call callback only if not in ignore state

View File

@ -5,6 +5,7 @@
* Author: true * Author: true
*/ */
#include <string.h>
#include <ch32v20x.h> #include <ch32v20x.h>
#include "config.h" #include "config.h"
@ -42,11 +43,13 @@ uint32_t chip_get_flash_size()
} }
} }
static uint32_t * calc_address() static uint32_t calc_address()
{ {
return (uint32_t *)(FLASH_BASE + chip_get_flash_size()) - (CONF_FLASH_PAGE_SIZE * CONF_FLASH_PAGES); uint32_t calc = (FLASH_BASE + chip_get_flash_size()) - (CONF_FLASH_PAGE_SIZE * CONF_FLASH_PAGES);
return calc;
} }
/*
static void read_page_from_flash(uint8_t page, uint32_t *data, uint16_t len) static void read_page_from_flash(uint8_t page, uint32_t *data, uint16_t len)
{ {
uint32_t *addr = calc_address(); uint32_t *addr = calc_address();
@ -60,25 +63,26 @@ static void read_page_from_flash(uint8_t page, uint32_t *data, uint16_t len)
// read the data // read the data
flash_read(addr, data, len); flash_read(addr, data, len);
} }
*/
static void write_page_to_flash(uint8_t page, uint32_t *data, uint16_t len) static void write_page_to_flash(uint8_t page, uint32_t *data, uint16_t len)
{ {
len = (uint16_t)len; len = (uint16_t)len;
uint32_t *addr = calc_address() + (page * CONF_FLASH_PAGE_SIZE); uint32_t addr = calc_address() + (page * CONF_FLASH_PAGE_SIZE);
// write the data // write the data
// note we don't pass any length. we'll just read whatever garbage // note we don't pass any length. we'll just read whatever garbage
// is in RAM after our config and write it to flash. lol // is in RAM after our config and write it to flash. lol
flash_write256(addr, data); flash_write256((uint32_t *)addr, data);
} }
static uint16_t checksum() static uint16_t checksum(struct UserConf *conf)
{ {
uint16_t i; uint16_t i;
uint16_t sum = 0; uint16_t sum = 0;
uint8_t *uc = (uint8_t *)&userconf; uint8_t *uc = (uint8_t *)conf;
// calculate checksum // calculate checksum
for (i = 0; i < sizeof(userconf) - 6; i++) { for (i = 0; i < sizeof(userconf) - 6; i++) {
@ -90,30 +94,39 @@ static uint16_t checksum()
void userconf_load() void userconf_load()
{ {
uint8_t valid = 0; uint8_t i;
uint32_t ver_highest = 0;
uint8_t page = CONF_FLASH_PAGES; uint8_t page = CONF_FLASH_PAGES;
uint32_t addr;
struct UserConf *flash;
// read pages backward until we get non-empty flash // read pages and see if we can find our data
while (page) { while (page--) {
read_page_from_flash(--page, (uint32_t *)&userconf, sizeof(userconf)/4); addr = calc_address() + (page * CONF_FLASH_PAGE_SIZE);
if (userconf.checkval == CHECKVAL) { flash = (struct UserConf *)addr;
if (userconf.checksum == checksum()) { if (flash->checkval == CHECKVAL) {
// data appears to be valid... use it if (flash->checksum == checksum(flash)) {
if (ver_highest < flash->version) {
ver_highest = flash->version;
active_page = page; active_page = page;
valid = 1; }
} }
} }
} }
if (!valid) { if (!ver_highest) {
// config is invalid; reset to default // config is invalid; reset to default
userconf.version = 0; userconf.version = 0;
userconf.checksum = checksum(); // default program configs
userconf.checkval = CHECKVAL; for (i = 0; i < 8; i++) {
userconf.pep_conf[i][0] = 0;
// default program connfigs userconf.pep_conf[i][1] = 0;
userconf.pep_conf[i][2] = 0;
userconf.pep_conf[i][3] = 0;
}
// program 0: flashing // program 0: flashing
userconf.pep_conf[0][0] = 0x80; // brightness userconf.pep_conf[0][0] = 0x80; // brightness
@ -122,19 +135,25 @@ void userconf_load()
// program 2: loops // program 2: loops
userconf.pep_conf[2][0] = 0xc0; // brightness userconf.pep_conf[2][0] = 0xc0; // brightness
userconf.pep_conf[2][1] = 0; // looping rate userconf.pep_conf[2][1] = 0; // looping rate
userconf.pep_conf[2][3] = 3; // decay rate userconf.pep_conf[2][3] = 1; // decay rate
// program 4: alternating // program 4: alternating
userconf.pep_conf[4][0] = 0x80; // brightness userconf.pep_conf[4][0] = 0x80; // brightness
userconf.pep_conf[4][1] = 8; // flash rate userconf.pep_conf[4][1] = 12; // flash rate
userconf.pep_conf[4][3] = 0x0b; // lower brightness on alternate userconf.pep_conf[4][3] = 0x0b; // lower brightness on alternate
userconf.checksum = checksum(&userconf);
userconf.checkval = CHECKVAL;
} else {
memcpy(&userconf, (uint8_t *)(calc_address() + (active_page * 256)), sizeof(userconf));
} }
} }
void userconf_save() void userconf_save()
{ {
// we can only save ~4 billion times
userconf.version++; userconf.version++;
userconf.checksum = checksum(); userconf.checksum = checksum(&userconf);
userconf.checkval = CHECKVAL; userconf.checkval = CHECKVAL;
// determine page to write // determine page to write
@ -145,6 +164,5 @@ void userconf_save()
// nothing is mentioned in the datasheet nor reference manual // nothing is mentioned in the datasheet nor reference manual
// about repeated writes. so to be safe, each page gets // about repeated writes. so to be safe, each page gets
// written to once per erase. // written to once per erase.
write_page_to_flash(active_page, (uint32_t *)&userconf, sizeof(userconf)/4); write_page_to_flash(active_page, (uint32_t *)&userconf, sizeof(userconf)/4);
} }

View File

@ -67,6 +67,15 @@ void led_init()
TIM_TimeBaseInitTypeDef timer ={0}; TIM_TimeBaseInitTypeDef timer ={0};
TIM_OCInitTypeDef pwm = {0}; TIM_OCInitTypeDef pwm = {0};
uint8_t i;
// reset LED values
for (i = 0; i < FL3729_SW_COUNT * FL3729_CS_COUNT; i++) {
led.all[i] = 0;
}
rgb[0] = rgb[1] = rgb[2] = 0;
// configure matrix // configure matrix
is31fl3729_init(FL3729_ADDR, is31fl3729_init(FL3729_ADDR,

View File

@ -195,17 +195,53 @@ static void pep_2_loops(uint8_t tick)
* segment, upper and lower, turns on * segment, upper and lower, turns on
* after both segments are on the separator goes off * after both segments are on the separator goes off
*/ */
static uint8_t flicker_count[2];
static uint8_t flicker_delay[2];
static uint8_t flicker_timer[2];
static void pep_3_neon_sign(uint8_t tick) static void pep_3_neon_sign(uint8_t tick)
{ {
uint8_t i; uint8_t i;
uint16_t rnd;
uint8_t target;
if (tick & 4) {
rnd = prng_get8();
if (rnd >= 0xfc) {
// do a flicker
target = rnd & 1;
flicker_count[target] = prng_scale16(1, 4) << 1;
flicker_delay[target] = prng_scale16(12, 240);
if (rnd & 2) {
flicker_count[target ^ 1] = flicker_count[target];
flicker_delay[target ^ 1] = flicker_delay[target];
}
}
}
// todo: implement flickering on/off // todo: implement flickering on/off
// like a flourescent tube warming up or failing // like a flourescent tube warming up or failing
for (i = 0; i < 2; i++) {
if (flicker_count[i]) {
if (flicker_timer[i]) {
flicker_timer[i]--;
} else {
flicker_count[i]--;
flicker_timer[i] = flicker_delay[i];
}
}
}
for (i = 0; i < LED_PEP_COUNT; i++) { for (i = 0; i < LED_PEP_COUNT; i++) {
led.section.pep[i] = (tick & 2) ? 0x40 : 16; led.section.pep[i] = flicker_count[0] & 1 ? 0 : 0x80;
} }
for (i = 0; i < LED_HAT_COUNT; i++) { for (i = 0; i < LED_HAT_COUNT; i++) {
led.section.hat[i] = (tick & 2) ? 0x80 : 24; led.section.hat[i] = flicker_count[1] & 1 ? 0 : 0x60;
} }
led_matrix_is_updated(); led_matrix_is_updated();
@ -257,9 +293,10 @@ static const uint8_t nom_map[5][2] = {
{14, 21}, {14, 21},
{12, 23}, {12, 23},
{ 9, 24}, { 9, 24},
{ 5, 27}, { 7, 27},
{ 2, 29} { 3, 28}
}; };
uint16_t nom_timeout = 0;
static void pep_5_nom(uint8_t tick) static void pep_5_nom(uint8_t tick)
{ {
@ -269,13 +306,21 @@ static void pep_5_nom(uint8_t tick)
switch (pep_work[0]) { switch (pep_work[0]) {
case 0: case 0:
case 2: { // wait a while case 2: { // wait a while
if (!pep_work[2]) { if (!nom_timeout) {
// just got here; set a new random timeout // just got here; set a new random timeout
pep_work[2] = 0xff - (prng_get8() >> 2); nom_timeout = 0x3ff - prng_get8();
// also set the pepper to be on
for (i = LED_PEP_NOTOP; i < LED_PEP_COUNT; i++) {
led.section.pep[i] = 0x80;
}
for (i = 0; i < LED_HAT_COUNT; i++) {
led.section.hat[i] = 0x60;
}
led_matrix_is_updated();
} else { } else {
// wait around for a little while // wait around for a little while
pep_work[2]--; nom_timeout--;
if (!pep_work[2]) { if (!nom_timeout) {
// done here // done here
pep_work[1] = 0; pep_work[1] = 0;
pep_work[0]++; pep_work[0]++;
@ -286,9 +331,28 @@ static void pep_5_nom(uint8_t tick)
break; break;
} }
case 1: { // eat the pepper case 1: { // regen the pepper
if (led.section.pep[pep_work[1]] >= (0x80 - 4)) {
// next segment
pep_work[1]++;
if (pep_work[1] > (LED_PEP_NOTOP - pep_work[1])) {
// we're done regenerating
pep_work[0]++;
}
} else {
led.section.pep[pep_work[1]] += 4;
if (pep_work[1]) {
led.section.pep[LED_PEP_NOTOP - pep_work[1]] = led.section.pep[pep_work[1]];
}
led_matrix_is_updated();
}
break;
}
case 3: { // eat the pepper
// eat at about one bite per second // eat at about one bite per second
if ((tick & 0x3f) != 0) break; if ((tick & 0x7f) != 0) break;
start = 0; start = 0;
@ -321,26 +385,7 @@ static void pep_5_nom(uint8_t tick)
pep_work[1]++; pep_work[1]++;
if (pep_work[1] > 6) pep_work[0]++; if (pep_work[1] > 6) pep_work[0] = 0;
break;
}
case 3: { // regen the pepper
if (led.section.pep[pep_work[1]] >= (0x80 - 4)) {
// next segment
pep_work[1]++;
if (pep_work[1] > (LED_PEP_NOTOP - pep_work[1])) {
// we're done regenerating
pep_work[0] = 0;
}
} else {
led.section.pep[pep_work[1]] += 4;
if (pep_work[1]) {
led.section.pep[LED_PEP_NOTOP - pep_work[1]] = led.section.pep[pep_work[1]];
}
led_matrix_is_updated();
}
break; break;
} }
@ -413,6 +458,7 @@ void ledprog_pep_init()
} }
// per-program initialization // per-program initialization
nom_timeout = 0;
// there is none on this badge // there is none on this badge
} }

View File

@ -15,11 +15,18 @@ static uint16_t rgb_work[4];
/*
*
*/
static void rgb_0_nothing(uint8_t tick)
{
rgb[0] = rgb[1] = rgb[2] = 0;
}
/* /*
* rainbow puke * rainbow puke
*/ */
static void rgb_1_rainbow(uint8_t tick)
static void rgb_0_rainbow(uint8_t tick)
{ {
} }
@ -27,7 +34,7 @@ static void rgb_0_rainbow(uint8_t tick)
/* /*
* static color with bright flickers * static color with bright flickers
*/ */
static void rgb_1_flicker(uint8_t tick) static void rgb_2_candle(uint8_t tick)
{ {
} }
@ -35,17 +42,18 @@ static void rgb_1_flicker(uint8_t tick)
/* /*
* alternate between two colors * alternate between two colors
*/ */
static void rgb_2_alternate(uint8_t tick) static void rgb_3_alternate(uint8_t tick)
{ {
} }
const void (*ledprog_rgb[4])(uint8_t) = { void (*ledprog_rgb[4])(uint8_t) = {
(const void (*)(uint8_t))rgb_0_rainbow, rgb_0_nothing,
(const void (*)(uint8_t))rgb_1_flicker, rgb_1_rainbow,
(const void (*)(uint8_t))rgb_2_alternate rgb_2_candle,
rgb_3_alternate
}; };

View File

@ -14,7 +14,7 @@
extern void (*ledprog_rgb[8])(uint8_t, uint8_t); extern void (*ledprog_rgb[8])(uint8_t);

View File

@ -4,8 +4,11 @@
#include <stdint.h> #include <stdint.h>
#include "ui.h"
#include "adc.h" #include "adc.h"
#include "btn.h" #include "btn.h"
#include "config.h"
#include "led.h" #include "led.h"
#include "ledprog_pep.h" #include "ledprog_pep.h"
#include "ledprog_rgb.h" #include "ledprog_rgb.h"
@ -16,11 +19,13 @@
#define MODE_PROGRAM 1 #define MODE_PROGRAM 1
#define MODE_PARAMETER 2 #define MODE_PARAMETER 2
#define UI_CONF_SAVE_TIMEOUT 160 #define UI_CONF_SAVE_TIMEOUT 512
#define UI_PROG_RUNTIME_MIN (128*15) // 15 seconds #define UI_PROG_RUNTIME_MIN (128*15) // 15 seconds
#define UI_PROG_RUNTIME_MAX (128*120) // 120 seconds #define UI_PROG_RUNTIME_MAX (128*120) // 120 seconds
#define PROG_REPEAT 0x80
static const uint8_t led_gc_map[] = { static const uint8_t led_gc_map[] = {
@ -35,6 +40,8 @@ static const uint8_t led_gc_map[] = {
static uint8_t mode = MODE_RUN; static uint8_t mode = MODE_RUN;
static uint8_t tick = 0; static uint8_t tick = 0;
static uint16_t save_delay = 0;
void ui_btn_push_cb(uint8_t idx) void ui_btn_push_cb(uint8_t idx)
@ -44,33 +51,61 @@ void ui_btn_push_cb(uint8_t idx)
void ui_btn_hold_cb(uint8_t idx) void ui_btn_hold_cb(uint8_t idx)
{ {
switch (idx) {
case 0: { // left pepper hat
userconf.pep_prog_ena_map ^= PROG_REPEAT;
break;
}
case 1: { // right pepper hat
userconf.rgb_prog_ena_map ^= PROG_REPEAT;
break;
}
}
} }
void ui_btn_release_cb(uint8_t idx) void ui_btn_release_cb(uint8_t idx)
{ {
uint8_t update;
switch (idx) {
case 0: { // left pepper hat
update = userconf.pep_prog_ena_map & ~(PROG_REPEAT);
update++;
if (update > 5) update = 0;
userconf.pep_prog_ena_map = update | (userconf.pep_prog_ena_map & PROG_REPEAT);
ledprog_pep_init();
break;
}
case 1: { // right pepper hat
update = userconf.rgb_prog_ena_map & ~(PROG_REPEAT);
update++;
if (update > 3) update = 0;
userconf.rgb_prog_ena_map = update | (userconf.rgb_prog_ena_map & PROG_REPEAT);
ledprog_rgb_init();
break;
}
}
save_delay = UI_CONF_SAVE_TIMEOUT;
} }
void ui_init() void ui_init()
{ {
btn[0].hold = 330 >> 1; btn[0].hold = 1200 >> 1;
btn[0].repeat = 0; // (1000 / 20) >> 1; btn[0].repeat = 0; // (1000 / 20) >> 1;
btn[0].cb_push = ui_btn_push_cb; btn[0].cb_push = ui_btn_push_cb;
btn[0].cb_hold = ui_btn_hold_cb; btn[0].cb_hold = ui_btn_hold_cb;
btn[0].cb_release = ui_btn_release_cb; btn[0].cb_release = ui_btn_release_cb;
btn[1].hold = 330 >> 1; btn[1].hold = 1200 >> 1;
btn[1].repeat = 0; btn[1].repeat = 0;
btn[1].cb_push = ui_btn_push_cb; btn[1].cb_push = ui_btn_push_cb;
btn[1].cb_hold = ui_btn_hold_cb; btn[1].cb_hold = ui_btn_hold_cb;
btn[1].cb_release = ui_btn_release_cb; btn[1].cb_release = ui_btn_release_cb;
} }
uint8_t tmp;
volatile uint8_t prog_run = 5;
void ui_render() void ui_render()
{ {
@ -78,8 +113,27 @@ void ui_render()
tick++; tick++;
uint8_t prog_pep_idx = userconf.pep_prog_ena_map & ~(PROG_REPEAT);
uint8_t prog_rgb_idx = userconf.rgb_prog_ena_map & ~(PROG_REPEAT);
switch (mode) { switch (mode) {
case MODE_RUN: { case MODE_RUN: {
// run programs
if (ledprog_pep[prog_pep_idx]) {
ledprog_pep[prog_pep_idx](tick);
}
if (ledprog_rgb[prog_rgb_idx]) {
ledprog_rgb[prog_rgb_idx](tick);
}
// check flash save
if (save_delay) {
save_delay--;
if (!save_delay) {
userconf_save();
}
}
break; break;
} }
@ -107,7 +161,7 @@ void ui_render()
// temporary: testing // temporary: testing
ledprog_pep[prog_run](tick); // ledprog_pep[prog_run](tick);
/* /*
if ((tick & 3) == 3) { if ((tick & 3) == 3) {