diff --git a/Production/Firmware/iidx_pico.uf2 b/Production/Firmware/iidx_pico.uf2 index 6afc53e..a4be3ef 100644 Binary files a/Production/Firmware/iidx_pico.uf2 and b/Production/Firmware/iidx_pico.uf2 differ diff --git a/README.md b/README.md index 4b9eb2f..439399e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ Features: * Key color theme and customization. * Multiple turntable effects. * Many live settings. -* Analog channels. * Konami device mode for IIDX Ultimate Mobile. * All source files open. @@ -72,7 +71,6 @@ I made this project in my personal time with no financial benefit or sponsorship * 2x SN74LV1T34DBVR (SOT-23-5) level shifter, optional, for better voltage tolerance. * 2x 0603 10ohm resistors to bypass said level shifters, optional. * 1x 0603 5.1kohm resistors for USB. -* 1x 0805 1nF capacitor for filtering analog signal, optional. * 5x 0805 1uF capacitors. * 4x Kailh low-profile keycaps. * 4x M3*6mm screws and hex nuts to fix parts together. @@ -139,28 +137,16 @@ It's very small and requires higher accuracy. You need to scrape off some solder mask to expose the ground copper (don't scrape the solder mask under 5V pin). I found a good place to mount the REF3030, this is how I handled it: - * Before proceeding further, it's important to note that the silicone hinge used in my IIDX Teeny has proven to be the most effective and stable. Therefore, it's highly recommended to opt for the hinge option, rather than the subsequent digital (pogopin) or analog (3.5mm headphone jack) options. + * Before proceeding further, it's important to note that the silicone hinge used in my IIDX Teeny has proven to be the most effective and stable. Therefore, it's highly recommended to opt for the hinge option, rather than the subsequent digital (pogopin). I've also removed the analog (3.5mm headphone jack) option simply because it's not good. * If you go with digital (magnetic pogo pin connector) There're a set of I2C and a WS2812B signal line together in the cable that connects turntable and the keyboard. Unfortunately, these signals crosstalk. So, we have to use shield cables for them. Two I2C lines should have a shield cable, and the WS2812B signal should have another shield cable. Good thing is, an HDMI cable has 4 shield cables and bunch of other small cables. We can make use of it. - * If you go with analog (3.5mm headphone jack) - Here's the pin definition. - - - The "ANGLE" connects to the AS5600 analog OUT. You need to remove a resistor from as5600 board to get OUT pin working. - - - Note: For analog, crosstalk maybe no longer an issue, but ground level becomes a new concern. When driving the turntable LED ring, there's a considerable amount of current travelling through the ground cable which lifts AS5600 ground level. There're two ways to handle this. - * One is to use a 5 wire cable. You need to separate LED ground from the sensor ground. A typical Type-C to Type-C cable has 5 wires inside. You can use red and black to power the LED and others to serve sensor ground, sensor analog out and LED signal. - - - - * The other one is to minimize the ground wire resistance. A metal braid shielding cable can be used with the metal shield serving as the ground line. Or you can find a 4 wire cable with thick core copper. * Turntable LED Ring I recommend to use LED strip over LED board, becuase the LED board's light is facing up, but using strip, the light can spread out pefectly. Use transparent double-sided tape to stick the LED around the turntable inner wall. + ### Step 4 - Assemble * Assemble the turntable I don't know how to draw an explosion diagram, this is done by coding in OpenSCAD: @@ -189,7 +175,7 @@ It's very small and requires higher accuracy. * If it is already running my IIDX firmware, hold two small AUX buttons together will do the same as the BOOTSEL button. * You need to setup your configuration such as AS5600 connection mode and LED ring. * Also you can setup the key color theme and turntable effects at runtime. I think I made the configuration too showey and complicated. Check out the manual. -[Nice Looking Manual Here](doc/Firmware_Manual.pdf) +[Nice Looking Manual Here](doc/Firmware_manual.pdf) ### What If? * I can't find pogopin connector. diff --git a/cad/TT_base_hinge.stl b/cad/TT_base_hinge.stl deleted file mode 100644 index 34693c5..0000000 Binary files a/cad/TT_base_hinge.stl and /dev/null differ diff --git a/cad/TT_base_hinge_cover.stl b/cad/TT_base_hinge_cover.stl deleted file mode 100644 index cd12e67..0000000 Binary files a/cad/TT_base_hinge_cover.stl and /dev/null differ diff --git a/cad/bearing_seat_hinge.scad b/cad/teeny_bearing.scad similarity index 100% rename from cad/bearing_seat_hinge.scad rename to cad/teeny_bearing.scad diff --git a/doc/5A_usb.jpg b/doc/5A_usb.jpg deleted file mode 100644 index 38848c9..0000000 Binary files a/doc/5A_usb.jpg and /dev/null differ diff --git a/doc/Firmware_manual.pdf b/doc/Firmware_manual.pdf index 8398515..9b616e6 100644 Binary files a/doc/Firmware_manual.pdf and b/doc/Firmware_manual.pdf differ diff --git a/doc/Firmware_manual.pptx b/doc/Firmware_manual.pptx index 9d38494..11493c0 100644 Binary files a/doc/Firmware_manual.pptx and b/doc/Firmware_manual.pptx differ diff --git a/doc/analog_wiring.png b/doc/analog_wiring.png deleted file mode 100644 index 69749a7..0000000 Binary files a/doc/analog_wiring.png and /dev/null differ diff --git a/doc/as5600_mod.png b/doc/as5600_mod.png deleted file mode 100644 index 1cc3d4d..0000000 Binary files a/doc/as5600_mod.png and /dev/null differ diff --git a/doc/headphone_jack_wiring.png b/doc/headphone_jack_wiring.png deleted file mode 100644 index 5bdba8a..0000000 Binary files a/doc/headphone_jack_wiring.png and /dev/null differ diff --git a/firmware/src/CMakeLists.txt b/firmware/src/CMakeLists.txt index 5d69263..82b068c 100644 --- a/firmware/src/CMakeLists.txt +++ b/firmware/src/CMakeLists.txt @@ -4,7 +4,7 @@ set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip) function(make_firmware board board_def) pico_sdk_init() add_executable(${board} - main.c buttons.c rgb.c save.c config.c setup.c + main.c cli.c commands.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}) diff --git a/firmware/src/cli.c b/firmware/src/cli.c new file mode 100644 index 0000000..9c2b1a9 --- /dev/null +++ b/firmware/src/cli.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include + +#include "pico/stdio.h" +#include "pico/stdlib.h" +#include "pico/bootrom.h" +#include "cli.h" +#include "save.h" + +#define MAX_COMMANDS 32 +#define MAX_PARAMETERS 6 +#define MAX_PARAMETER_LENGTH 20 + +const char *cli_prompt = "cli>"; +const char *cli_logo = "CLI"; + +static const char *commands[MAX_COMMANDS]; +static const char *helps[MAX_COMMANDS]; +static cmd_handler_t handlers[MAX_COMMANDS]; +static int max_cmd_len = 0; + +static int num_commands = 0; + +void cli_register(const char *cmd, cmd_handler_t handler, const char *help) +{ + if (num_commands < MAX_COMMANDS) { + commands[num_commands] = cmd; + handlers[num_commands] = handler; + helps[num_commands] = help; + num_commands++; + if (strlen(cmd) > max_cmd_len) { + max_cmd_len = strlen(cmd); + } + } +} + +// return -1 if not matched, return -2 if ambiguous +int cli_match_prefix(const char *str[], int num, const char *prefix) +{ + int match = -1; + bool found = false; + + for (int i = 0; (i < num) && str[i]; i++) { + if (strncasecmp(str[i], prefix, strlen(prefix)) == 0) { + if (found) { + return -2; + } + found = true; + match = i; + } + } + + return match; +} + +static void handle_help(int argc, char *argv[]) +{ + printf("%s", cli_logo); + printf("\tSN: %016llx\n\n", board_id_64()); + printf("Available commands:\n"); + for (int i = 0; i < num_commands; i++) { + printf("%*s: %s\n", max_cmd_len + 2, commands[i], helps[i]); + } +} + +static int fps[2]; +void cli_fps_count(int core) +{ + static uint32_t last[2] = {0}; + static int counter[2] = {0}; + + counter[core]++; + + uint32_t now = time_us_32(); + if (now - last[core] < 1000000) { + return; + } + last[core] = now; + fps[core] = counter[core]; + counter[core] = 0; +} + +static void handle_fps(int argc, char *argv[]) +{ + printf("FPS: core 0: %d, core 1: %d\n", fps[0], fps[1]); +} +static void handle_update(int argc, char *argv[]) +{ + printf("Boot into update mode.\n"); + fflush(stdout); + sleep_ms(100); + reset_usb_boot(0, 2); +} + +int cli_extract_non_neg_int(const char *param, int len) +{ + if (len == 0) { + len = strlen(param); + } + int result = 0; + for (int i = 0; i < len; i++) { + if (!isdigit((uint8_t)param[i])) { + return -1; + } + result = result * 10 + param[i] - '0'; + } + return result; +} + +static char cmd_buf[256]; +static int cmd_len = 0; + +static void process_cmd() +{ + char *argv[MAX_PARAMETERS]; + int argc; + + char *cmd = strtok(cmd_buf, " \n"); + + if (strlen(cmd) == 0) { + return; + } + + argc = 0; + while ((argc < MAX_PARAMETERS) && + (argv[argc] = strtok(NULL, " ,\n")) != NULL) { + argc++; + } + + int match = cli_match_prefix(commands, num_commands, cmd); + if (match == -2) { + printf("Ambiguous command.\n"); + return; + } else if (match == -1) { + printf("Unknown command.\n"); + handle_help(0, NULL); + return; + } + + handlers[match](argc, argv); +} + +void cli_run() +{ + int c = getchar_timeout_us(0); + if (c == EOF) { + return; + } + + if (c == '\b' || c == 127) { // both backspace and delete + if (cmd_len > 0) { + cmd_len--; + printf("\b \b"); + } + return; + } + + if ((c != '\n') && (c != '\r')) { + + if (cmd_len < sizeof(cmd_buf) - 2) { + cmd_buf[cmd_len] = c; + printf("%c", c); + cmd_len++; + } + return; + } + + cmd_buf[cmd_len] = '\0'; + cmd_len = 0; + + printf("\n"); + + process_cmd(); + + printf(cli_prompt); +} + +void cli_init(const char *prompt, const char *logo) +{ + if (prompt) { + cli_prompt = prompt; + } + if (logo) { + cli_logo = logo; + } + + cli_register("?", handle_help, "Display this help message."); + cli_register("fps", handle_fps, "Display FPS."); + cli_register("update", handle_update, "Update firmware."); +} diff --git a/firmware/src/cli.h b/firmware/src/cli.h new file mode 100644 index 0000000..fdbfa01 --- /dev/null +++ b/firmware/src/cli.h @@ -0,0 +1,20 @@ +/* + * Chu Controller Command Line Framework + * WHowe + */ + +#ifndef CLI_H +#define CLI_H + + +typedef void (*cmd_handler_t)(int argc, char *argv[]); + +void cli_init(const char *prompt, const char *logo); +void cli_register(const char *cmd, cmd_handler_t handler, const char *help); +void cli_run(); +void cli_fps_count(int core); + +int cli_extract_non_neg_int(const char *param, int len); +int cli_match_prefix(const char *str[], int num, const char *prefix); + +#endif diff --git a/firmware/src/commands.c b/firmware/src/commands.c new file mode 100644 index 0000000..a4a1d40 --- /dev/null +++ b/firmware/src/commands.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +#include "pico/stdio.h" +#include "pico/stdlib.h" + +#include "config.h" +#include "save.h" +#include "cli.h" + +static void handle_save() +{ + save_request(true); +} + +static void handle_factory_reset() +{ + config_factory_reset(); + printf("Factory reset done.\n"); +} + +void commands_init() +{ + cli_register("save", handle_save, "Save config to flash."); + cli_register("factory", handle_factory_reset, "Reset everything to default."); +} diff --git a/firmware/src/commands.h b/firmware/src/commands.h new file mode 100644 index 0000000..57c70f4 --- /dev/null +++ b/firmware/src/commands.h @@ -0,0 +1,11 @@ +/* + * Chu Controller Command Line Commands + * WHowe + */ + +#ifndef COMMANDS_H +#define COMMANDS_H + +void commands_init(); + +#endif diff --git a/firmware/src/config.c b/firmware/src/config.c index 6d5dae6..a915765 100644 --- a/firmware/src/config.c +++ b/firmware/src/config.c @@ -23,16 +23,10 @@ static iidx_cfg_t default_cfg = { .mode = 0, }, .tt_sensor = { - .mode = 2, + .reversed = false, .deadzone = 1, .ppr = 1, }, - .effects = { - .e1 = 255, - .e2 = 128, - .e3 = 128, - .e4 = 128, - }, .level = 128, .konami = false, }; @@ -57,10 +51,6 @@ static void config_loaded() iidx_cfg->tt_led.mode = 0; config_changed(); } - if (iidx_cfg->tt_sensor.mode > 3) { - iidx_cfg->tt_sensor.mode = 2; - config_changed(); - } if (iidx_cfg->tt_sensor.ppr > 3) { iidx_cfg->tt_sensor.ppr = 1; config_changed(); diff --git a/firmware/src/config.h b/firmware/src/config.h index 9d7353c..e4aeb89 100644 --- a/firmware/src/config.h +++ b/firmware/src/config.h @@ -26,16 +26,10 @@ typedef struct __attribute ((packed)) { uint8_t mode; /* 0: on, 1: reversed, 2: off */ } tt_led; struct { - uint8_t mode; /* 0: analog, 1: analog reversed, 2: i2c, 3: i2c reversed */ + bool reversed; uint8_t deadzone; /* only for analog */ uint8_t ppr; /* 0: 256, 1: 128, 2: 96, 3: 64, other: 256 */ } tt_sensor; - struct { - uint8_t e1; - uint8_t e2; - uint8_t e3; - uint8_t e4; - } effects; uint8_t level; /* led brightness limit */ bool konami; /* konami spoof */ } iidx_cfg_t; diff --git a/firmware/src/main.c b/firmware/src/main.c index bdc6e01..1a1d75d 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -24,26 +24,19 @@ #include "tt_blade.h" #include "tt_rainbow.h" -#include "config.h" #include "save.h" - -/* Measure the time of a function call */ -#define RUN_TIME(func) \ - { uint64_t _t = time_us_64(); func; \ - printf(#func ":%lld\n", time_us_64() - _t); } +#include "config.h" +#include "cli.h" +#include "commands.h" struct { uint16_t buttons; - uint8_t joy[6]; + uint8_t joy[2]; } hid_report; void report_usb_hid() { if (tud_hid_ready()) { - hid_report.joy[2] = iidx_cfg->effects.e1; - hid_report.joy[3] = iidx_cfg->effects.e2; - hid_report.joy[4] = iidx_cfg->effects.e3; - hid_report.joy[5] = iidx_cfg->effects.e4; tud_hid_n_report(0x00, REPORT_ID_JOYSTICK, &hid_report, sizeof(hid_report)); } } @@ -76,20 +69,9 @@ void mode_check() } } -static bool request_core1_pause = false; - -static void pause_core1(bool pause) -{ - request_core1_pause = pause; - if (pause) { - sleep_ms(5); /* wait for any IO ops to finish */ - } -} - +static mutex_t core1_io_lock; static void core1_loop() { -#define RUN_EVERY_N_MS(a, ms) { if (frame % ms == 0) a; } - uint32_t frame = 0; while (true) { uint32_t angle = turntable_raw(); rgb_set_angle(angle); @@ -98,37 +80,28 @@ static void core1_loop() hid_report.joy[0] = angle8; hid_report.joy[1] = 255 - angle8; - RUN_EVERY_N_MS(rgb_update(), 2); - turntable_update(); - frame++; - do { - sleep_ms(1); - } while (request_core1_pause); - } -} + if (mutex_try_enter(&core1_io_lock, NULL)) { + rgb_update(); + mutex_exit(&core1_io_lock); + } -static void boot_usb_check(uint16_t buttons) -{ - uint16_t usb_boot_keys = 0x1855; /* YES, NO, 1, 3, 5, 7 */ - if (buttons == usb_boot_keys) { - reset_usb_boot(0, 2); // usb boot to flash - } - - uint16_t factory_default_keys = 0x182a; /* YES, NO, 2, 4, 6 */ - if (buttons == factory_default_keys) { - config_factory_reset(); - watchdog_enable(1, 1); - while(1); // just reboot + cli_fps_count(1); + sleep_us(500); } } static void core0_loop() { + uint64_t next_frame = 0; + while (true) { tud_task(); + cli_run(); + + turntable_update(); + uint16_t buttons = button_read(); - boot_usb_check(buttons); uint16_t angle = turntable_raw() >> 4; if (setup_run(buttons, angle)) { rgb_force_display(setup_led_button, setup_led_tt); @@ -138,6 +111,10 @@ static void core0_loop() save_loop(); } report_usb_hid(); + cli_fps_count(0); + + sleep_until(next_frame); + next_frame = time_us_64() + 1000; // 1KHz } } @@ -157,19 +134,20 @@ void init() setup_init(); config_init(); - save_init(pause_core1); + mutex_init(&core1_io_lock); + save_init(0xca341234, &core1_io_lock); + cli_init("iidx_pico>", "\n << IIDX Pico|Teeny Controller >>\n" + " https://github.com/whowechina\n\n"); + commands_init(); mode_check(); } -int main(void) +void main(void) { init(); multicore_launch_core1(core1_loop); - core0_loop(); - - return 0; } // Invoked when received GET_REPORT control request diff --git a/firmware/src/rgb.c b/firmware/src/rgb.c index 82a9349..c59dba4 100644 --- a/firmware/src/rgb.c +++ b/firmware/src/rgb.c @@ -292,7 +292,15 @@ static void follow_mode_change() void rgb_update() { + static uint64_t last = 0; + uint64_t now = time_us_64(); + if (now - last < 4000) { // no faster than 250Hz + return; + } + last = now; + follow_mode_change(); + set_effect(iidx_cfg->tt_led.effect); if (time_us_64() > force_expire_time) { effect_update(); diff --git a/firmware/src/save.c b/firmware/src/save.c index b003a2c..8148dd9 100644 --- a/firmware/src/save.c +++ b/firmware/src/save.c @@ -1,8 +1,8 @@ /* - * Controller Save Save and Load + * Controller Config Save and Load * WHowe * - * Save is stored in last sector of flash + * Config is stored in last sector of flash */ #include "save.h" @@ -18,7 +18,8 @@ #include "pico/stdio.h" #include "hardware/flash.h" -#include "hardware/sync.h" +#include "pico/multicore.h" +#include "pico/unique_id.h" static struct { size_t size; @@ -27,7 +28,10 @@ static struct { } modules[8] = {0}; static int module_num = 0; -#define SAVE_PAGE_MAGIC 0xcafe4321 +static uint32_t my_magic = 0xcafecafe; + +#define SAVE_TIMEOUT_US 5000000 + #define SAVE_SECTOR_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE) typedef struct __attribute ((packed)) { @@ -43,30 +47,34 @@ static int data_page = -1; static bool requesting_save = false; static uint64_t requesting_time = 0; -static io_locker_func io_lock; +static mutex_t *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); + if (mutex_enter_timeout_us(io_lock, 200000)) { + sleep_ms(20); /* wait for all io operations to finish */ + 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); + mutex_exit(io_lock); + printf("\nProgram Flash %d %8lx done.\n", data_page, old_data.magic); + } else { + printf("Program Flash failed.\n"); } - 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; + new_data.magic = my_magic; } static const page_t *get_page(int id) @@ -78,7 +86,7 @@ static const page_t *get_page(int 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) { + if (get_page(i)->magic != my_magic) { break; } data_page = i; @@ -102,8 +110,30 @@ static void save_loaded() } } -void save_init(io_locker_func locker) +static union __attribute__((packed)) { + pico_unique_board_id_t id; + struct { + uint32_t id32h; + uint32_t id32l; + }; + uint64_t id64; +} board_id; + +uint32_t board_id_32() { + pico_get_unique_board_id(&board_id.id); + return board_id.id32h ^ board_id.id32l; +} + +uint64_t board_id_64() +{ + pico_get_unique_board_id(&board_id.id); + return board_id.id64; +} + +void save_init(uint32_t magic, mutex_t *locker) +{ + my_magic = magic; io_lock = locker; save_load(); save_loop(); @@ -112,17 +142,13 @@ void save_init(io_locker_func locker) void save_loop() { - if (requesting_save && (time_us_64() - requesting_time > 1000000)) { + if (requesting_save && (time_us_64() - requesting_time > SAVE_TIMEOUT_US)) { 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; - } + if (memcmp(&old_data, &new_data, sizeof(old_data)) == 0) { + return; } - printf("No change.\n"); + save_program(); } } @@ -140,9 +166,9 @@ void *save_alloc(size_t size, void *def, void (*after_load)()) void save_request(bool immediately) { if (!requesting_save) { - printf("Save marked.\n"); + printf("Save requested.\n"); requesting_save = true; - new_data.magic = SAVE_PAGE_MAGIC; + new_data.magic = my_magic; requesting_time = time_us_64(); } if (immediately) { diff --git a/firmware/src/save.h b/firmware/src/save.h index e1b9ae6..cde4229 100644 --- a/firmware/src/save.h +++ b/firmware/src/save.h @@ -7,11 +7,17 @@ #define SAVE_H #include +#include #include +#include "pico/multicore.h" + +uint32_t board_id_32(); +uint64_t board_id_64(); + /* 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_init(uint32_t magic, mutex_t *lock); void save_loop(); diff --git a/firmware/src/setup.c b/firmware/src/setup.c index 6c39492..73e4da8 100644 --- a/firmware/src/setup.c +++ b/firmware/src/setup.c @@ -33,7 +33,6 @@ uint32_t setup_led_button[BUTTON_RGB_NUM]; typedef enum { MODE_NONE, MODE_TURNTABLE, - MODE_ANALOG, MODE_LEVEL, MODE_TT_THEME, MODE_KEY_THEME, @@ -129,7 +128,6 @@ static int16_t input_delta(int16_t start_angle) static setup_mode_t key_to_mode[11] = { MODE_KEY_THEME, MODE_TT_THEME, MODE_KEY_ON, MODE_KEY_OFF, MODE_NONE, MODE_NONE, MODE_NONE, - MODE_ANALOG, MODE_ANALOG, MODE_ANALOG, MODE_ANALOG, }; static struct { @@ -203,7 +201,7 @@ static void tt_key_change() } else if (JUST_PRESSED(E3)) { iidx_cfg->tt_led.mode = (iidx_cfg->tt_led.mode + 1) % 3; } else if (JUST_PRESSED(E4)) { - iidx_cfg->tt_sensor.mode = (iidx_cfg->tt_sensor.mode + 1) % 4; + iidx_cfg->tt_sensor.reversed = !iidx_cfg->tt_sensor.reversed; } else if (JUST_PRESSED(KEY_2)) { iidx_cfg->tt_sensor.deadzone = 0; } else if (JUST_PRESSED(KEY_4)) { @@ -287,20 +285,8 @@ static void tt_loop() break; } - switch (iidx_cfg->tt_sensor.mode) { - case 0: - setup_led_button[LED_E4] = GREEN; - break; - case 1: - setup_led_button[LED_E4] = RED; - break; - case 2: - setup_led_button[LED_E4] = CYAN; - break; - default: - setup_led_button[LED_E4] = YELLOW; - break; - } + setup_led_button[LED_E4] = iidx_cfg->tt_sensor.reversed ? CYAN : YELLOW; + setup_led_button[LED_KEY_2] = iidx_cfg->tt_sensor.deadzone == 0 ? SILVER : 0; setup_led_button[LED_KEY_4] = iidx_cfg->tt_sensor.deadzone == 1 ? SILVER : 0; setup_led_button[LED_KEY_6] = iidx_cfg->tt_sensor.deadzone == 2 ? SILVER : 0; @@ -357,107 +343,6 @@ static void level_loop() } } -static struct { - uint8_t channel; /* 0:E1(Start), 1:E2(Effect), 2:E3(VEFX), 3:E4 */ - volatile uint8_t *value; - int16_t start_angle; -} analog_ctx; - -static void analog_key_change() -{ - if (JUST_PRESSED(E1)) { - analog_ctx.channel = 0; - analog_ctx.value = &iidx_cfg->effects.e1; - } else if (JUST_PRESSED(E2)) { - analog_ctx.channel = 1; - analog_ctx.value = &iidx_cfg->effects.e2; - } else if (JUST_PRESSED(E3)) { - analog_ctx.channel = 2; - analog_ctx.value = &iidx_cfg->effects.e3; - } else if (JUST_PRESSED(E4)) { - analog_ctx.channel = 3; - analog_ctx.value = &iidx_cfg->effects.e4; - } else if (JUST_PRESSED(KEY_1)) { - *analog_ctx.value = 0; - } else if (JUST_PRESSED(KEY_2)) { - *analog_ctx.value = 43; - } else if (JUST_PRESSED(KEY_3)) { - *analog_ctx.value = 85; - } else if (JUST_PRESSED(KEY_4)) { - *analog_ctx.value = 128; - } else if (JUST_PRESSED(KEY_5)) { - *analog_ctx.value = 170; - } else if (JUST_PRESSED(KEY_6)) { - *analog_ctx.value = 213; - } else if (JUST_PRESSED(KEY_7)) { - *analog_ctx.value = 255; - } - - check_exit(); -} - -static void analog_enter() -{ - analog_key_change(); -} - -static void analog_rotate() -{ - int16_t new_value = *analog_ctx.value; - new_value += input.rotate; - if (new_value < 0) { - new_value = 0; - } else if (new_value > 255) { - new_value = 255; - } - *analog_ctx.value = new_value; -} - -static uint32_t scale_color(uint32_t color, uint8_t value, uint8_t factor) -{ - uint8_t r = (color >> 16) & 0xff; - uint8_t g = (color >> 8) & 0xff; - uint8_t b = color & 0xff; - - r = (r * value) / factor; - g = (g * value) / factor; - b = (b * value) / factor; - - return (r << 16) | (g << 8) | b; -} - -static void analog_loop() -{ - uint32_t colors[4] = { RED, GREEN, CYAN, YELLOW}; - uint32_t tt_colors[4] = { TT_RED, TT_GREEN, TT_CYAN, TT_YELLOW }; - - for (int i = 0; i < 4; i++) { - uint32_t color = colors[i]; - if (analog_ctx.channel == i) { - color &= blink_fast; - } - setup_led_button[LED_E1 + i] = color; - } - - int tt_split = (int)*analog_ctx.value * iidx_cfg->tt_led.num / 255; - - for (int i = 1; i < iidx_cfg->tt_led.num - 1; i++) { - setup_led_tt[i] = i < tt_split ? tt_colors[analog_ctx.channel] : 0; - } - - int button_split = *analog_ctx.value / 37; - int scale = *analog_ctx.value % 37; - for (int i = 0; i < 7; i++) { - uint32_t color = colors[analog_ctx.channel]; - if (i == button_split) { - color = scale_color(color, scale, 37); - } else if (i > button_split) { - color = 0; - } - setup_led_button[LED_KEY_1 + i] = color; - } -} - static struct { uint8_t phase; /* 0:H, 1:S, 2:V */ hsv_t hsv; @@ -657,7 +542,6 @@ static struct { } mode_defs[] = { [MODE_NONE] = { nop, none_rotate, none_loop, nop}, [MODE_TURNTABLE] = { tt_key_change, tt_rotate, tt_loop, tt_enter}, - [MODE_ANALOG] = { analog_key_change, analog_rotate, analog_loop, analog_enter}, [MODE_LEVEL] = { level_key_change, level_rotate, level_loop, nop}, [MODE_TT_THEME] = { tt_theme_key_change, nop, tt_theme_loop, nop}, [MODE_KEY_THEME] = { key_theme_key_change, nop, key_theme_loop, nop}, @@ -672,18 +556,19 @@ static void join_mode(setup_mode_t new_mode) memset(&setup_led_button, 0, sizeof(setup_led_button)); current_mode = new_mode; mode_defs[current_mode].enter(); - printf("Entering setup %d\n", new_mode); + printf("Entering setup mode %d\n", new_mode); } static void quit_mode(bool apply) { + printf("Setup %s\n", apply ? "accepted." : "discarded."); + if (apply) { config_changed(); } else { *iidx_cfg = cfg_save; } current_mode = MODE_NONE; - printf("Quit setup %s\n", apply ? "saved." : "discarded."); } bool setup_run(uint16_t keys, uint16_t angle) @@ -693,17 +578,11 @@ bool setup_run(uint16_t keys, uint16_t angle) input.angle = angle; input.just_pressed = keys & ~input.last_keys; input.just_released = ~keys & input.last_keys; + input.rotate = input_delta(input.last_angle); if (input.rotate != 0) { - printf("@ %3d %2x\n", input.rotate, input.angle); mode_defs[current_mode].rotate(); } - 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(); diff --git a/firmware/src/turntable.c b/firmware/src/turntable.c index 88624d3..76195b8 100644 --- a/firmware/src/turntable.c +++ b/firmware/src/turntable.c @@ -19,7 +19,6 @@ #include "config.h" static uint16_t angle = 0; -static uint8_t current_mode = 0; static void init_i2c() { @@ -32,130 +31,12 @@ static void init_i2c() gpio_pull_up(TT_AS5600_SDA); } -static void init_analog() -{ - adc_init(); - adc_gpio_init(TT_AS5600_ANALOG); - adc_select_input(TT_AS5600_ANALOG - 26); -} - -static void follow_mode_change() -{ - if (current_mode != iidx_cfg->tt_sensor.mode) { - turntable_init(); - } -} - void turntable_init() { - current_mode = iidx_cfg->tt_sensor.mode; - if (current_mode > 1) { - init_i2c(); - } else { - init_analog(); - } + init_i2c(); } -static uint32_t min_adc = 0; /* idealy [0..3740] */ -static uint32_t max_adc = 3740; -static bool min_touched = false; -static bool max_touched = false; - -static inline void adjust_max(uint32_t value) -{ - if (value > max_adc) { - max_adc += (value - max_adc + 1) / 2; - printf("Auto adc max: %4lu %4lu\n", min_adc, max_adc); - } - max_touched = true; -} - -static inline void adjust_min(uint32_t value) -{ - if (value < min_adc) { - min_adc -= (min_adc - value + 1) / 2; - printf("Auto adc min: %4lu %4lu\n", min_adc, max_adc); - } - min_touched = true; -} - -static void auto_adjust_adc() -{ - if (!min_touched || !max_touched) { - return; - } - min_touched = false; - max_touched = false; - - if (max_adc > 3540) { - max_adc--; - } - if (min_adc < 200) { - min_adc++; - } - printf("Auto adc adj: %4lu %4lu\n", min_adc, max_adc); -} - -static uint16_t read_average(uint16_t size) -{ - uint32_t large_cnt = 0; - uint32_t small_cnt = 0; - uint32_t large = 0; - uint32_t small = 0; - uint32_t medium = 0; - - for (int i = 0; i < size; i++) { - uint32_t sample = adc_read(); - if (sample > 3540) { - large_cnt++; - large += sample; - } else if (sample < 200) { - small_cnt++; - small += sample; - } else { - medium += sample; - } - } - - if (large_cnt > 50) { - adjust_max(large / large_cnt); - } - - if (small_cnt > 50) { - adjust_min(small / small_cnt); - } - - uint32_t all = large + small + medium; - - if (large_cnt && small_cnt) { - all += small_cnt * max_adc; - } - - return (all / size) % max_adc; -} - -static void update_analog() -{ - static uint16_t sample = 0; - - auto_adjust_adc(); - - uint16_t deadzone = (iidx_cfg->tt_sensor.deadzone + 1) * 16; - - int new_value = read_average(200); - int delta = abs(new_value - sample); - if ((abs(delta) < deadzone) || (abs(delta) > 4096 - deadzone)) { - return; - } - sample = new_value; - if (sample < min_adc) { - angle = 0; - return; - } - angle = (sample - min_adc) * 4095 / (max_adc - min_adc); -} - -static void update_i2c() +void turntable_update() { const uint8_t as5600_addr = 0x36; uint8_t buf[2] = {0x0c, 0x00}; @@ -167,20 +48,9 @@ static void update_i2c() angle = ((uint16_t)buf[0] & 0x0f) << 8 | buf[1]; } -void turntable_update() -{ - follow_mode_change(); - if (current_mode > 1) { - update_i2c(); - } else { - update_analog(); - } -} - uint16_t turntable_raw() { - bool reversed = iidx_cfg->tt_sensor.mode & 0x01; - return reversed ? 4095 - angle : angle; // 12bit + return iidx_cfg->tt_sensor.reversed ? 4095 - angle : angle; // 12bit } uint8_t turntable_read() diff --git a/firmware/src/usb_descriptors.h b/firmware/src/usb_descriptors.h index c6021d8..d7925d0 100644 --- a/firmware/src/usb_descriptors.h +++ b/firmware/src/usb_descriptors.h @@ -7,8 +7,6 @@ enum { REPORT_ID_JOYSTICK = 1, REPORT_ID_LIGHTS, - REPORT_ID_KEYBOARD, - REPORT_ID_MOUSE, }; // because they are missing from tusb_hid.h @@ -34,9 +32,7 @@ enum { HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_LOGICAL_MIN(0x00), \ HID_LOGICAL_MAX_N(0x00ff, 2), /* Below is Joystick/analog */ \ HID_USAGE(HID_USAGE_DESKTOP_X), HID_USAGE(HID_USAGE_DESKTOP_Y), \ - HID_USAGE(HID_USAGE_DESKTOP_Z), HID_USAGE(HID_USAGE_DESKTOP_RX), \ - HID_USAGE(HID_USAGE_DESKTOP_RY), HID_USAGE(HID_USAGE_DESKTOP_RZ), \ - HID_REPORT_COUNT(6), HID_REPORT_SIZE(8), \ + HID_REPORT_COUNT(2), HID_REPORT_SIZE(8), \ HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), HID_COLLECTION_END // Light Map @@ -52,19 +48,6 @@ enum { HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \ HID_COLLECTION_END -// NKRO Descriptor -#define GAMECON_REPORT_DESC_NKRO(...) \ - HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(HID_USAGE_PAGE_KEYBOARD), \ - HID_COLLECTION(HID_COLLECTION_APPLICATION), \ - __VA_ARGS__ HID_REPORT_SIZE(1), HID_REPORT_COUNT(8), \ - HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), HID_USAGE_MIN(224), \ - HID_USAGE_MAX(231), HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \ - HID_INPUT(HID_VARIABLE), HID_REPORT_SIZE(1), HID_REPORT_COUNT(31 * 8), \ - HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \ - HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), HID_USAGE_MIN(0), \ - HID_USAGE_MAX(31 * 8 - 1), HID_INPUT(HID_VARIABLE), HID_COLLECTION_END - - /* Enable Konami spoof mode */ void konami_mode();