Compare commits

...

2 Commits

Author SHA1 Message Date
true 279c7d3c68 implement idle mode, re-implement missing accelerometer movement code 2024-10-26 18:35:26 -07:00
true e309c01d47 add basic readme 2024-10-26 18:34:14 -07:00
7 changed files with 174 additions and 74 deletions

27
nametag8_CH592/README.md Normal file
View File

@ -0,0 +1,27 @@
# GAT Nametag SC8 CH592 Firmware
main mcu firmware for the GAT Nametag originally shown at Supercon 8.
## building
This is a MounRiver Studio project. Build in MRS.
## bugs, quirks, omissions
- this micro has VERY SLOW flash. therefore, most user code is executed from RAM. additionally, the flash is protected and not really documented. will take time to reverse engineer it from the provided .a file to possibly determine if wait states can be adjusted.
- i2c master is bitbanged and may still have bugs.
- i2c slave is bitbanged and hasn't been tested at all.
- "idle mode" isn't really a power saving mode. no time to implement. need to do the following to implement this properly:
- implement a low power mode in the sub MCU as well, tell it to go to sleep
- get low power mode implemented and tested with gpio wakeup on this MCU
- put rgbled controller into low power mode
- configure accelerometer for wakeup with interrupt pin output
- get the interrupt chain working from accel -> sub mcu -> main mcu
- properly reinitialize and wake up once interrupt received and data matches expectations
- cpu usage calculation is pretty busted and is only meant to gauge how much free time may exist for doing more processing before the framerate drops.

View File

@ -167,7 +167,18 @@ void oled_update_done()
}
// power saving
// todo: don't hardcode the movement values
if (accel_get_movement() > 3) {
idle_go_sleep = 0;
idle_time_still = 0;
idle_time_menu = 0;
}
// start going to sleep if the option is enabled and if we've been still for enough time
if (uconf.sleep_timeout && (idle_time_still > uconf.sleep_timeout)) {
idle_go_sleep = 1;
}
#ifdef MENU_TIMEOUT_TO_NAMETAG
@ -210,68 +221,104 @@ void lowprio_task() {
// can corrupt transactions
SYS_DisableAllIrq(&interrupt_flags);
// temporary: re-send sub interrupts, sub button holds, enable rgb_hwen
if (flags_lo & FLAG_CH32V_RESEND_CONF) {
flags_lo &= ~FLAG_CH32V_RESEND_CONF;
ch32sub_intr_defaults();
btn_commit_hold();
ch32sub_rgb_hwen(1);
}
// process sub MCU interrupt, if pending
ch32sub_process();
if (flags_lo & FLAG_RGBLED_SEND) {
flags_lo &= ~FLAG_RGBLED_SEND;
rgbled_send();
}
if (flags_lo & FLAG_ACCEL_POLL) {
flags_lo &= ~FLAG_ACCEL_POLL;
accel_poll();
}
// send the last oled frame data
if (flags_lo & FLAG_OLED_UPDATE) {
flags_lo &= ~FLAG_OLED_UPDATE;
/*** oled ***/
if (oled.callback && !(oled.state & SSD1306_STATE_BUSY)) {
if (ssd1306_cb_get()) {
oled.callback();
}
// todo: implement proper sleep mode that saves power
// and wakes up on interrupt, instead of this
if (!idle_go_sleep) {
// temporary: re-send sub interrupts, sub button holds, enable rgb_hwen
if (flags_lo & FLAG_CH32V_RESEND_CONF) {
flags_lo &= ~FLAG_CH32V_RESEND_CONF;
ch32sub_intr_defaults();
btn_commit_hold();
ch32sub_rgb_hwen(1);
}
// update
// only update this frame if we're not in the middle of starting up
if (uptime) {
// process other tasks
if (oled.state & SSD1306_STATE_INITIALIZED) {
oled.callback = oled_update_done;
}
ssd1306_update();
// process sub MCU interrupt, if pending
ch32sub_process();
if (flags_lo & FLAG_RGBLED_SEND) {
flags_lo &= ~FLAG_RGBLED_SEND;
rgbled_send();
}
cpu_use = SysTick->CNT;
// send the last oled frame data
if (flags_lo & FLAG_OLED_UPDATE) {
flags_lo &= ~FLAG_OLED_UPDATE;
if (oled.callback && !(oled.state & SSD1306_STATE_BUSY)) {
if (ssd1306_cb_get()) {
oled.callback();
}
}
// update
// only update this frame if we're not in the middle of starting up
if (uptime) {
// process other tasks
if (oled.state & SSD1306_STATE_INITIALIZED) {
oled.callback = oled_update_done;
}
ssd1306_update();
}
cpu_use = SysTick->CNT;
}
// re-enable IRQs
SYS_RecoverIrq(interrupt_flags);
// render new rgbled frame
// this is not included in cpu calcs :/
if (flags_lo & FLAG_RGBLED_RUN) {
flags_lo &= ~FLAG_RGBLED_RUN;
// make sure a valid program is selected
if (uconf.ledprog_rgb_idx > (sizeof(rgb_pgm) / sizeof(rgb_pgm[0]))) {
uconf.ledprog_rgb_idx = 0;
}
// now run a program
rgbled_runprog(st_tick >> 2);
}
} else {
switch (idle_go_sleep) {
case 1: {
idle_go_sleep++;
// just entered the mode? then turn things off
ssd1306_set_display(0);
uconf.flags |= UCONF_FLAGS_LEDS_DISABLE;
rgbled_send();
break;
}
default: {
// check to see if we should wake
if (accel_get_movement() > 3) {
idle_go_sleep = 0;
idle_time_still = 0;
idle_time_menu = 0;
// wake up things
ssd1306_set_display(1);
uconf.flags &= ~UCONF_FLAGS_LEDS_DISABLE;
rgbled_send();
}
break;
}
}
}
// re-enable IRQs
SYS_RecoverIrq(interrupt_flags);
// render new rgbled frame
// this is not included in cpu calcs :/
if (flags_lo & FLAG_RGBLED_RUN) {
flags_lo &= ~FLAG_RGBLED_RUN;
// make sure a valid program is selected
if (uconf.ledprog_rgb_idx > (sizeof(rgb_pgm) / sizeof(rgb_pgm[0]))) {
uconf.ledprog_rgb_idx = 0;
}
// now run a program
rgbled_runprog(st_tick >> 2);
}
// drop into lower clock rate
SetSysClock(SYSCLK_FREQ_IDLE);
}
@ -342,6 +389,7 @@ void RTC_IRQHandler(void)
}
idle_time_still++;
idle_time_menu++;
}
oled_tick++;

View File

@ -13,6 +13,7 @@
#include "misc/i8atan2.h"
#include <stdio.h>
#include <stdlib.h>
@ -20,8 +21,11 @@
AccelData accel;
uint8_t accel_found = 0;
int16_t movement;
uint16_t movement_worst;
uint8_t movement_idx; // index into "read" register
int16_t movement_read[4]; // last read movement value
int16_t movement; // last calculated movement value
uint16_t movement_worst; // worst seen movement value
// hardware
@ -29,7 +33,6 @@ static stmdev_ctx_t dev_ctx;
int32_t accel_i2c_write(void *handle, uint8_t reg, const uint8_t *bufp, uint16_t len)
{
(void)(handle);
@ -96,6 +99,7 @@ void accel_init()
}
}
__HIGH_CODE
void accel_poll()
{
uint8_t reg = 1;
@ -111,13 +115,30 @@ void accel_poll()
// read acceleration data
memset(xyz, 0x00, 3 * sizeof(int16_t));
lis2dw12_acceleration_raw_get(&dev_ctx, xyz);
accel.x = xyz[0] >>= 8;
accel.y = xyz[1] >>= 8;
accel.z = xyz[2] >>= 8;
// compute our shitty "movement" thing
// awful way to detect being still
reg = sizeof(movement_read) / sizeof(movement_read[0]);
movement_idx++;
if (movement_idx == reg) {
movement_idx = 0;
movement = abs(movement_read[3] - movement_read[0]);
if (movement > movement_worst) {
movement_worst = movement;
}
}
movement_read[movement_idx] = abs(accel.x) + abs(accel.y);
}
}
}
__HIGH_CODE
int8_t accel_get_rotation(struct AccelData *a)
{
int8_t nx, ny, ret;

View File

@ -27,8 +27,8 @@ typedef struct AccelData {
extern AccelData accel;
extern uint8_t accel_found;
extern uint16_t movement_worst;

View File

@ -217,7 +217,7 @@ void menu_6_disp(uint8_t idx)
sprintf(txt, "m%i", abs(accel_get_movement()));
ssd1306fb_set_cursor(104, 10);
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
sprintf(txt, "w%d", 0); // todo: fix this: movement_worst);
sprintf(txt, "w%d", movement_worst);
ssd1306fb_set_cursor(106, 17);
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);

View File

@ -32,34 +32,34 @@ static const uint8_t uconf_edge_defaults[8][8] = {
static void uconf_defaults()
void uconf_defaults()
{
int i;
uconf.ver = UCONF_VER;
uconf.flags = UCONF_FLAGS_LEDS_ENABLE;
uconf.framemod = UCONF_FRAMERATE_FULL;
uconf.nameconf = UCONF_NAME_DISP_DEMOWAVE1 | UCONF_NAME_MODE_AUTOROTATE | UCONF_NAME_MODE_COLOR_INVERT;
uconf.ver = UCONF_VER;
uconf.flags = UCONF_FLAGS_LEDS_ENABLE;
uconf.framemod = UCONF_FRAMERATE_FULL;
uconf.nameconf = UCONF_NAME_DISP_DEMOWAVE1 | UCONF_NAME_MODE_AUTOROTATE | UCONF_NAME_MODE_COLOR_INVERT;
// UCONF_NAME_DISP_DEMOWAVES1 | UCONF_NAME_MODE_AUTOROTATE | UCONF_NAME_MODE_COLOR_INVERT;
if (uconf_flash_offset == 0xf0) {
uconf.iter = 0;
uconf.iter = 0;
}
uconf.font_idx = 4;
uconf.char_spacing = 2;
uconf.y_offset = 0;
uconf.font_idx = 4;
uconf.char_spacing = 2;
uconf.y_offset = 0;
// todo: add LUT
strcpy(uconf.name, "Supercon");
uconf.favcolor_hue = 170;
uconf.favcolor_sat = 240;
uconf.favcolor_val = 32;
uconf.favcolor_hue = 170;
uconf.favcolor_sat = 240;
uconf.favcolor_val = 32;
uconf.altcolor_hue = 0;
uconf.altcolor_sat = 240;
uconf.altcolor_val = 32;
uconf.altcolor_hue = 0;
uconf.altcolor_sat = 240;
uconf.altcolor_val = 32;
uconf.ledprog_rgb_idx = 4;
uconf.ledprog_rgb_idx = 4;
for (i = 0; i < 8; i++) {
uconf.ledprog_rgb[i] = 0;
@ -67,10 +67,11 @@ static void uconf_defaults()
memcpy(uconf.ledprog_rgb_data, uconf_edge_defaults, sizeof(uconf_edge_defaults));
uconf.lsens_dark_thresh = 0x6a0; // todo: figure out what this should be by testing
uconf.sleep_timeout = 20 * 60;
uconf.lsens_dark_thresh = 0x6a0; // todo: figure out what this should be by testing
uconf.menu_timeout = 5 * 60;
uconf.sleep_timeout = 20 * 60;
uconf.checksum = checksum_gen((uint8_t *)&uconf, sizeof(uconf) - 2);
uconf.checksum = checksum_gen((uint8_t *)&uconf, sizeof(uconf) - 2);
}
static int8_t uconf_validate()

View File

@ -83,8 +83,9 @@ typedef struct UserConf {
uint8_t padding0; // 52
uint8_t ledprog_rgb[16]; // 68
uint8_t ledprog_rgb_data[16][8]; // 196
uint8_t padding1[52]; // 248
uint16_t lsens_dark_thresh; // 250
uint8_t padding1[50]; // 246
uint16_t lsens_dark_thresh; // 248
uint16_t menu_timeout; // 250
uint16_t sleep_timeout; // 252
uint16_t tempcx10_offset; // 253-254
uint16_t checksum; // 255-256
@ -106,6 +107,8 @@ extern uint8_t temp_degc_decimal;
void uconf_load();
void uconf_write();
void uconf_defaults();
#endif /* USER_CONFIG_H_ */