mirror of
https://github.com/whowechina/iidx_pico.git
synced 2024-11-28 00:20:50 +01:00
Dynamic LED setup working
This commit is contained in:
parent
10e99e362a
commit
ea01a60d6c
BIN
Production/.DS_Store
vendored
Normal file
BIN
Production/.DS_Store
vendored
Normal file
Binary file not shown.
Binary file not shown.
@ -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)
|
||||
|
@ -1,145 +1,48 @@
|
||||
/*
|
||||
* Controller Config Save and Load
|
||||
* Controller Config Data
|
||||
* WHowe <github.com/whowechina>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <memory.h>
|
||||
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);
|
||||
}
|
||||
|
@ -1,21 +1,43 @@
|
||||
/*
|
||||
* Controller Config Save and Load
|
||||
* Controller Config
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* 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
|
||||
|
@ -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)
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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);
|
||||
|
148
firmware/src/save.c
Normal file
148
firmware/src/save.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Controller Save Save and Load
|
||||
* WHowe <github.com/whowechina>
|
||||
*
|
||||
* Save is stored in last sector of flash
|
||||
*/
|
||||
|
||||
#include "save.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <memory.h>
|
||||
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
21
firmware/src/save.h
Normal file
21
firmware/src/save.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Controller Flash Save and Load
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#ifndef SAVE_H
|
||||
#define SAVE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* 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
|
@ -12,26 +12,22 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#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()
|
||||
{
|
||||
}
|
@ -10,34 +10,6 @@
|
||||
#include <stdbool.h>
|
||||
#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);
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
void turntable_init();
|
||||
void turntable_set_hardware(bool sensor_reversed);
|
||||
uint16_t turntable_read();
|
||||
void turntable_update();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user