From ea01a60d6cfc0aea98e43a38979ca8b93b7f75fe Mon Sep 17 00:00:00 2001 From: whowe Date: Sun, 9 Apr 2023 14:21:11 +0800 Subject: [PATCH] Dynamic LED setup working --- Production/.DS_Store | Bin 0 -> 6148 bytes Production/Firmware/iidx_pico.uf2 | Bin 80384 -> 95232 bytes firmware/src/CMakeLists.txt | 4 +- firmware/src/config.c | 155 ++++----------------- firmware/src/config.h | 38 ++++-- firmware/src/main.c | 19 +-- firmware/src/rgb.c | 91 +++++++++---- firmware/src/rgb.h | 11 +- firmware/src/save.c | 148 ++++++++++++++++++++ firmware/src/save.h | 21 +++ firmware/src/setup.c | 219 ++++++++++++++++-------------- firmware/src/setup.h | 28 ---- firmware/src/tt_blade.c | 17 +-- firmware/src/tt_rainbow.c | 7 +- firmware/src/turntable.c | 16 +-- firmware/src/turntable.h | 1 - 16 files changed, 442 insertions(+), 333 deletions(-) create mode 100644 Production/.DS_Store create mode 100644 firmware/src/save.c create mode 100644 firmware/src/save.h diff --git a/Production/.DS_Store b/Production/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e11f8a516d9d8570265227cb3655e558da120b5b GIT binary patch literal 6148 zcmeHK!D`z;5S_K%WYtjcp((ip1igmVbxNV+qR4I`w^EhlkQP_6O)IKsgDh7BW6-gs zAJbbtCBKur*Rp;m^*sAtHK+%n-FFl%ak| zJ(@1&{F3wg21n9c)E0C^6~(}@c}EuYV)JkHNi{i6GF^X~9B1jI>~zjVquF|}vMQxq zlb@oms*1|EoKC{HION_3rLtrZ$H{&=t_JPLJ(ZVnnvV-jNJnFod^||=k*Y#9%|{c> zjm&`bJa5o`GMja_{BE~1-|}a#yNF+IznafIx&HL|&ilc)>?l`1SQ>l=_ztu^vbcbs z2$od%9u;|}@-G-;UK4ZBqwt0{zc6xDe{`KW1)Ks-fjcT-*DASo$Llz{Q@|;3{}kZ$ z!G|*j7Hflg=|H280KgW~+7RLb|F4r=&ne&(xK|2@RzK>8cqF^G yE;hgO?NpdMidPZ^avMZOHo^00WD)L1bX=M?lNq8mGWNRp0`W%YIb= literal 0 HcmV?d00001 diff --git a/Production/Firmware/iidx_pico.uf2 b/Production/Firmware/iidx_pico.uf2 index ecdbbd5c4229c110b5744f889ac307979981e1d3..ced4f0f9411f03ec135eb45b9c1dfd32db213703 100644 GIT binary patch literal 95232 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 JS1Ca8|35WkV=w>! diff --git a/firmware/src/CMakeLists.txt b/firmware/src/CMakeLists.txt index b2852ab..3e19a87 100644 --- a/firmware/src/CMakeLists.txt +++ b/firmware/src/CMakeLists.txt @@ -1,7 +1,7 @@ function(make_firmware board board_def) add_executable(${board} - main.c buttons.c rgb.c config.c setup.c turntable.c - tt_rainbow.c tt_blade.c + main.c buttons.c rgb.c save.c config.c setup.c + turntable.c tt_rainbow.c tt_blade.c usb_descriptors.c) target_compile_definitions(${board} PUBLIC ${board_def}) pico_enable_stdio_usb(${board} 1) diff --git a/firmware/src/config.c b/firmware/src/config.c index 01200a1..e9d5e29 100644 --- a/firmware/src/config.c +++ b/firmware/src/config.c @@ -1,145 +1,48 @@ /* - * Controller Config Save and Load + * Controller Config Data * WHowe * - * Config is stored in last sector of flash + * Config is a global data structure that stores all the configuration */ #include "config.h" +#include "save.h" -#include -#include -#include -#include +iidx_cfg_t *iidx_cfg; - -#include "bsp/board.h" -#include "pico/bootrom.h" -#include "pico/stdio.h" - -#include "hardware/flash.h" -#include "hardware/sync.h" - -static struct { - size_t size; - size_t offset; - void (*after_load)(); -} modules[8] = {0}; -static int module_num = 0; - -#define CONFIG_PAGE_MAGIC 0x13424321 -#define CONFIG_SECTOR_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE) - -typedef struct __attribute ((packed)) { - uint32_t magic; - uint8_t data[FLASH_PAGE_SIZE - 4]; -} page_t; - -static page_t old_cfg = {0}; -static page_t new_cfg = {0}; -static page_t default_cfg = {0}; -static int cfg_page = -1; - -static bool requesting_save = false; -static uint64_t requesting_time = 0; - -static io_locker_func io_lock; - -static void config_save() -{ - old_cfg = new_cfg; - - cfg_page = (cfg_page + 1) % (FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE); - printf("Program Flash %d %8lx\n", cfg_page, old_cfg.magic); - io_lock(true); - uint32_t ints = save_and_disable_interrupts(); - if (cfg_page == 0) { - flash_range_erase(CONFIG_SECTOR_OFFSET, FLASH_SECTOR_SIZE); +static iidx_cfg_t default_cfg = { + .key_off = { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}, + .key_on = { {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, + {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, + }, + .tt_led = { + .start = 0, + .num = 24, + .effect = 0, + .param = 0, + .brightness = 5, + .reversed = false, + }, + .tt_sensor_reversed = false, + .effects = { + .play_vol = 255, + .filter = 128, + .eq_low = 128, + .eq_hi = 128, } - flash_range_program(CONFIG_SECTOR_OFFSET + cfg_page * FLASH_PAGE_SIZE, - (uint8_t *)&old_cfg, FLASH_PAGE_SIZE); - restore_interrupts(ints); - io_lock(false); -} - -static void load_default() -{ - printf("Load Default\n"); - new_cfg = default_cfg; - new_cfg.magic = CONFIG_PAGE_MAGIC; -} - -static const page_t *get_page(int id) -{ - int addr = XIP_BASE + CONFIG_SECTOR_OFFSET; - return (page_t *)(addr + FLASH_PAGE_SIZE * id); -} - -static void config_load() -{ - for (int i = 0; i < FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE; i++) { - if (get_page(i)->magic != CONFIG_PAGE_MAGIC) { - break; - } - cfg_page = i; - } - - if (cfg_page < 0) { - load_default(); - config_request_save(); - return; - } - - old_cfg = *get_page(cfg_page); - new_cfg = old_cfg; - printf("Page Loaded %d %8lx\n", cfg_page, new_cfg.magic); -} +}; static void config_loaded() { - for (int i = 0; i < module_num; i++) { - modules[i].after_load(); - } + /* configuration validation */ } -void config_init(io_locker_func locker) +void config_changed() { - io_lock = locker; - config_load(); - config_loop(); - config_loaded(); + save_request(); } -void config_loop() +void config_init() { - if ((requesting_save) && (time_us_64() - requesting_time > 1000000)) { - requesting_save = false; - /* only when data is actually changed */ - for (int i = 0; i < sizeof(old_cfg); i++) { - if (((uint8_t *)&old_cfg)[i] != ((uint8_t *)&new_cfg)[i]) { - config_save(); - return; - } - } - } -} - -void *config_alloc(size_t size, void *def, void (*after_load)()) -{ - modules[module_num].size = size; - size_t offset = module_num > 0 ? modules[module_num - 1].offset + size : 0; - modules[module_num].offset = offset; - modules[module_num].after_load = after_load; - module_num++; - memcpy(default_cfg.data + offset, def, size); // backup the default - return new_cfg.data + offset; -} - -void config_request_save() -{ - requesting_time = time_us_64(); - if (!requesting_save) { - requesting_save = true; - new_cfg.magic = CONFIG_PAGE_MAGIC; - } + iidx_cfg = (iidx_cfg_t *)save_alloc(sizeof(iidx_cfg), &default_cfg, config_loaded); } diff --git a/firmware/src/config.h b/firmware/src/config.h index 0bd3600..3617fa6 100644 --- a/firmware/src/config.h +++ b/firmware/src/config.h @@ -1,21 +1,43 @@ /* - * Controller Config Save and Load + * Controller Config * WHowe */ #ifndef CONFIG_H #define CONFIG_H -#include +#include #include -/* It's safer to lock other I/O ops during saving, so we need a locker */ -typedef void (*io_locker_func)(bool pause); -void config_init(io_locker_func locker); +typedef struct __attribute ((packed)) { + uint8_t h; // hue; + uint8_t s; // saturation; + uint8_t v; // value; +} hsv_t; -void config_loop(); +typedef struct __attribute ((packed)) { + hsv_t key_off[11]; + hsv_t key_on[11]; + struct { + uint8_t start; + uint8_t num; + uint8_t effect; + uint8_t param; + uint8_t brightness; + bool reversed; + } tt_led; + bool tt_sensor_reversed; + struct { + uint8_t play_vol; + uint8_t filter; + uint8_t eq_low; + uint8_t eq_hi; + } effects; +} iidx_cfg_t; -void *config_alloc(size_t size, void *def, void (*after_load)()); -void config_request_save(); +extern iidx_cfg_t *iidx_cfg; + +void config_init(); +void config_changed(); // Notify the config has changed #endif diff --git a/firmware/src/main.c b/firmware/src/main.c index 1edd400..46a1edc 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -24,6 +24,7 @@ #include "tt_rainbow.h" #include "config.h" +#include "save.h" /* Measure the time of a function call */ #define RUN_TIME(func) \ @@ -38,10 +39,10 @@ struct { void report_usb_hid() { if (tud_hid_ready()) { - hid_report.joy[2] = iidx_cfg.effects.play_vol; - hid_report.joy[3] = iidx_cfg.effects.filter; - hid_report.joy[4] = iidx_cfg.effects.eq_low; - hid_report.joy[5] = iidx_cfg.effects.eq_hi; + hid_report.joy[2] = iidx_cfg->effects.play_vol; + hid_report.joy[3] = iidx_cfg->effects.filter; + hid_report.joy[4] = iidx_cfg->effects.eq_low; + hid_report.joy[5] = iidx_cfg->effects.eq_hi; tud_hid_n_report(0x00, REPORT_ID_JOYSTICK, &hid_report, sizeof(hid_report)); } } @@ -73,8 +74,10 @@ static void core1_loop() while (true) { uint32_t angle = turntable_read(); rgb_set_angle(angle); + hid_report.joy[0] = angle >> 4; // 12bit to 8bit hid_report.joy[1] = 255 - hid_report.joy[0]; + RUN_EVERY_N_MS(rgb_update(), 2); turntable_update(); frame++; @@ -92,18 +95,15 @@ static void core0_loop() uint16_t buttons = button_read(); uint16_t angle = turntable_read() >> 4; if (setup_run(buttons, angle)) { - turntable_set_hardware(iidx_cfg.tt_sensor_reversed); - rgb_set_hardware(iidx_cfg.tt_led.start, iidx_cfg.tt_led.num, iidx_cfg.tt_led.reversed); rgb_force_display(setup_led_button, setup_led_tt); report_usb_hid(); - sleep_ms(5); continue; } hid_report.buttons = buttons; report_usb_hid(); rgb_set_button_light(buttons); - config_loop(); + save_loop(); } } @@ -122,7 +122,8 @@ void init() stdio_init_all(); setup_init(); - config_init(pause_core1); + config_init(); + save_init(pause_core1); } int main(void) diff --git a/firmware/src/rgb.c b/firmware/src/rgb.c index 35a4aec..fb8eb02 100644 --- a/firmware/src/rgb.c +++ b/firmware/src/rgb.c @@ -18,6 +18,7 @@ #include "ws2812.pio.h" #include "board_defs.h" +#include "config.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) @@ -86,13 +87,8 @@ void rgb_set_level(uint8_t level) } uint8_t button_lights[BUTTON_RGB_NUM]; - -static uint32_t tt_led_buf[128] = {0}; -uint32_t *tt_ring_buf = &tt_led_buf[0]; -uint32_t tt_ring_start = 0; -uint32_t tt_ring_size = 24; -bool tt_ring_reversed = false; -uint32_t tt_ring_angle = 0; +uint32_t tt_led_buf[128] = {0}; +uint32_t tt_led_angle = 0; static uint32_t button_led_buf[BUTTON_RGB_NUM] = {0}; @@ -109,9 +105,46 @@ void drive_led() for (int i = 0; i < ARRAY_SIZE(button_led_buf); i++) { pio_sm_put_blocking(pio0, 0, button_led_buf[i] << 8u); } + for (int i = 0; i < iidx_cfg->tt_led.start; i++) { + pio_sm_put_blocking(pio1, 0, 0); + } + for (int i = 0; i < TT_LED_NUM; i++) { + uint8_t id = iidx_cfg->tt_led.reversed ? TT_LED_NUM - i - 1 : i; + pio_sm_put_blocking(pio1, 0, tt_led_buf[id] << 8u); + } + for (int i = 0; i < 8; i++) { // a few more to wipe out the last led + pio_sm_put_blocking(pio1, 0, 0); + } +} - for (int i = 0; i < ARRAY_SIZE(tt_led_buf); i++) { - pio_sm_put_blocking(pio1, 0, tt_led_buf[i] << 8u); +uint32_t rgb32_from_hsv(hsv_t hsv) +{ + uint8_t region, remainder, p, q, t; + + if (hsv.s == 0) { + return hsv.v << 16 | hsv.v << 8 | hsv.v; + } + + region = hsv.h / 43; + remainder = (hsv.h - (region * 43)) * 6; + + p = (hsv.v * (255 - hsv.s)) >> 8; + q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8; + t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8; + + switch (region) { + case 0: + return hsv.v << 16 | t << 8 | p; + case 1: + return q << 16 | hsv.v << 8 | p; + case 2: + return p << 16 | hsv.v << 8 | t; + case 3: + return p << 16 | q << 8 | hsv.v; + case 4: + return t << 16 | p << 8 | hsv.v; + default: + return hsv.v << 16 | p << 8 | q; } } @@ -123,16 +156,16 @@ static void button_lights_update() for (int i = 0; i < BUTTON_RGB_NUM; i++) { int led = button_rgb_map[i]; if (button_lights[i] > 0) { - button_led_buf[led] = button_rgb32(0x80, 0x80, 0x80, true); + button_led_buf[led] = rgb32_from_hsv(iidx_cfg->key_on[i]); } else { - button_led_buf[led] = 0; + button_led_buf[led] = rgb32_from_hsv(iidx_cfg->key_off[i]); } } } void rgb_set_angle(uint32_t angle) { - tt_ring_angle = angle; + tt_led_angle = angle; effects[current_effect].set_angle(angle); } @@ -160,26 +193,34 @@ static void effect_update() #define FORCE_EXPIRE_DURATION 100000ULL static uint64_t force_expire_time = 0; +uint32_t *force_buttons = NULL; +uint32_t *force_tt = NULL; + +void force_update() +{ + for (int i = 0; i < BUTTON_RGB_NUM; i++) { + int led = button_rgb_map[i]; + button_led_buf[led] = force_buttons[i]; + } + + memcpy(tt_led_buf, force_tt, TT_LED_NUM * sizeof(uint32_t)); +} void rgb_update() { if (time_us_64() > force_expire_time) { effect_update(); button_lights_update(); + } else { + force_update(); } drive_led(); } -void rgb_force_display(uint32_t *keyboard, uint32_t *tt) +void rgb_force_display(uint32_t *buttons, uint32_t *tt) { - for (int i = 0; i < BUTTON_RGB_NUM; i++) { - int led = button_rgb_map[i]; - button_led_buf[led] = keyboard[i]; - } - - memset(tt_led_buf, 0, tt_ring_start * sizeof(uint32_t)); - memcpy(tt_led_buf + tt_ring_start, tt, tt_ring_size * sizeof(uint32_t)); - + force_buttons = buttons; + force_tt = tt; force_expire_time = time_us_64() + FORCE_EXPIRE_DURATION; } @@ -199,11 +240,3 @@ void rgb_reg_tt_effect(tt_effect_t effect) effects[effect_num] = effect; effect_num++; } - -void rgb_set_hardware(uint16_t tt_start, uint16_t tt_num, bool tt_reversed) -{ - tt_ring_start = tt_start; - tt_ring_size = tt_num; - tt_ring_reversed = tt_reversed; - tt_ring_buf = &tt_led_buf[tt_start]; -} diff --git a/firmware/src/rgb.h b/firmware/src/rgb.h index bd3908f..290164b 100644 --- a/firmware/src/rgb.h +++ b/firmware/src/rgb.h @@ -9,6 +9,8 @@ #include #include +#include "config.h" + void rgb_init(); void rgb_set_hardware(uint16_t tt_start, uint16_t tt_num, bool tt_reversed); @@ -33,11 +35,12 @@ typedef struct { void rgb_reg_tt_effect(tt_effect_t effect); +extern uint32_t tt_led_buf[]; +#define TT_LED_NUM (iidx_cfg->tt_led.num) + /* These global variables meant to be accessed by effect codes */ -extern uint32_t *tt_ring_buf; -extern uint32_t tt_ring_size; -extern uint32_t tt_ring_angle; -extern bool tt_ring_reversed; +extern uint32_t tt_led_angle; + uint32_t button_rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix); uint32_t tt_rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix); diff --git a/firmware/src/save.c b/firmware/src/save.c new file mode 100644 index 0000000..b2008a9 --- /dev/null +++ b/firmware/src/save.c @@ -0,0 +1,148 @@ +/* + * Controller Save Save and Load + * WHowe + * + * Save is stored in last sector of flash + */ + +#include "save.h" + +#include +#include +#include +#include + + +#include "bsp/board.h" +#include "pico/bootrom.h" +#include "pico/stdio.h" + +#include "hardware/flash.h" +#include "hardware/sync.h" + +static struct { + size_t size; + size_t offset; + void (*after_load)(); +} modules[8] = {0}; +static int module_num = 0; + +#define SAVE_PAGE_MAGIC 0x13424321 +#define SAVE_SECTOR_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE) + +typedef struct __attribute ((packed)) { + uint32_t magic; + uint8_t data[FLASH_PAGE_SIZE - 4]; +} page_t; + +static page_t old_data = {0}; +static page_t new_data = {0}; +static page_t default_data = {0}; +static int data_page = -1; + +static bool requesting_save = false; +static uint64_t requesting_time = 0; + +static io_locker_func io_lock; + +static void save_program() +{ + old_data = new_data; + + data_page = (data_page + 1) % (FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE); + printf("Program Flash %d %8lx\n", data_page, old_data.magic); + io_lock(true); + uint32_t ints = save_and_disable_interrupts(); + if (data_page == 0) { + flash_range_erase(SAVE_SECTOR_OFFSET, FLASH_SECTOR_SIZE); + } + flash_range_program(SAVE_SECTOR_OFFSET + data_page * FLASH_PAGE_SIZE, + (uint8_t *)&old_data, FLASH_PAGE_SIZE); + restore_interrupts(ints); + io_lock(false); +} + +static void load_default() +{ + printf("Load Default\n"); + new_data = default_data; + new_data.magic = SAVE_PAGE_MAGIC; +} + +static const page_t *get_page(int id) +{ + int addr = XIP_BASE + SAVE_SECTOR_OFFSET; + return (page_t *)(addr + FLASH_PAGE_SIZE * id); +} + +static void save_load() +{ + for (int i = 0; i < FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE; i++) { + if (get_page(i)->magic != SAVE_PAGE_MAGIC) { + break; + } + data_page = i; + } + + if (data_page < 0) { + load_default(); + save_request(); + return; + } + + old_data = *get_page(data_page); + new_data = old_data; + printf("Page Loaded %d %8lx\n", data_page, new_data.magic); +} + +static void save_loaded() +{ + for (int i = 0; i < module_num; i++) { + modules[i].after_load(); + } +} + +void save_init(io_locker_func locker) +{ + io_lock = locker; + save_load(); + save_loop(); + save_loaded(); +} + +void save_loop() +{ + if (requesting_save && (time_us_64() - requesting_time > 1000000)) { + requesting_save = false; + printf("Time to save.\n"); + /* only when data is actually changed */ + for (int i = 0; i < sizeof(old_data); i++) { + if (((uint8_t *)&old_data)[i] != ((uint8_t *)&new_data)[i]) { + save_program(); + return; + } + } + printf("No change.\n"); + } +} + +void *save_alloc(size_t size, void *def, void (*after_load)()) +{ + modules[module_num].size = size; + size_t offset = module_num > 0 ? modules[module_num - 1].offset + size : 0; + modules[module_num].offset = offset; + modules[module_num].after_load = after_load; + module_num++; + memcpy(default_data.data + offset, def, size); // backup the default + return new_data.data + offset; +} + +void save_request() +{ + requesting_time = time_us_64(); + if (!requesting_save) { + printf("Save marked.\n"); + requesting_save = true; + new_data.magic = SAVE_PAGE_MAGIC; + } +} diff --git a/firmware/src/save.h b/firmware/src/save.h new file mode 100644 index 0000000..c6ee9d6 --- /dev/null +++ b/firmware/src/save.h @@ -0,0 +1,21 @@ +/* + * Controller Flash Save and Load + * WHowe + */ + +#ifndef SAVE_H +#define SAVE_H + +#include +#include + +/* It's safer to lock other I/O ops during saving, so we need a locker */ +typedef void (*io_locker_func)(bool pause); +void save_init(io_locker_func locker); + +void save_loop(); + +void *save_alloc(size_t size, void *def, void (*after_load)()); +void save_request(); + +#endif diff --git a/firmware/src/setup.c b/firmware/src/setup.c index 7112e77..b6dda91 100644 --- a/firmware/src/setup.c +++ b/firmware/src/setup.c @@ -12,26 +12,22 @@ #include #include "bsp/board.h" +#include "pico/bootrom.h" #include "rgb.h" #include "config.h" -iidx_cfg_t iidx_cfg; static iidx_cfg_t cfg_save; +static uint64_t setup_tick_ms = 0; +#define CONCAT(a, b) a ## b +#define TVAR(line) CONCAT(a, line) +#define RUN_EVERY_N_MS(a, ms) { static uint64_t TVAR(__LINE__) = 0; \ + if (setup_tick_ms - TVAR(__LINE__) >= ms) { a; TVAR(__LINE__) = setup_tick_ms; } } + uint32_t setup_led_tt[128]; uint32_t setup_led_button[BUTTON_RGB_NUM]; -static void cfg_loaded() -{ - /* configuration validation */ -} - -void setup_init() -{ - config_alloc(sizeof(iidx_cfg), &iidx_cfg, cfg_loaded); -} - typedef enum { MODE_NONE, MODE_TURNTABLE, @@ -43,25 +39,26 @@ static setup_mode_t current_mode = MODE_NONE; static struct { uint16_t last_keys; - uint16_t last_angle; uint16_t keys; - int16_t angle; uint16_t just_pressed; uint16_t just_released; + + int16_t last_angle; + int16_t angle; int16_t rotate; } input = { 0 }; -#define KEY_1 0x0001 -#define KEY_2 0x0002 -#define KEY_3 0x0004 -#define KEY_4 0x0008 -#define KEY_5 0x0010 -#define KEY_6 0x0020 -#define KEY_7 0x0040 -#define START 0x0080 -#define EFFECT 0x0100 -#define VEFX 0x0200 -#define E4 0x0400 +#define KEY_1 0x0001 +#define KEY_2 0x0002 +#define KEY_3 0x0004 +#define KEY_4 0x0008 +#define KEY_5 0x0010 +#define KEY_6 0x0020 +#define KEY_7 0x0040 +#define E_START 0x0080 +#define E_EFFECT 0x0100 +#define E_VEFX 0x0200 +#define E_4 0x0400 #define AUX_NO 0x0800 #define AUX_YES 0x1000 @@ -72,11 +69,10 @@ static struct { #define LED_KEY_5 4 #define LED_KEY_6 5 #define LED_KEY_7 6 -#define LED_START 7 -#define LED_EFFECT 8 -#define LED_VEFX 9 -#define LED_E4 10 - +#define LED_E_START 7 +#define LED_E_EFFECT 8 +#define LED_E_VEFX 9 +#define LED_E_4 10 #define PRESSED_ALL(k) ((input.keys & (k)) == (k)) #define PRESSED_ANY(k) (input.keys & (k)) @@ -105,6 +101,10 @@ static void mode_none_loop() static bool escaped = false; static uint64_t escape_time = 0; + if (PRESSED_ALL(AUX_NO | AUX_YES | E_START)) { + reset_usb_boot(0, 2); // usb boot to flash + } + if (PRESSED_ALL(AUX_YES | AUX_NO)) { if (!escaped) { escaped = true; @@ -118,7 +118,7 @@ static void mode_none_loop() return; } - uint16_t pressed = PRESSED_ANY(START | EFFECT | VEFX | E4); + uint16_t pressed = PRESSED_ANY(E_START | E_EFFECT | E_VEFX | E_4); if (pressed) { escaped = false; join_mode(MODE_ANALOG); @@ -133,7 +133,7 @@ static void mode_none_loop() static struct { bool adjust_led_start; - uint16_t start_angle; + int16_t start_angle; uint8_t counter; } tt_ctx; @@ -142,95 +142,100 @@ void mode_tt_enter() tt_ctx.start_angle = input.angle; } -void mode_tt_op() +static void mode_tt_key_change() { - if (JUST_PRESSED(START)) { + if (JUST_PRESSED(E_START)) { tt_ctx.adjust_led_start = true; tt_ctx.start_angle = input.angle; - } else if (JUST_PRESSED(EFFECT)) { + } else if (JUST_PRESSED(E_EFFECT)) { tt_ctx.adjust_led_start = false; tt_ctx.start_angle = input.angle; - } else if (JUST_PRESSED(VEFX)) { - iidx_cfg.tt_led.reversed = !iidx_cfg.tt_led.reversed; - tt_ctx.counter = iidx_cfg.tt_led.num; - } else if (JUST_PRESSED(E4)) { - iidx_cfg.tt_sensor_reversed = !iidx_cfg.tt_sensor_reversed; - tt_ctx.counter = iidx_cfg.tt_led.num; + } else if (JUST_PRESSED(E_VEFX)) { + iidx_cfg->tt_led.reversed = !iidx_cfg->tt_led.reversed; + } else if (JUST_PRESSED(E_4)) { + iidx_cfg->tt_sensor_reversed = !iidx_cfg->tt_sensor_reversed; } - int16_t delta = input.angle - tt_ctx.start_angle; - if (abs(delta) < 4) { - return; - } - - tt_ctx.start_angle = input.angle; - - if (tt_ctx.adjust_led_start) { - if ((delta > 0) && (iidx_cfg.tt_led.start < 8)) { - iidx_cfg.tt_led.start++; - } else if ((delta < -8) && (iidx_cfg.tt_led.start > 0)) { - iidx_cfg.tt_led.start--; - } - } else { - if ((delta < 0) && (iidx_cfg.tt_led.num < 128)) { - iidx_cfg.tt_led.num++; - } else if ((delta < -8) && (iidx_cfg.tt_led.num > 0)) { - iidx_cfg.tt_led.num--; - } - } - - if (iidx_cfg.tt_led.start + iidx_cfg.tt_led.num > 128) { - iidx_cfg.tt_led.num = 128 - iidx_cfg.tt_led.start; - } check_exit(); } +static void mode_tt_rotate() +{ + int16_t delta = input.angle - tt_ctx.start_angle; + if (abs(delta) > 8) { + tt_ctx.start_angle = input.angle; + + #define LED_START iidx_cfg->tt_led.start + #define LED_NUM iidx_cfg->tt_led.num + + if (tt_ctx.adjust_led_start) { + if ((delta > 0) & (LED_START < 8)) { + LED_START++; + if (LED_NUM > 1) { + LED_NUM--; + } + } else if ((delta < 0) & (LED_START > 0)) { + LED_START--; + LED_NUM++; + } + } else { + if ((delta > 0) & (LED_NUM + LED_START < 128)) { + LED_NUM++; + } else if ((delta < 0) & (LED_NUM > 1)) { // at least 1 led + LED_NUM--; + } + } + } +} + void mode_tt_loop() { - for (int i = 0; i < iidx_cfg.tt_led.num; i++) { - int index = iidx_cfg.tt_led.start + i; - setup_led_tt[index] = tt_rgb32(10, 10, 10, false); - } + static uint32_t mask = 0xffffff; - setup_led_tt[iidx_cfg.tt_led.start] = tt_rgb32(0xa0, 0, 0, false); - setup_led_tt[iidx_cfg.tt_led.start + iidx_cfg.tt_led.num -1 ] = tt_rgb32(0, 0xa0, 0, false); + RUN_EVERY_N_MS(mask = ~mask, 50); + + for (int i = 1; i < iidx_cfg->tt_led.num - 1; i++) { + setup_led_tt[i] = tt_rgb32(10, 10, 10, false); + } + int head = iidx_cfg->tt_led.reversed ? TT_LED_NUM - 1 : 0; + int tail = iidx_cfg->tt_led.reversed ? 0 : TT_LED_NUM - 1; + + setup_led_tt[head] = tt_rgb32(0xa0, 0, 0, false); + setup_led_tt[tail] = tt_rgb32(0, 0xa0, 0, false); if (tt_ctx.adjust_led_start) { - setup_led_button[LED_START] = tt_rgb32(0x80, 12, 12, false); - setup_led_button[LED_EFFECT] = 0; + setup_led_tt[head] &= mask; + setup_led_button[LED_E_START] = tt_rgb32(128, 0, 0, false) & mask; + setup_led_button[LED_E_EFFECT] = tt_rgb32(0, 10, 0, false); } else { - setup_led_button[LED_START] = 0; - setup_led_button[LED_EFFECT] = tt_rgb32(12, 0x80, 12, false); + setup_led_tt[tail] &= mask; + setup_led_button[LED_E_START] = tt_rgb32(10, 0, 0, false); + setup_led_button[LED_E_EFFECT] = tt_rgb32(0, 128, 0, false) & mask; } - if (iidx_cfg.tt_led.reversed) { - setup_led_button[LED_VEFX] = tt_rgb32(0x80, 12, 12, false); - } else { - setup_led_button[LED_VEFX] = tt_rgb32(12, 0x80, 12, false); - } + uint32_t cyan = button_rgb32(0, 90, 90, false); + uint32_t yellow = button_rgb32(90, 90, 0, false); - if (iidx_cfg.tt_sensor_reversed) { - setup_led_button[LED_E4] = tt_rgb32(0x80, 12, 12, false); - } else { - setup_led_button[LED_E4] = tt_rgb32(12, 0x80, 12, false); - } + setup_led_button[LED_E_VEFX] = iidx_cfg->tt_led.reversed ? cyan : yellow; + setup_led_button[LED_E_4] = iidx_cfg->tt_sensor_reversed ? cyan : yellow; } static struct { - mode_func operate; + mode_func key_change; + mode_func rotate; mode_func loop; mode_func enter; } mode_defs[] = { - [MODE_NONE] = { nop, mode_none_loop, nop}, - [MODE_TURNTABLE] = { mode_tt_op, mode_tt_loop, mode_tt_enter}, - [MODE_ANALOG] = { check_exit, nop, nop}, - [MODE_TT_EFFECT] = { check_exit, nop, nop}, - [MODE_KEY_COLOR] = { check_exit, nop, nop}, + [MODE_NONE] = { nop, nop, mode_none_loop, nop}, + [MODE_TURNTABLE] = { mode_tt_key_change, mode_tt_rotate, mode_tt_loop, mode_tt_enter}, + [MODE_ANALOG] = { nop, check_exit, nop, nop}, + [MODE_TT_EFFECT] = { nop, check_exit, nop, nop}, + [MODE_KEY_COLOR] = { nop, check_exit, nop, nop}, }; -static void join_mode(uint8_t new_mode) +static void join_mode(setup_mode_t new_mode) { - cfg_save = iidx_cfg; + cfg_save = *iidx_cfg; memset(&setup_led_tt, 0, sizeof(setup_led_tt)); memset(&setup_led_button, 0, sizeof(setup_led_button)); current_mode = new_mode; @@ -241,27 +246,37 @@ static void join_mode(uint8_t new_mode) static void quit_mode(bool apply) { if (apply) { - iidx_cfg = iidx_cfg; - config_request_save(); + config_changed(); + } else { + *iidx_cfg = cfg_save; } current_mode = MODE_NONE; - printf("Quit setup\n"); + printf("Quit setup %s\n", apply ? "saved." : "discarded."); } bool setup_run(uint16_t keys, uint16_t angle) { + setup_tick_ms = time_us_64() / 1000; input.keys = keys; input.angle = angle; input.just_pressed = keys & ~input.last_keys; input.just_released = ~keys & input.last_keys; - input.rotate = angle - input.last_angle; + input.rotate = input.angle - input.last_angle; - if (input.just_pressed || input.just_released) { - printf("%4x %4x\n", input.just_pressed, input.just_released); + if (input.rotate != 0) { + printf("@ %d\n", input.rotate); + mode_defs[current_mode].rotate(); } - if (input.just_pressed || input.just_released || input.rotate) { - mode_defs[current_mode].operate(); + if (input.just_pressed) { + printf("+ %04x\n", input.just_pressed); + } + if (input.just_released) { + printf("- %04x\n", input.just_released); + } + + if (input.just_pressed || input.just_released) { + mode_defs[current_mode].key_change(); } mode_defs[current_mode].loop(); @@ -271,3 +286,7 @@ bool setup_run(uint16_t keys, uint16_t angle) return current_mode != MODE_NONE; } + +void setup_init() +{ +} \ No newline at end of file diff --git a/firmware/src/setup.h b/firmware/src/setup.h index da8d403..cf00854 100644 --- a/firmware/src/setup.h +++ b/firmware/src/setup.h @@ -10,34 +10,6 @@ #include #include "board_defs.h" -typedef struct __attribute ((packed)) { - uint8_t hue; - uint8_t saturation; - uint8_t value; -} key_color_t; - -typedef struct __attribute ((packed)) { - key_color_t key_off[11]; - key_color_t key_on[11]; - struct { - uint8_t start; - uint8_t num; - uint8_t effect; - uint8_t param; - uint8_t brightness; - bool reversed; - } tt_led; - bool tt_sensor_reversed; - struct { - uint8_t play_vol; - uint8_t filter; - uint8_t eq_low; - uint8_t eq_hi; - } effects; -} iidx_cfg_t; - -extern iidx_cfg_t iidx_cfg; - void setup_init(); bool setup_run(uint16_t key_flag, uint16_t tt_angle); diff --git a/firmware/src/tt_blade.c b/firmware/src/tt_blade.c index 160f993..9d22c58 100644 --- a/firmware/src/tt_blade.c +++ b/firmware/src/tt_blade.c @@ -42,19 +42,14 @@ static inline void blade_color_mix(int index, uint32_t color, uint32_t distance, /* pos: 0..4095 */ static void blade_put_pixel(uint32_t pos, uint32_t color, uint32_t level) { - pos = (pos % 4096) * tt_ring_size / 16; // *256/4096, zoom in by 256x + pos = (pos % 4096) * TT_LED_NUM / 16; // *256/4096, zoom in by 256x // calc brightness share between left and right LEDs uint32_t index_left = pos >> 8; - uint32_t index_right = (index_left + 1) % tt_ring_size; + uint32_t index_right = (index_left + 1) % TT_LED_NUM; uint32_t dis_left = pos & 0xff; uint32_t dis_right = 255 - dis_left; - if (tt_ring_reversed) { - index_left = tt_ring_size - 1 - index_left; - index_right = tt_ring_size - 1 - index_right; - } - blade_color_mix(index_left, color, dis_left, level); blade_color_mix(index_right, color, dis_right, level); } @@ -112,9 +107,9 @@ static uint32_t apply_level(uint32_t color) static void update(uint32_t context) { - uint32_t delta = tt_ring_angle > snake[0] ? tt_ring_angle - snake[0] : snake[0] - tt_ring_angle; + uint32_t delta = tt_led_angle > snake[0] ? tt_led_angle - snake[0] : snake[0] - tt_led_angle; - snake[0] = tt_ring_angle; + snake[0] = tt_led_angle; life[0] = 255; life[1] = delta > 7 ? 255: delta * 8; @@ -136,8 +131,8 @@ static void update(uint32_t context) life[i]--; } } - for (int i = 0; i < tt_ring_size; i++) { - tt_ring_buf[i] = apply_level(blade_buf[i]); + for (int i = 0; i < TT_LED_NUM; i++) { + tt_led_buf[i] = apply_level(blade_buf[i]); } } diff --git a/firmware/src/tt_rainbow.c b/firmware/src/tt_rainbow.c index f65e967..3f1a844 100644 --- a/firmware/src/tt_rainbow.c +++ b/firmware/src/tt_rainbow.c @@ -64,11 +64,10 @@ static void set_angle(uint32_t angle) static void update(uint32_t context) { - for (int i = 0; i < tt_ring_size; i++) { + for (int i = 0; i < TT_LED_NUM; i++) { uint32_t pitch = COLOR_WHEEL_SIZE * RGB_RING_CYCLE * i; - uint32_t index = (phase + pitch / tt_ring_size) % COLOR_WHEEL_SIZE; - int led = tt_ring_reversed ? tt_ring_size - 1 - i: i; - tt_ring_buf[led] = color_wheel[index]; + uint32_t index = (phase + pitch / TT_LED_NUM) % COLOR_WHEEL_SIZE; + tt_led_buf[i] = color_wheel[index]; } } diff --git a/firmware/src/turntable.c b/firmware/src/turntable.c index c7d318e..4a7c0eb 100644 --- a/firmware/src/turntable.c +++ b/firmware/src/turntable.c @@ -14,11 +14,10 @@ #include "hardware/i2c.h" #include "board_defs.h" -#include "rgb.h" +#include "config.h" static uint8_t as5600_addr = 0x36; static uint16_t angle = 0; -static bool reversed = false; void turntable_init() { @@ -30,22 +29,17 @@ void turntable_init() } void turntable_update() -{ +{ uint8_t buf[2] = {0x0c, 0x00}; i2c_write_blocking_until(TT_AS5600_I2C, as5600_addr, buf, 1, true, - time_us_64() + 1000); + make_timeout_time_ms(1)); i2c_read_blocking_until(TT_AS5600_I2C, as5600_addr, buf, 2, false, - time_us_64() + 1000); + make_timeout_time_ms(1)); angle = ((uint16_t)buf[0] & 0x0f) << 8 | buf[1]; } -void turntable_set_hardware(bool sensor_reversed) -{ - reversed = sensor_reversed; -} - uint16_t turntable_read() { - return reversed ? (4096 - angle) : angle; // 12bit + return iidx_cfg->tt_sensor_reversed ? 4095 - angle : angle; // 12bit } diff --git a/firmware/src/turntable.h b/firmware/src/turntable.h index ed568b5..672826f 100644 --- a/firmware/src/turntable.h +++ b/firmware/src/turntable.h @@ -10,7 +10,6 @@ #include void turntable_init(); -void turntable_set_hardware(bool sensor_reversed); uint16_t turntable_read(); void turntable_update();