Suppress nkro when button stuck

This commit is contained in:
whowechina 2024-04-29 10:52:28 +08:00
parent bbd2ff3472
commit ed44780cb2
8 changed files with 1046 additions and 1026 deletions

Binary file not shown.

View File

@ -1,97 +1,107 @@
/*
* Mai Controller Buttons
* WHowe <github.com/whowechina>
*
*/
#include "button.h"
#include <stdint.h>
#include <stdbool.h>
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "hardware/pwm.h"
#include "config.h"
#include "board_defs.h"
static const uint8_t gpio_def[] = BUTTON_DEF;
static uint8_t gpio_real[] = BUTTON_DEF;
#define BUTTON_NUM (sizeof(gpio_def))
static bool sw_val[BUTTON_NUM]; /* true if pressed */
static uint64_t sw_freeze_time[BUTTON_NUM];
void button_init()
{
for (int i = 0; i < BUTTON_NUM; i++)
{
sw_val[i] = false;
sw_freeze_time[i] = 0;
uint8_t gpio = mai_cfg->alt.buttons[i];
if (gpio > 29) {
gpio = gpio_def[i];
}
gpio_real[i] = gpio;
gpio_init(gpio);
gpio_set_function(gpio, GPIO_FUNC_SIO);
gpio_set_dir(gpio, GPIO_IN);
gpio_pull_up(gpio);
}
}
uint8_t button_num()
{
return BUTTON_NUM;
}
uint8_t button_real_gpio(int id)
{
if (id >= BUTTON_NUM) {
return 0xff;
}
return gpio_real[id];
}
uint8_t button_default_gpio(int id)
{
if (id >= BUTTON_NUM) {
return 0xff;
}
return gpio_def[id];
}
static uint16_t button_reading;
/* If a switch flips, it freezes for a while */
#define DEBOUNCE_FREEZE_TIME_US 3000
void button_update()
{
uint64_t now = time_us_64();
uint16_t buttons = 0;
for (int i = BUTTON_NUM - 1; i >= 0; i--) {
bool sw_pressed = !gpio_get(gpio_real[i]);
if (now >= sw_freeze_time[i]) {
if (sw_pressed != sw_val[i]) {
sw_val[i] = sw_pressed;
sw_freeze_time[i] = now + DEBOUNCE_FREEZE_TIME_US;
}
}
buttons <<= 1;
if (sw_val[i]) {
buttons |= 1;
}
}
button_reading = buttons;
}
uint16_t button_read()
{
return button_reading;
}
/*
* Mai Controller Buttons
* WHowe <github.com/whowechina>
*
*/
#include "button.h"
#include <stdint.h>
#include <stdbool.h>
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "hardware/pwm.h"
#include "config.h"
#include "board_defs.h"
static const uint8_t gpio_def[] = BUTTON_DEF;
static uint8_t gpio_real[] = BUTTON_DEF;
#define BUTTON_NUM (sizeof(gpio_def))
static bool sw_val[BUTTON_NUM]; /* true if pressed */
static uint64_t sw_freeze_time[BUTTON_NUM];
void button_init()
{
for (int i = 0; i < BUTTON_NUM; i++)
{
sw_val[i] = false;
sw_freeze_time[i] = 0;
uint8_t gpio = mai_cfg->alt.buttons[i];
if (gpio > 29) {
gpio = gpio_def[i];
}
gpio_real[i] = gpio;
gpio_init(gpio);
gpio_set_function(gpio, GPIO_FUNC_SIO);
gpio_set_dir(gpio, GPIO_IN);
gpio_pull_up(gpio);
}
}
bool button_is_stuck()
{
for (int i = 0; i < BUTTON_NUM; i++) {
if (!gpio_get(gpio_real[i])) {
return true;
}
}
return false;
}
uint8_t button_num()
{
return BUTTON_NUM;
}
uint8_t button_real_gpio(int id)
{
if (id >= BUTTON_NUM) {
return 0xff;
}
return gpio_real[id];
}
uint8_t button_default_gpio(int id)
{
if (id >= BUTTON_NUM) {
return 0xff;
}
return gpio_def[id];
}
static uint16_t button_reading;
/* If a switch flips, it freezes for a while */
#define DEBOUNCE_FREEZE_TIME_US 3000
void button_update()
{
uint64_t now = time_us_64();
uint16_t buttons = 0;
for (int i = BUTTON_NUM - 1; i >= 0; i--) {
bool sw_pressed = !gpio_get(gpio_real[i]);
if (now >= sw_freeze_time[i]) {
if (sw_pressed != sw_val[i]) {
sw_val[i] = sw_pressed;
sw_freeze_time[i] = now + DEBOUNCE_FREEZE_TIME_US;
}
}
buttons <<= 1;
if (sw_val[i]) {
buttons |= 1;
}
}
button_reading = buttons;
}
uint16_t button_read()
{
return button_reading;
}

View File

@ -1,20 +1,24 @@
/*
* Mai Controller Buttons
* WHowe <github.com/whowechina>
*/
#ifndef BUTTONS_H
#define BUTTONS_H
#include <stdint.h>
#include <stdbool.h>
#include "hardware/flash.h"
void button_init();
uint8_t button_num();
void button_update();
uint16_t button_read();
uint8_t button_real_gpio(int id);
uint8_t button_default_gpio(int id);
#endif
/*
* Mai Controller Buttons
* WHowe <github.com/whowechina>
*/
#ifndef BUTTONS_H
#define BUTTONS_H
#include <stdint.h>
#include <stdbool.h>
#include "hardware/flash.h"
void button_init();
/* if anykey is pressed, no debounce */
bool button_is_stuck();
uint8_t button_num();
void button_update();
uint16_t button_read();
uint8_t button_real_gpio(int id);
uint8_t button_default_gpio(int id);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,120 +1,121 @@
/*
* Controller Config and Runtime Data
* WHowe <github.com/whowechina>
*
* Config is a global data structure that stores all the configuration
* Runtime is something to share between files.
*/
#include <string.h>
#include "config.h"
#include "save.h"
#include "touch.h"
mai_cfg_t *mai_cfg;
static mai_cfg_t default_cfg = {
.color = {
.key_on = 0xc0c0c0,
.key_off = 0x080808,
.level = 127,
},
.sense = {
.filter = 0x10,
.debounce_touch = 1,
.debounce_release = 2,
},
.hid = {
.joy = 0,
.nkro = 1,
},
.rgb = {
.per_button = 1,
.per_aux = 1,
},
.alt = {
.buttons = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
.touch = TOUCH_MAP,
},
.aime = {
.mode = 0,
.virtual_aic = 0,
}
};
mai_runtime_t *mai_runtime;
static inline bool in_range(int val, int min, int max)
{
return (val >= min) && (val <= max);
}
static bool touch_map_valid()
{
uint64_t mask = 0;
for (int i = 0; i < sizeof(mai_cfg->alt.touch); i++) {
if (mai_cfg->alt.touch[i] < 34) {
mask |= 1ULL << mai_cfg->alt.touch[i];
}
}
int keys = 0;
for (int i = 0; i < 34; i++) {
if (mask & (1ULL << i)) {
keys++;
}
}
return keys > 10; // bad data results in low touch key coverage
}
static void config_loaded()
{
if ((mai_cfg->sense.filter & 0x0f) > 3 ||
((mai_cfg->sense.filter >> 4) & 0x0f) > 3) {
mai_cfg->sense = default_cfg.sense;
config_changed();
}
if (!in_range(mai_cfg->sense.global, -9, 9)) {
mai_cfg->sense = default_cfg.sense;
config_changed();
}
for (int i = 0; i < 34; i++) {
if (!in_range(mai_cfg->sense.zones[i], -9, 9)) {
mai_cfg->sense = default_cfg.sense;
config_changed();
break;
}
}
if (!in_range(mai_cfg->sense.debounce_touch, 0, 7) ||
!in_range(mai_cfg->sense.debounce_release, 0, 7)) {
mai_cfg->sense = default_cfg.sense;
config_changed();
}
if (!in_range(mai_cfg->rgb.per_button, 1, 16) ||
!in_range(mai_cfg->rgb.per_aux, 1, 16)) {
mai_cfg->rgb = default_cfg.rgb;
config_changed();
}
if (!touch_map_valid()) {
memcpy(mai_cfg->alt.touch, default_cfg.alt.touch,
sizeof(mai_cfg->alt.touch));
config_changed();
}
}
void config_changed()
{
save_request(false);
}
void config_factory_reset()
{
*mai_cfg = default_cfg;
save_request(true);
}
void config_init()
{
mai_cfg = (mai_cfg_t *)save_alloc(sizeof(*mai_cfg), &default_cfg, config_loaded);
}
/*
* Controller Config and Runtime Data
* WHowe <github.com/whowechina>
*
* Config is a global data structure that stores all the configuration
* Runtime is something to share between files.
*/
#include <string.h>
#include "config.h"
#include "save.h"
#include "touch.h"
mai_cfg_t *mai_cfg;
static mai_cfg_t default_cfg = {
.color = {
.key_on = 0xc0c0c0,
.key_off = 0x080808,
.level = 127,
},
.sense = {
.filter = 0x10,
.debounce_touch = 1,
.debounce_release = 2,
},
.hid = {
.joy = 0,
.nkro = 1,
},
.rgb = {
.per_button = 1,
.per_aux = 1,
},
.alt = {
.buttons = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
.touch = TOUCH_MAP,
},
.aime = {
.mode = 0,
.virtual_aic = 0,
}
};
mai_runtime_t mai_runtime;
static inline bool in_range(int val, int min, int max)
{
return (val >= min) && (val <= max);
}
static bool touch_map_valid()
{
uint64_t mask = 0;
for (int i = 0; i < sizeof(mai_cfg->alt.touch); i++) {
if (mai_cfg->alt.touch[i] < 34) {
mask |= 1ULL << mai_cfg->alt.touch[i];
}
}
int keys = 0;
for (int i = 0; i < 34; i++) {
if (mask & (1ULL << i)) {
keys++;
}
}
return keys > 10; // bad data results in low touch key coverage
}
static void config_loaded()
{
if ((mai_cfg->sense.filter & 0x0f) > 3 ||
((mai_cfg->sense.filter >> 4) & 0x0f) > 3) {
mai_cfg->sense = default_cfg.sense;
config_changed();
}
if (!in_range(mai_cfg->sense.global, -9, 9)) {
mai_cfg->sense = default_cfg.sense;
config_changed();
}
for (int i = 0; i < 34; i++) {
if (!in_range(mai_cfg->sense.zones[i], -9, 9)) {
mai_cfg->sense = default_cfg.sense;
config_changed();
break;
}
}
if (!in_range(mai_cfg->sense.debounce_touch, 0, 7) ||
!in_range(mai_cfg->sense.debounce_release, 0, 7)) {
mai_cfg->sense = default_cfg.sense;
config_changed();
}
if (!in_range(mai_cfg->rgb.per_button, 1, 16) ||
!in_range(mai_cfg->rgb.per_aux, 1, 16)) {
mai_cfg->rgb = default_cfg.rgb;
config_changed();
}
if (!touch_map_valid()) {
memcpy(mai_cfg->alt.touch, default_cfg.alt.touch,
sizeof(mai_cfg->alt.touch));
config_changed();
}
}
void config_changed()
{
save_request(false);
}
void config_factory_reset()
{
*mai_cfg = default_cfg;
save_request(true);
}
void config_init()
{
mai_cfg = (mai_cfg_t *)save_alloc(sizeof(*mai_cfg), &default_cfg, config_loaded);
}

View File

@ -1,56 +1,57 @@
/*
* Controller Config
* WHowe <github.com/whowechina>
*/
#ifndef CONFIG_H
#define CONFIG_H
#include <stdint.h>
#include <stdbool.h>
#include "board_defs.h"
typedef struct __attribute__((packed)) {
struct {
uint32_t key_on;
uint32_t key_off;
uint8_t level;
} color;
struct {
int8_t filter;
int8_t global;
uint8_t debounce_touch;
uint8_t debounce_release;
int8_t zones[34];
} sense;
struct {
uint8_t joy : 4;
uint8_t nkro : 4;
} hid;
struct {
uint8_t per_button;
uint8_t per_aux;
} rgb;
struct {
uint8_t buttons[12];
uint8_t touch[36];
} alt;
struct {
uint8_t mode : 4;
uint8_t virtual_aic : 4;
} aime;
} mai_cfg_t;
typedef struct {
uint16_t fps[2];
} mai_runtime_t;
extern mai_cfg_t *mai_cfg;
extern mai_runtime_t *mai_runtime;
void config_init();
void config_changed(); // Notify the config has changed
void config_factory_reset(); // Reset the config to factory default
#endif
/*
* Controller Config
* WHowe <github.com/whowechina>
*/
#ifndef CONFIG_H
#define CONFIG_H
#include <stdint.h>
#include <stdbool.h>
#include "board_defs.h"
typedef struct __attribute__((packed)) {
struct {
uint32_t key_on;
uint32_t key_off;
uint8_t level;
} color;
struct {
int8_t filter;
int8_t global;
uint8_t debounce_touch;
uint8_t debounce_release;
int8_t zones[34];
} sense;
struct {
uint8_t joy : 4;
uint8_t nkro : 4;
} hid;
struct {
uint8_t per_button;
uint8_t per_aux;
} rgb;
struct {
uint8_t buttons[12];
uint8_t touch[36];
} alt;
struct {
uint8_t mode : 4;
uint8_t virtual_aic : 4;
} aime;
} mai_cfg_t;
typedef struct {
uint16_t fps[2];
bool key_stuck;
} mai_runtime_t;
extern mai_cfg_t *mai_cfg;
extern mai_runtime_t mai_runtime;
void config_init();
void config_changed(); // Notify the config has changed
void config_factory_reset(); // Reset the config to factory default
#endif

View File

@ -1,122 +1,122 @@
#include <stdint.h>
#include <stdbool.h>
#include "board_defs.h"
#include "tusb.h"
#include "usb_descriptors.h"
#include "button.h"
#include "config.h"
#include "hid.h"
struct __attribute__((packed)) {
uint16_t adcs[8];
uint16_t spinners[4];
uint16_t chutes[2];
uint16_t buttons[2];
uint8_t system_status;
uint8_t usb_status;
uint8_t padding[29];
} hid_joy;
struct __attribute__((packed)) {
uint8_t modifier;
uint8_t keymap[15];
} hid_nkro;
static uint16_t native_to_io4(uint16_t button)
{
static const int target_pos[] = { 2, 3, 0, 15, 14, 13, 12, 11, 1, 9, 6 };
uint16_t io4btn = 0;
for (int i = 0; i < 8; i++) {
bool pressed = button & (1 << i);
io4btn |= pressed ? 0 : (1 << target_pos[i]);
}
for (int i = 8; i < 11; i++) {
bool pressed = button & (1 << i);
io4btn |= pressed ? (1 << target_pos[i]) : 0;
}
return io4btn;
}
static void report_usb_hid()
{
if (tud_hid_ready()) {
if (mai_cfg->hid.joy) {
static uint16_t last_buttons = 0;
uint16_t buttons = button_read();
hid_joy.buttons[0] = native_to_io4(buttons);
hid_joy.buttons[1] = native_to_io4(0);
if (last_buttons ^ buttons & (1 << 11)) {
if (buttons & (1 << 11)) {
// just pressed coin button
hid_joy.chutes[0] += 0x100;
}
}
tud_hid_n_report(0, REPORT_ID_JOYSTICK, &hid_joy, sizeof(hid_joy));
last_buttons = buttons;
}
if (mai_cfg->hid.nkro) {
tud_hid_n_report(1, 0, &hid_nkro, sizeof(hid_nkro));
}
}
}
const char keymap_p1[] = BUTTON_NKRO_MAP_P1;
const char keymap_p2[] = BUTTON_NKRO_MAP_P2;
static void gen_nkro_report()
{
if (!mai_cfg->hid.nkro) {
return;
}
uint16_t buttons = button_read();
const char *keymap = (mai_cfg->hid.nkro == 2) ? keymap_p2 : keymap_p1;
for (int i = 0; i < button_num(); i++) {
uint8_t code = keymap[i];
uint8_t byte = code / 8;
uint8_t bit = code % 8;
if (buttons & (1 << i)) {
hid_nkro.keymap[byte] |= (1 << bit);
} else {
hid_nkro.keymap[byte] &= ~(1 << bit);
}
}
}
void hid_update()
{
gen_nkro_report();
report_usb_hid();
}
typedef struct __attribute__((packed)) {
uint8_t report_id;
uint8_t cmd;
uint8_t payload[62];
} hid_output_t;
void hid_proc(const uint8_t *data, uint8_t len)
{
hid_output_t *output = (hid_output_t *)data;
if (output->report_id == REPORT_ID_OUTPUT) {
switch (output->cmd) {
case 0x01: // Set Timeout
case 0x02: // Set Sampling Count
hid_joy.system_status = 0x30;
break;
case 0x03: // Clear Board Status
hid_joy.chutes[0] = 0;
hid_joy.chutes[1] = 0;
hid_joy.system_status = 0x00;
break;
case 0x04: // Set General Output
case 0x41: // I don't know what this is
break;
default:
printf("USB unknown cmd: %d\n", output->cmd);
break;
}
}
}
#include <stdint.h>
#include <stdbool.h>
#include "board_defs.h"
#include "tusb.h"
#include "usb_descriptors.h"
#include "button.h"
#include "config.h"
#include "hid.h"
struct __attribute__((packed)) {
uint16_t adcs[8];
uint16_t spinners[4];
uint16_t chutes[2];
uint16_t buttons[2];
uint8_t system_status;
uint8_t usb_status;
uint8_t padding[29];
} hid_joy;
struct __attribute__((packed)) {
uint8_t modifier;
uint8_t keymap[15];
} hid_nkro;
static uint16_t native_to_io4(uint16_t button)
{
static const int target_pos[] = { 2, 3, 0, 15, 14, 13, 12, 11, 1, 9, 6 };
uint16_t io4btn = 0;
for (int i = 0; i < 8; i++) {
bool pressed = button & (1 << i);
io4btn |= pressed ? 0 : (1 << target_pos[i]);
}
for (int i = 8; i < 11; i++) {
bool pressed = button & (1 << i);
io4btn |= pressed ? (1 << target_pos[i]) : 0;
}
return io4btn;
}
static void report_usb_hid()
{
if (tud_hid_ready()) {
if (mai_cfg->hid.joy || mai_runtime.key_stuck) {
static uint16_t last_buttons = 0;
uint16_t buttons = button_read();
hid_joy.buttons[0] = native_to_io4(buttons);
hid_joy.buttons[1] = native_to_io4(0);
if (last_buttons ^ buttons & (1 << 11)) {
if (buttons & (1 << 11)) {
// just pressed coin button
hid_joy.chutes[0] += 0x100;
}
}
tud_hid_n_report(0, REPORT_ID_JOYSTICK, &hid_joy, sizeof(hid_joy));
last_buttons = buttons;
}
if (mai_cfg->hid.nkro && !mai_runtime.key_stuck) {
tud_hid_n_report(1, 0, &hid_nkro, sizeof(hid_nkro));
}
}
}
const char keymap_p1[] = BUTTON_NKRO_MAP_P1;
const char keymap_p2[] = BUTTON_NKRO_MAP_P2;
static void gen_nkro_report()
{
if (!mai_cfg->hid.nkro) {
return;
}
uint16_t buttons = button_read();
const char *keymap = (mai_cfg->hid.nkro == 2) ? keymap_p2 : keymap_p1;
for (int i = 0; i < button_num(); i++) {
uint8_t code = keymap[i];
uint8_t byte = code / 8;
uint8_t bit = code % 8;
if (buttons & (1 << i)) {
hid_nkro.keymap[byte] |= (1 << bit);
} else {
hid_nkro.keymap[byte] &= ~(1 << bit);
}
}
}
void hid_update()
{
gen_nkro_report();
report_usb_hid();
}
typedef struct __attribute__((packed)) {
uint8_t report_id;
uint8_t cmd;
uint8_t payload[62];
} hid_output_t;
void hid_proc(const uint8_t *data, uint8_t len)
{
hid_output_t *output = (hid_output_t *)data;
if (output->report_id == REPORT_ID_OUTPUT) {
switch (output->cmd) {
case 0x01: // Set Timeout
case 0x02: // Set Sampling Count
hid_joy.system_status = 0x30;
break;
case 0x03: // Clear Board Status
hid_joy.chutes[0] = 0;
hid_joy.chutes[1] = 0;
hid_joy.system_status = 0x00;
break;
case 0x04: // Set General Output
case 0x41: // I don't know what this is
break;
default:
printf("USB unknown cmd: %d\n", output->cmd);
break;
}
}
}

View File

@ -142,6 +142,8 @@ void init()
cli_init("mai_pico>", "\n << Mai Pico Controller >>\n"
" https://github.com/whowechina\n\n");
commands_init();
mai_runtime.key_stuck = button_is_stuck();
}
int main(void)