diff --git a/README.md b/README.md index dc3a59b..a209486 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Pico-Game-Controller -Code for a keyboard or game controller using a Raspberry Pi Pico. Capable of handling 11 buttons, 10 LEDs, 1 WS2812B RGB strip, and 2 encoders. Developed with SDVX and IIDX in mind - see branches release/pocket-sdvx-pico and release/pocket-iidx for preconfigured versions. +Code for a keyboard or game controller using a Raspberry Pi Pico. Capable of handling 11 buttons, 10 LEDs, 1 WS2812B RGB strip, and 2 encoders. Developed with SDVX and IIDX in mind - see branches release/pocket-sdvx-pico and release/pocket-iidx for preconfigured versions. Demo of this firmware running on Pocket SDVX Pico, purchasable at https://discord.gg/MmuKd73XbY @@ -16,6 +16,7 @@ Currently working/fixed: - sdvx/iidx spoof - Tested on EAC - checkout branches release/pocket-sdvx-pico or release/pocket-iidx - 1000hz polling - Reversable Encoders with debouncing +- Switch debouncing - Switch and LED pins are now staggered for easier wiring - Fix 0-~71% encoder rollover in gamepad mode, uint32 max val isn't divisible evenly by ppr\*4 for joystick - thanks friends - HID LEDs now have labels, thanks CrazyRedMachine @@ -25,7 +26,6 @@ TODO: - refactor ws2812b into a seperate file for cleaner code & implement more RGB modes - store configuration settings in a text file? consider implementing littlefs https://github.com/littlefs-project/littlefs https://www.raspberrypi.org/forums/viewtopic.php?t=313009 https://www.raspberrypi.org/forums/viewtopic.php?p=1894014#p1894014 - Store last mode in flash memory (probably implement into above TODO if possible) https://www.raspberrypi.org/forums/viewtopic.php?t=305570 -- debounce on switches How to Use: diff --git a/build_uf2/Pico_Game_Controller.uf2 b/build_uf2/Pico_Game_Controller.uf2 index de2ee72..c95ea12 100644 Binary files a/build_uf2/Pico_Game_Controller.uf2 and b/build_uf2/Pico_Game_Controller.uf2 differ diff --git a/src/controller_config.h b/src/controller_config.h index 45bfdd3..478a74e 100644 --- a/src/controller_config.h +++ b/src/controller_config.h @@ -6,6 +6,7 @@ #define ENC_GPIO_SIZE 2 // Number of encoders #define ENC_PPR 600 // Encoder PPR #define ENC_DEBOUNCE true // Encoder Debouncing +#define SW_DEBOUNCE_TIME_US 4000 // Switch debounce delay in us #define ENC_PULSE (ENC_PPR * 4) // 4 pulses per PPR #define REACTIVE_TIMEOUT_MAX 500000 // Cycles before HID falls back to reactive #define WS2812B_LED_SIZE 10 // Number of WS2812B LEDs diff --git a/src/pico_game_controller.c b/src/pico_game_controller.c index c18305a..adb91a5 100644 --- a/src/pico_game_controller.c +++ b/src/pico_game_controller.c @@ -26,11 +26,11 @@ uint32_t enc_val[ENC_GPIO_SIZE]; uint32_t prev_enc_val[ENC_GPIO_SIZE]; int cur_enc_val[ENC_GPIO_SIZE]; -bool sw_val[SW_GPIO_SIZE]; +bool prev_sw_val[SW_GPIO_SIZE]; +uint64_t sw_timestamp[SW_GPIO_SIZE]; bool kbm_report; -bool leds_changed; unsigned long reactive_timeout_count = REACTIVE_TIMEOUT_MAX; void (*loop_mode)(); @@ -114,23 +114,20 @@ void update_lights() { if (reactive_timeout_count < REACTIVE_TIMEOUT_MAX) { reactive_timeout_count++; } - if (leds_changed) { - for (int i = 0; i < LED_GPIO_SIZE; i++) { - if (reactive_timeout_count >= REACTIVE_TIMEOUT_MAX) { - if (sw_val[i]) { - gpio_put(LED_GPIO[i], 1); - } else { - gpio_put(LED_GPIO[i], 0); - } + for (int i = 0; i < LED_GPIO_SIZE; i++) { + if (reactive_timeout_count >= REACTIVE_TIMEOUT_MAX) { + if (!gpio_get(SW_GPIO[i])) { + gpio_put(LED_GPIO[i], 1); } else { - if (lights_report.lights.buttons[i] == 0) { - gpio_put(LED_GPIO[i], 0); - } else { - gpio_put(LED_GPIO[i], 1); - } + gpio_put(LED_GPIO[i], 0); + } + } else { + if (lights_report.lights.buttons[i] == 0) { + gpio_put(LED_GPIO[i], 0); + } else { + gpio_put(LED_GPIO[i], 1); } } - leds_changed = false; } } @@ -147,7 +144,13 @@ void joy_mode() { if (tud_hid_ready()) { uint16_t translate_buttons = 0; for (int i = SW_GPIO_SIZE - 1; i >= 0; i--) { - translate_buttons = (translate_buttons << 1) | (sw_val[i] ? 1 : 0); + if (!gpio_get(SW_GPIO[i]) && + time_us_64() - sw_timestamp[i] >= SW_DEBOUNCE_TIME_US) { + translate_buttons = + (translate_buttons << 1) | (!gpio_get(SW_GPIO[i]) ? 1 : 0); + } else { + translate_buttons <<= 1; + } } report.buttons = translate_buttons; @@ -177,7 +180,8 @@ void key_mode() { /*------------- Keyboard -------------*/ uint8_t nkro_report[32] = {0}; for (int i = 0; i < SW_GPIO_SIZE; i++) { - if (sw_val[i]) { + if (!gpio_get(SW_GPIO[i]) && + time_us_64() - sw_timestamp[i] >= SW_DEBOUNCE_TIME_US) { uint8_t bit = SW_KEYCODE[i] % 8; uint8_t byte = (SW_KEYCODE[i] / 8) + 1; if (SW_KEYCODE[i] >= 240 && SW_KEYCODE[i] <= 247) { @@ -209,14 +213,16 @@ void key_mode() { /** * Update Input States + * Note: Switches are pull up, negate value **/ void update_inputs() { for (int i = 0; i < SW_GPIO_SIZE; i++) { - sw_val[i] = !gpio_get(SW_GPIO[i]); // Switches are pull up, negate value + // If switch gets pressed, record timestamp + if (prev_sw_val[i] == false && !gpio_get(SW_GPIO[i]) == true) { + sw_timestamp[i] = time_us_64(); + } + prev_sw_val[i] = !gpio_get(SW_GPIO[i]); } - - // Update LEDs if input changed while in reactive mode - if (reactive_timeout_count >= REACTIVE_TIMEOUT_MAX) leds_changed = true; } /** @@ -251,9 +257,7 @@ void init() { // Setup Encoders for (int i = 0; i < ENC_GPIO_SIZE; i++) { - enc_val[i] = 0; - prev_enc_val[i] = 0; - cur_enc_val[i] = 0; + enc_val[i], prev_enc_val[i], cur_enc_val[i] = 0; encoders_program_init(pio, i, offset, ENC_GPIO[i], ENC_DEBOUNCE); dma_channel_config c = dma_channel_get_default_config(i); @@ -280,7 +284,8 @@ void init() { // Setup Button GPIO for (int i = 0; i < SW_GPIO_SIZE; i++) { - sw_val[i] = false; + prev_sw_val[i] = false; + sw_timestamp[i] = 0; gpio_init(SW_GPIO[i]); gpio_set_function(SW_GPIO[i], GPIO_FUNC_SIO); gpio_set_dir(SW_GPIO[i], GPIO_IN); @@ -294,16 +299,15 @@ void init() { } // Set listener bools - leds_changed = false; kbm_report = false; // Joy/KB Mode Switching - if (gpio_get(SW_GPIO[0])) { - loop_mode = &joy_mode; - joy_mode_check = true; - } else { + if (!gpio_get(SW_GPIO[0])) { loop_mode = &key_mode; joy_mode_check = false; + } else { + loop_mode = &joy_mode; + joy_mode_check = true; } } @@ -368,6 +372,5 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, lights_report.raw[i] = buffer[i + 1]; } reactive_timeout_count = 0; - leds_changed = true; } }