Just playable now

This commit is contained in:
whowechina 2023-09-30 20:06:35 +08:00
parent fec2f5e6f7
commit aef8ce1931
11 changed files with 465 additions and 130 deletions

View File

@ -4,8 +4,8 @@ set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip)
function(make_firmware board board_def)
pico_sdk_init()
add_executable(${board}
main.c touch.c rgb.c save.c config.c cli.c commands.c mpr121.c
usb_descriptors.c)
main.c touch.c button.c rgb.c save.c config.c cli.c commands.c io.c
mpr121.c usb_descriptors.c)
target_compile_definitions(${board} PUBLIC ${board_def})
pico_enable_stdio_usb(${board} 1)
pico_enable_stdio_uart(${board} 0)

View File

@ -12,8 +12,14 @@
#define RGB_PIN 13
#define RGB_ORDER GRB // or RGB
#define RGB_BUTTON_MAP { 5, 4, 3, 2, 1, 0, 7, 6 }
#define NKRO_KEYMAP "1aqz2swx3dec4frv5gtb6hyn7jum8ki90olp,."
#define BUTTON_DEF { 1, 0, 4, 5, 8, 9, 3, 2 }
#define BUTTON_NKRO_MAP "WEDCXZAQ"
#define TOUCH_MAP { E3, A2, B2, D2, E2, A1, B1, D1, E1, C2, A8, B8, \
D8, E8, A7, B7, D7, E7, A6, B6, D6, E6, A5, B5, \
D5, E5, C1, A4, B4, D4, E4, A3, B3, D3 }
#else
#endif

76
firmware/src/button.c Normal file
View File

@ -0,0 +1,76 @@
/*
* 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 button_gpio[] = BUTTON_DEF;
#define BUTTON_NUM (sizeof(button_gpio))
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;
int8_t gpio = button_gpio[i];
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;
}
static uint16_t button_reading;
/* If a switch flips, it freezes for a while */
#define DEBOUNCE_FREEZE_TIME_US 1000
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(button_gpio[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;
}

18
firmware/src/button.h Normal file
View File

@ -0,0 +1,18 @@
/*
* 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();
#endif

260
firmware/src/io.c Normal file
View File

@ -0,0 +1,260 @@
#include <stdint.h>
#include <stdbool.h>
#include "io.h"
#include "tusb.h"
#include "usb_descriptors.h"
#include "touch.h"
#include "rgb.h"
static struct {
bool stat;
uint64_t last_io_time;
int touch_interface;
} ctx = { false, 0, 0 };
typedef union {
uint8_t raw[28];
struct {
uint8_t body;
uint8_t ext;
uint8_t side;
};
struct {
uint8_t index;
uint8_t r;
uint8_t g;
uint8_t b;
};
struct {
uint8_t start;
uint8_t len;
uint8_t skip;
uint8_t mr;
uint8_t mg;
uint8_t mb;
uint8_t speed;
};
uint8_t rgb[11][3];
} led_data_t;
typedef struct {
int interface;
bool connected;
bool in_cmd;
bool is_touch;
bool escape;
union {
uint8_t buf[48];
struct {
struct {
uint8_t dst;
uint8_t src;
uint8_t len;
uint8_t cmd;
} hdr;
led_data_t data;
};
};
uint8_t len;
uint8_t checksum;
} cdc_t;
static cdc_t cdc[2] = {
{ .interface = 1 },
{ .interface = 2 },
};
static void touch_cmd(cdc_t *cdc)
{
cdc->in_cmd = false;
if (cdc->len != 4) {
return;
}
cdc->len = 0;
ctx.last_io_time = time_us_64();
ctx.touch_interface = cdc->interface;
printf("Touch CMD: %*s -> ", 4, cdc->buf);
switch (cdc->buf[2]) {
case 'E':
printf("RSET\n");
break;
case 'L':
printf("HALT\n");
ctx.stat = false;
break;
case 'A':
printf("STAT\n");
ctx.stat = true;
break;
case 'r':
printf("Ratio\n");
tud_cdc_n_write_char(cdc->interface, '(');
tud_cdc_n_write_char(cdc->interface, cdc->buf[0]); //L,R
tud_cdc_n_write_char(cdc->interface, cdc->buf[1]); //sensor
tud_cdc_n_write_char(cdc->interface, 'r');
tud_cdc_n_write_char(cdc->interface, cdc->buf[3]); // Ratio
tud_cdc_n_write_char(cdc->interface, ')');
tud_cdc_n_write_flush(cdc->interface);
break;
case 'k':
printf("Sense\n");
tud_cdc_n_write_char(cdc->interface, '(');
tud_cdc_n_write_char(cdc->interface, cdc->buf[0]); //L,R
tud_cdc_n_write_char(cdc->interface, cdc->buf[1]); //sensor
tud_cdc_n_write_char(cdc->interface, 'k');
tud_cdc_n_write_char(cdc->interface, cdc->buf[3]); // Ratio
tud_cdc_n_write_char(cdc->interface, ')');
tud_cdc_n_write_flush(cdc->interface);
break;
default:
printf("Unknown\n");
return;
}
}
static void led_cmd(cdc_t *cdc)
{
cdc->in_cmd = false;
cdc->len = 0;
ctx.last_io_time = time_us_64();
switch (cdc->hdr.cmd) {
case 0x31:
printf("8b\n");
uint32_t color = rgb32(cdc->data.r, cdc->data.g, cdc->data.b, false);
rgb_set_button_color(cdc->data.index, color);
break;
case 0x32:
printf("8bM\n");
for (int i = 0; i < cdc->data.len; i++) {
rgb_set_button_color(i + cdc->data.start, rgb32(cdc->data.mr, cdc->data.mg, cdc->data.mb, false));
}
break;
case 0x33:
printf("8bMF\n");
for (int i = 0; i < cdc->data.len; i++) {
rgb_set_button_color(i + cdc->data.start, rgb32(cdc->data.mr, cdc->data.mg, cdc->data.mb, false));
}
break;
case 0x39:
printf("Fet\n");
rgb_set_cab_color(0, rgb32(cdc->data.body, cdc->data.body, cdc->data.body, false));
rgb_set_cab_color(1, rgb32(cdc->data.ext, cdc->data.ext, cdc->data.ext, false));
rgb_set_cab_color(2, rgb32(cdc->data.side, cdc->data.side, cdc->data.side, false));
break;
case 0x3C:
printf("Upd\n");
break;
case 0x82:
printf("Dir\n");
break;
default:
printf("LEDUnk\n");
return;
}
}
static inline void assemble_cmd(cdc_t *cdc, uint8_t c)
{
if (c == 0xE0) {
cdc->len = 0;
cdc->in_cmd = true;
cdc->is_touch = false;
cdc->escape = false;
cdc->checksum = 0;
return;
}
if ((!cdc->in_cmd || cdc->is_touch) && (c == '{')) {
cdc->len = 0;
cdc->in_cmd = true;
cdc->is_touch = true;
return;
}
if (cdc->is_touch) {
// Touch cmd
if (c == '}') {
touch_cmd(cdc);
} else if (cdc->len < sizeof(cdc->buf)) {
cdc->buf[cdc->len] = c;
cdc->len++;
}
return;
}
// LED cmd
if (c == 0xD0) {
cdc->escape = true;
return;
}
if (cdc->escape) {
cdc->escape = false;
c++;
}
if ((cdc->len == cdc->hdr.len + 3) && (cdc->checksum == c)) {
led_cmd(cdc);
return;
}
if (cdc->len < sizeof(cdc->buf)) {
cdc->buf[cdc->len] = c;
cdc->len++;
cdc->checksum += c;
}
}
static void update_itf(cdc_t *cdc)
{
cdc->connected = tud_cdc_n_connected(cdc->interface);
if (tud_cdc_n_available(cdc->interface)) {
uint8_t buf[48];
uint32_t count = tud_cdc_n_read(cdc->interface, buf, sizeof(buf));
for (int i = 0; i < count; i++) {
assemble_cmd(cdc, buf[i]);
}
}
}
static void send_touch()
{
if ((ctx.touch_interface == 0) | (!ctx.stat)) {
return;
}
static uint32_t last_sent_time = 0;
uint32_t now = time_us_32();
if (now - last_sent_time < 5000) {
return;
}
last_sent_time = now;
uint8_t report[9] = "(\0\0\0\0\0\0\0)";
uint64_t touch = touch_touchmap();
for (int i = 0; i < 7; i++) {
report[i + 1] = touch & 0x1f;
touch >>= 5;
}
tud_cdc_n_write(ctx.touch_interface, report, sizeof(report));
tud_cdc_n_write_flush(ctx.touch_interface);
}
void io_update()
{
update_itf(cdc);
update_itf(cdc + 1);
send_touch();
}
uint64_t io_last_io_time()
{
return ctx.last_io_time;
}

12
firmware/src/io.h Normal file
View File

@ -0,0 +1,12 @@
/*
* Game I/O
* WHowe <github.com/whowechina>
*/
#ifndef IO_H_
#define IO_H_
void io_update();
uint64_t io_last_io_time();
#endif

View File

@ -24,12 +24,14 @@
#include "board_defs.h"
#include "touch.h"
#include "button.h"
#include "rgb.h"
#include "save.h"
#include "config.h"
#include "cli.h"
#include "commands.h"
#include "rgb.h"
#include "io.h"
struct __attribute__((packed)) {
uint16_t buttons; // 16 buttons; see JoystickButtons_t for bit mapping
@ -48,9 +50,6 @@ void report_usb_hid()
if (tud_hid_ready()) {
hid_joy.HAT = 0;
hid_joy.VendorSpec = 0;
if (mai_cfg->hid.joy) {
tud_hid_n_report(0x00, REPORT_ID_JOYSTICK, &hid_joy, sizeof(hid_joy));
}
if (mai_cfg->hid.nkro &&
(memcmp(&hid_nkro, &sent_hid_nkro, sizeof(hid_nkro)) != 0)) {
sent_hid_nkro = hid_nkro;
@ -59,40 +58,16 @@ void report_usb_hid()
}
}
static void gen_joy_report()
{
hid_joy.axis = 0;
for (int i = 0; i < 16; i++) {
if (touch_touched(i * 2)) {
hid_joy.axis |= 1 << (30 - i * 2);
}
if (touch_touched(i * 2 + 1)) {
hid_joy.axis |= 1 << (31 - i * 2);
}
}
hid_joy.axis ^= 0x80808080; // some magic number from CrazyRedMachine
}
const uint8_t keycode_table[128][2] = { HID_ASCII_TO_KEYCODE };
const char keymap[38 + 1] = NKRO_KEYMAP; // 32 keys, 6 air keys, 1 terminator
const char keymap[8] = BUTTON_NKRO_MAP; // 8 buttons
static void gen_nkro_report()
{
for (int i = 0; i < 32; i++) {
uint16_t buttons = button_read();
for (int i = 0; i < 8; i++) {
uint8_t code = keycode_table[keymap[i]][1];
uint8_t byte = code / 8;
uint8_t bit = code % 8;
if (touch_touched(i)) {
hid_nkro.keymap[byte] |= (1 << bit);
} else {
hid_nkro.keymap[byte] &= ~(1 << bit);
}
}
for (int i = 0; i < 6; i++) {
uint8_t code = keycode_table[keymap[32 + i]][1];
uint8_t byte = code / 8;
uint8_t bit = code % 8;
if (hid_joy.buttons & (1 << i)) {
if (buttons & (1 << i)) {
hid_nkro.keymap[byte] |= (1 << bit);
} else {
hid_nkro.keymap[byte] &= ~(1 << bit);
@ -109,68 +84,21 @@ static void run_lights()
return;
}
uint32_t colors[5] = { 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xffffff };
if (now - io_last_io_time() < 5000000) {
return;
}
uint16_t buttons = button_read();
uint16_t touch = touch_touchmap();
for (int i = 0; i < 8; i++) {
rgb_set_color(i, 0);
}
for (int i = 0; i < 34; i++) {
if (touch_touched(i)) {
if (i < 32) {
rgb_set_color((i + 16) / 4 % 8, colors[i % 4]);
} else {
rgb_set_color(i - 32, colors[4]);
}
}
}
// for (int i = 0; i < 15; i++) {
// uint32_t color = rgb32_from_hsv(i * 255 / 8, 255, 16);
// rgb_set_color(i, color);
// }
for (int i = 0; i < 34; i++) {
// bool r = touch_touched(i * 2);
// bool g = touch_touched(i * 2 + 1);
// rgb_set_color(30 - i * 2, rgb32(r ? 80 : 0, g ? 80 : 0, 0, false));
}
}
static void echo_serial_port(uint8_t itf, uint8_t buf[], uint32_t count)
{
//tud_cdc_n_write_char(itf, buf[i]);
//tud_cdc_n_write_flush(itf);
}
static void cdc_task(void)
{
uint8_t itf;
for (itf = 1; itf < CFG_TUD_CDC; itf++)
{
// connected() check for DTR bit
// Most but not all terminal client set this when making connection
// if ( tud_cdc_n_connected(itf) )
if ( tud_cdc_n_available(itf) )
{
uint8_t buf[64];
uint32_t count = tud_cdc_n_read(itf, buf, sizeof(buf));
if (itf == 1) {
printf("1 TXT:", itf);
for (int i = 0; i < count; i++) {
printf("%c", buf[i]);
}
printf("\n");
} else if (itf == 2) {
printf("2 HEX:", itf);
for (int i = 0; i < count; i++) {
printf(" %02x", buf[i]);
}
printf("\n");
uint32_t color = 0;
if (buttons & (1 << i)) {
color |= 0x00ff00;
}
if (touch & (1 << i)) {
color |= 0xff0000;
}
rgb_set_button_color(i, color);
}
}
@ -192,15 +120,15 @@ static void core0_loop()
{
while(1) {
tud_task();
cdc_task();
io_update();
cli_run();
save_loop();
cli_fps_count(0);
touch_update();
button_update();
gen_joy_report();
gen_nkro_report();
report_usb_hid();
}
@ -219,6 +147,7 @@ void init()
save_init(0xca34cafe, &core1_io_lock);
touch_init();
button_init();
rgb_init();
cli_init("mai_pico>", "\n << Mai Pico Controller >>\n"

View File

@ -23,6 +23,7 @@
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static uint32_t rgb_buf[20];
static const uint8_t button_led_map[] = RGB_BUTTON_MAP;
#define _MAP_LED(x) _MAKE_MAPPER(x)
#define _MAKE_MAPPER(x) MAP_LED_##x
@ -121,28 +122,20 @@ static inline uint32_t apply_level(uint32_t color)
return r << 16 | g << 8 | b;
}
void rgb_set_color(unsigned index, uint32_t color)
void rgb_set_button_color(unsigned index, uint32_t color)
{
if (index >= ARRAY_SIZE(rgb_buf)) {
if (index >= 8) {
return;
}
rgb_buf[index] = apply_level(color);
rgb_buf[button_led_map[index]] = apply_level(color);
}
void rgb_set_brg(unsigned index, const uint8_t *brg_array, size_t num)
void rgb_set_cab_color(unsigned index, uint32_t color)
{
if (index >= ARRAY_SIZE(rgb_buf)) {
if (index >= 0) {
return;
}
if (index + num > ARRAY_SIZE(rgb_buf)) {
num = ARRAY_SIZE(rgb_buf) - index;
}
for (int i = 0; i < num; i++) {
uint8_t b = brg_array[i * 3 + 0];
uint8_t r = brg_array[i * 3 + 1];
uint8_t g = brg_array[i * 3 + 2];
rgb_buf[index + i] = apply_level(rgb32(r, g, b, false));
}
rgb_buf[8 + index] = apply_level(color);
}
void rgb_init()

View File

@ -18,8 +18,8 @@ void rgb_update();
uint32_t rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix);
uint32_t rgb32_from_hsv(uint8_t h, uint8_t s, uint8_t v);
void rgb_set_colors(const uint32_t *colors, unsigned index, size_t num);
void rgb_set_color(unsigned index, uint32_t color);
void rgb_set_button_color(unsigned index, uint32_t color);
void rgb_set_cab_color(unsigned index, uint32_t color);
/* num of the rgb leds, num*3 bytes in the array */
void rgb_set_brg(unsigned index, const uint8_t *brg_array, size_t num);

View File

@ -22,10 +22,18 @@
#define MPR121_ADDR 0x5A
static uint16_t readout[36];
static uint16_t touch[3];
static unsigned touch_counts[36];
enum touch_pads {
A1 = 0, A2, A3, A4, A5, A6, A7, A8,
B1, B2, B3, B4, B5, B6, B7, B8,
C1, C2, D1, D2, D3, D4, D5, D6, D7, D8,
E1, E2, E3, E4, E5, E6, E7, E8,
};
static unsigned touch_map[] = TOUCH_MAP;
void touch_init()
{
i2c_init(I2C_PORT, I2C_FREQ);
@ -40,30 +48,57 @@ void touch_init()
touch_update_config();
}
void touch_update()
static uint64_t touch_reading;
static void remap_reading()
{
static uint16_t last_touched[3];
touch[0] = mpr121_touched(MPR121_ADDR);
touch[1] = mpr121_touched(MPR121_ADDR + 1);
touch[2] = mpr121_touched(MPR121_ADDR + 2);
uint64_t map = 0;
for (int m = 0; m < 3; m++) {
uint16_t just_touched = touch[m] & ~last_touched[m];
last_touched[m] = touch[m];
for (int i = 0; i < 12; i++) {
if (just_touched & (1 << i)) {
touch_counts[m * 12 + i]++;
if (touch[m] & (1 << i)) {
map |= 1ULL << touch_map[m * 12 + i];
}
}
}
touch_reading = map;
}
static void touch_stat()
{
static uint64_t last_reading;
uint64_t just_touched = touch_reading & ~last_reading;
last_reading = touch_reading;
for (int i = 0; i < 34; i++) {
if (just_touched & (1ULL << i)) {
touch_counts[i]++;
}
}
}
void touch_update()
{
touch[0] = mpr121_touched(MPR121_ADDR) & 0x0fff;
touch[1] = mpr121_touched(MPR121_ADDR + 1) & 0x0fff;
touch[2] = mpr121_touched(MPR121_ADDR + 2) & 0x0fff;
remap_reading();
touch_stat();
}
const uint16_t *touch_raw()
{
mpr121_raw(MPR121_ADDR, readout, 12);
mpr121_raw(MPR121_ADDR + 1, readout + 12, 12);
mpr121_raw(MPR121_ADDR + 2, readout + 24, 12);
static uint16_t readout[36];
uint16_t buf[36];
mpr121_raw(MPR121_ADDR, buf, 12);
mpr121_raw(MPR121_ADDR + 1, buf + 12, 12);
mpr121_raw(MPR121_ADDR + 2, buf + 24, 10);
for (int i = 0; i < 34; i++) {
readout[touch_map[i]] = buf[i];
}
return readout;
}
@ -72,7 +107,12 @@ bool touch_touched(unsigned key)
if (key >= 34) {
return 0;
}
return touch[key / 12] & (1 << (key % 12));
return touch_reading & (1ULL << key);
}
uint64_t touch_touchmap()
{
return touch_reading;
}
unsigned touch_count(unsigned key)

View File

@ -12,10 +12,11 @@
void touch_init();
void touch_update();
bool touch_touched(unsigned key);
uint64_t touch_touchmap();
const uint16_t *touch_raw();
void touch_update_config();
unsigned touch_count(unsigned key);
void touch_reset_stat();
#endif