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()
{
uint8_t i;
touch_init();
for (i = 0; i < BTN_COUNT; i++) {
btn[i]._mask = BTN_RELEASE;
}
}
void btn_poll()
@ -29,16 +35,16 @@ void btn_poll()
ignore = btn[i]._mask & BTN_IGNORE;
if (touch_read_pushed(i)) {
// is pushed
if (btn[i]._count < BTN_DEBOUNCE) continue;
// hold counter
if (btn[i]._count < 0xffff) btn[i]._count++;
// pushed long enough?
if (btn[i]._count < BTN_DEBOUNCE) continue;
// first push?
if (!btn[i]._mask & BTN_PUSH) {
btn[i]._mask = BTN_PUSH;
if (btn[i].cb_push) {
if (!(btn[i]._mask & BTN_PUSH)) {
btn[i]._mask = BTN_PUSH | ignore;
if (btn[i].cb_push && !ignore) {
btn[i].cb_push(i);
btn[i]._mask |= (BTN_PUSH << 4);
}
@ -46,7 +52,7 @@ void btn_poll()
// held to count limit
// 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;
// call callback only if not in ignore state
@ -56,11 +62,14 @@ void btn_poll()
}
// 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 {
// 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]._count = 0;
// call callback only if not in ignore state

View File

@ -5,6 +5,7 @@
* Author: true
*/
#include <string.h>
#include <ch32v20x.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)
{
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
flash_read(addr, data, len);
}
*/
static void write_page_to_flash(uint8_t page, uint32_t *data, 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
// 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
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 sum = 0;
uint8_t *uc = (uint8_t *)&userconf;
uint8_t *uc = (uint8_t *)conf;
// calculate checksum
for (i = 0; i < sizeof(userconf) - 6; i++) {
@ -90,30 +94,39 @@ static uint16_t checksum()
void userconf_load()
{
uint8_t valid = 0;
uint8_t i;
uint32_t ver_highest = 0;
uint8_t page = CONF_FLASH_PAGES;
uint32_t addr;
struct UserConf *flash;
// read pages backward until we get non-empty flash
while (page) {
read_page_from_flash(--page, (uint32_t *)&userconf, sizeof(userconf)/4);
if (userconf.checkval == CHECKVAL) {
if (userconf.checksum == checksum()) {
// data appears to be valid... use it
active_page = page;
valid = 1;
// read pages and see if we can find our data
while (page--) {
addr = calc_address() + (page * CONF_FLASH_PAGE_SIZE);
flash = (struct UserConf *)addr;
if (flash->checkval == CHECKVAL) {
if (flash->checksum == checksum(flash)) {
if (ver_highest < flash->version) {
ver_highest = flash->version;
active_page = page;
}
}
}
}
if (!valid) {
if (!ver_highest) {
// config is invalid; reset to default
userconf.version = 0;
userconf.checksum = checksum();
userconf.checkval = CHECKVAL;
// default program connfigs
// default program configs
for (i = 0; i < 8; i++) {
userconf.pep_conf[i][0] = 0;
userconf.pep_conf[i][1] = 0;
userconf.pep_conf[i][2] = 0;
userconf.pep_conf[i][3] = 0;
}
// program 0: flashing
userconf.pep_conf[0][0] = 0x80; // brightness
@ -122,19 +135,25 @@ void userconf_load()
// program 2: loops
userconf.pep_conf[2][0] = 0xc0; // brightness
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
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.checksum = checksum(&userconf);
userconf.checkval = CHECKVAL;
} else {
memcpy(&userconf, (uint8_t *)(calc_address() + (active_page * 256)), sizeof(userconf));
}
}
void userconf_save()
{
// we can only save ~4 billion times
userconf.version++;
userconf.checksum = checksum();
userconf.checksum = checksum(&userconf);
userconf.checkval = CHECKVAL;
// determine page to write
@ -145,6 +164,5 @@ void userconf_save()
// nothing is mentioned in the datasheet nor reference manual
// about repeated writes. so to be safe, each page gets
// written to once per erase.
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_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
is31fl3729_init(FL3729_ADDR,

View File

@ -195,17 +195,53 @@ static void pep_2_loops(uint8_t tick)
* segment, upper and lower, turns on
* 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)
{
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
// 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++) {
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++) {
led.section.hat[i] = (tick & 2) ? 0x80 : 24;
led.section.hat[i] = flicker_count[1] & 1 ? 0 : 0x60;
}
led_matrix_is_updated();
@ -257,9 +293,10 @@ static const uint8_t nom_map[5][2] = {
{14, 21},
{12, 23},
{ 9, 24},
{ 5, 27},
{ 2, 29}
{ 7, 27},
{ 3, 28}
};
uint16_t nom_timeout = 0;
static void pep_5_nom(uint8_t tick)
{
@ -269,13 +306,21 @@ static void pep_5_nom(uint8_t tick)
switch (pep_work[0]) {
case 0:
case 2: { // wait a while
if (!pep_work[2]) {
if (!nom_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 {
// wait around for a little while
pep_work[2]--;
if (!pep_work[2]) {
nom_timeout--;
if (!nom_timeout) {
// done here
pep_work[1] = 0;
pep_work[0]++;
@ -286,9 +331,28 @@ static void pep_5_nom(uint8_t tick)
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
if ((tick & 0x3f) != 0) break;
if ((tick & 0x7f) != 0) break;
start = 0;
@ -321,26 +385,7 @@ static void pep_5_nom(uint8_t tick)
pep_work[1]++;
if (pep_work[1] > 6) pep_work[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();
}
if (pep_work[1] > 6) pep_work[0] = 0;
break;
}
@ -413,6 +458,7 @@ void ledprog_pep_init()
}
// per-program initialization
nom_timeout = 0;
// 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
*/
static void rgb_0_rainbow(uint8_t tick)
static void rgb_1_rainbow(uint8_t tick)
{
}
@ -27,7 +34,7 @@ static void rgb_0_rainbow(uint8_t tick)
/*
* 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
*/
static void rgb_2_alternate(uint8_t tick)
static void rgb_3_alternate(uint8_t tick)
{
}
const void (*ledprog_rgb[4])(uint8_t) = {
(const void (*)(uint8_t))rgb_0_rainbow,
(const void (*)(uint8_t))rgb_1_flicker,
(const void (*)(uint8_t))rgb_2_alternate
void (*ledprog_rgb[4])(uint8_t) = {
rgb_0_nothing,
rgb_1_rainbow,
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 "ui.h"
#include "adc.h"
#include "btn.h"
#include "config.h"
#include "led.h"
#include "ledprog_pep.h"
#include "ledprog_rgb.h"
@ -16,11 +19,13 @@
#define MODE_PROGRAM 1
#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_MAX (128*120) // 120 seconds
#define PROG_REPEAT 0x80
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 tick = 0;
static uint16_t save_delay = 0;
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)
{
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)
{
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()
{
btn[0].hold = 330 >> 1;
btn[0].hold = 1200 >> 1;
btn[0].repeat = 0; // (1000 / 20) >> 1;
btn[0].cb_push = ui_btn_push_cb;
btn[0].cb_hold = ui_btn_hold_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].cb_push = ui_btn_push_cb;
btn[1].cb_hold = ui_btn_hold_cb;
btn[1].cb_release = ui_btn_release_cb;
}
uint8_t tmp;
volatile uint8_t prog_run = 5;
void ui_render()
{
@ -78,8 +113,27 @@ void ui_render()
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) {
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;
}
@ -107,7 +161,7 @@ void ui_render()
// temporary: testing
ledprog_pep[prog_run](tick);
// ledprog_pep[prog_run](tick);
/*
if ((tick & 3) == 3) {