Arcade light support

This commit is contained in:
whowechina
2025-12-15 20:43:35 +08:00
parent 6dd96e7a26
commit b023ea0d6d
12 changed files with 365 additions and 89 deletions
Binary file not shown.
+1 -1
View File
@@ -5,7 +5,7 @@ function(make_firmware board board_def)
pico_sdk_init()
add_executable(${board}
main.c slider.c rgb.c button.c save.c config.c commands.c
cli.c mpr121.c hebtn.c usb_descriptors.c)
cli.c mpr121.c hebtn.c lzfx.c usb_descriptors.c)
target_compile_definitions(${board} PUBLIC ${board_def})
pico_enable_stdio_usb(${board} 1)
pico_enable_stdio_uart(${board} 0)
+169
View File
@@ -0,0 +1,169 @@
/*
* Lzfx decompressor
* WHowe <github.com/whowechina>
* This is actually taken from CrazyRedMachine's repo
* <https://github.com/CrazyRedMachine/RedBoard/blob/main/io_dll/src/utils/hid_impl.c>
*/
#include <stdlib.h>
#include "lzfx.h"
typedef unsigned char u8;
typedef const u8 *LZSTATE[LZFX_HSIZE];
/* Guess len. No parameters may be NULL; this is not checked. */
static int lzfx_getsize(const void *ibuf, unsigned int ilen, unsigned int *olen)
{
u8 const *ip = (const u8 *)ibuf;
u8 const *const in_end = ip + ilen;
int tot_len = 0;
while (ip < in_end)
{
unsigned int ctrl = *ip++;
if (ctrl < (1 << 5))
{
ctrl++;
if (ip + ctrl > in_end)
return LZFX_ECORRUPT;
tot_len += ctrl;
ip += ctrl;
}
else
{
unsigned int len = (ctrl >> 5);
if (len == 7)
{ /* i.e. format #2 */
len += *ip++;
}
len += 2; /* len is now #octets */
if (ip >= in_end)
return LZFX_ECORRUPT;
ip++; /* skip the ref byte */
tot_len += len;
}
}
*olen = tot_len;
return 0;
}
/* Decompressor */
int lzfx_decompress(const void *ibuf, unsigned int ilen,
void *obuf, unsigned int *olen)
{
u8 const *ip = (const u8 *)ibuf;
u8 const *const in_end = ip + ilen;
u8 *op = (u8 *)obuf;
u8 const *const out_end = (olen == NULL ? NULL : op + *olen);
unsigned int remain_len = 0;
int rc;
if (olen == NULL)
return LZFX_EARGS;
if (ibuf == NULL)
{
if (ilen != 0)
return LZFX_EARGS;
*olen = 0;
return 0;
}
if (obuf == NULL)
{
if (olen != 0)
return LZFX_EARGS;
return lzfx_getsize(ibuf, ilen, olen);
}
do
{
unsigned int ctrl = *ip++;
/* Format 000LLLLL: a literal byte string follows, of length L+1 */
if (ctrl < (1 << 5))
{
ctrl++;
if (fx_expect_false(op + ctrl > out_end))
{
--ip; /* Rewind to control byte */
goto guess;
}
if (fx_expect_false(ip + ctrl > in_end))
return LZFX_ECORRUPT;
do
*op++ = *ip++;
while (--ctrl);
/* Format #1 [LLLooooo oooooooo]: backref of length L+1+2
^^^^^ ^^^^^^^^
A B
#2 [111ooooo LLLLLLLL oooooooo] backref of length L+7+2
^^^^^ ^^^^^^^^
A B
In both cases the location of the backref is computed from the
remaining part of the data as follows:
location = op - A*256 - B - 1
*/
}
else
{
unsigned int len = (ctrl >> 5);
u8 *ref = op - ((ctrl & 0x1f) << 8) - 1;
if (len == 7)
len += *ip++; /* i.e. format #2 */
len += 2; /* len is now #octets */
if (fx_expect_false(op + len > out_end))
{
ip -= (len >= 9) ? 2 : 1; /* Rewind to control byte */
goto guess;
}
if (fx_expect_false(ip >= in_end))
return LZFX_ECORRUPT;
ref -= *ip++;
if (fx_expect_false(ref < (u8 *)obuf))
return LZFX_ECORRUPT;
do
*op++ = *ref++;
while (--len);
}
} while (ip < in_end);
*olen = op - (u8 *)obuf;
return 0;
guess:
rc = lzfx_getsize(ip, ilen - (ip - (u8 *)ibuf), &remain_len);
if (rc >= 0)
*olen = remain_len + (op - (u8 *)obuf);
return rc;
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Lzfx decompressor
* WHowe <github.com/whowechina>
* This is actually taken from CrazyRedMachine's repo
* <https://github.com/CrazyRedMachine/RedBoard/blob/main/io_dll/src/utils/hid_impl.c>
*/
#define LZFX_ESIZE -1 /* Output buffer too small */
#define LZFX_ECORRUPT -2 /* Invalid data for decompression */
#define LZFX_EARGS -3 /* Arguments invalid (NULL) */
#define LZFX_HLOG 16
#define LZFX_HSIZE (1 << (LZFX_HLOG))
#define fx_expect_false(expr) (expr)
#define fx_expect_true(expr) (expr)
int lzfx_decompress(const void* ibuf, unsigned int ilen,
void* obuf, unsigned int *olen);
+41 -18
View File
@@ -33,6 +33,7 @@
#include "rgb.h"
#include "button.h"
#include "hebtn.h"
#include "lzfx.h"
struct __attribute__((packed)) {
uint16_t buttons; // 16 buttons; see JoystickButtons_t for bit mapping
@@ -101,12 +102,8 @@ static void gen_joy_report()
}
static uint64_t last_hid_time = 0;
static bool motor_running = false;
static void run_lights()
{
uint64_t now = time_us_64();
uint32_t button_colors[] = {
rgb32(0, 0xff, 0, false),
rgb32(0xe0, 0x10, 0xe0, false),
@@ -119,19 +116,27 @@ static void run_lights()
for (int i = 0; i < 5; i++) {
bool pressed = buttons & (1 << i);
uint32_t color = pressed ? button_colors[i] : 0x505050;
if (motor_running && (now / 50000 % 2 == 0)) {
color = 0;
}
rgb_button_color(i, color);
}
uint64_t now = time_us_64();
static int rainbow_level = 0;
int zone_num = slider_zone_num();
uint32_t phase = ((now >> 10) / zone_num) & 0xff;
if (now - last_hid_time >= 1000000) {
for (int i = 0; i < zone_num; i++) {
uint32_t color = rgb32_from_hsv(i * 256 / zone_num + phase, 255, 96);
rgb_slider_color(i, slider_touched(i) ? 0xffffff: color);
}
uint32_t touch_bits = slider_touch_bits();
if ((touch_bits) && (rainbow_level > 80)) {
rainbow_level -= (rainbow_level > 168) ? 2 : 1;
}
if ((!touch_bits) && (rainbow_level < 256)) {
rainbow_level += (rainbow_level < 168) ? 2 : 1;
}
for (int i = 0; i < zone_num; i++) {
uint32_t color = rgb32_from_hsv(i * 256 / zone_num + phase, 255, rainbow_level / 2);
rgb_slider_color(i, touch_bits & (1 << i) ? 0xffffff: color);
}
}
@@ -271,12 +276,30 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id,
hid_report_type_t report_type, uint8_t const *buffer,
uint16_t bufsize)
{
if (bufsize > 1) {
motor_running = !motor_running;
}
if (report_type == HID_REPORT_TYPE_OUTPUT) {
last_hid_time = time_us_64();
return;
}
static uint8_t obuf[48];
memcpy(obuf, buffer, bufsize);
if (report_id == REPORT_ID_LED_SLIDER_1) {
rgb_set_hid_slider(0, 16, obuf);
} else if (report_id == REPORT_ID_LED_SLIDER_2) {
rgb_set_hid_slider(16, 16, obuf);
} else if (report_id == REPORT_ID_LED_BUTTON) {
rgb_set_hid_button(obuf);
}
} else if (report_type == HID_REPORT_TYPE_FEATURE) {
if (report_id == REPORT_ID_LED_COMPRESSED) {
static uint8_t fbuf[64];
memcpy(fbuf, buffer, bufsize);
static uint8_t decomp[100];
unsigned int olen = sizeof(decomp);
if (lzfx_decompress(fbuf + 1, fbuf[0], decomp, &olen) != 0) {
return;
}
if (olen < 96) {
return;
}
rgb_set_hid_slider(0, 32, decomp);
}
}
}
+63 -11
View File
@@ -20,9 +20,13 @@
#include "board_defs.h"
#include "config.h"
static uint32_t rgb_buf[37]; // 4(Main buttons) + 1(Start button) + 32(Max Slider)
#define HID_LIGHT_TIMEOUT_MS 1000
static int8_t button_rgb[] = RGB_BUTTON_MAP;
static uint32_t main_buf[37]; // 4(Main buttons) + 1(Start button) + 32(Max Slider)
static uint32_t hid_buf[37];
static uint64_t hid_timeout = 0;
static int8_t button_rgb_map[] = RGB_BUTTON_MAP;
#define _MAP_LED(x) _MAKE_MAPPER(x)
#define _MAKE_MAPPER(x) MAP_LED_##x
@@ -92,20 +96,23 @@ static void drive_led()
}
last = now;
for (int i = 0; i < count_of(rgb_buf); i++) {
pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u);
bool use_hid = (hid_timeout > 0) && (now < hid_timeout);
const uint32_t *buf = use_hid ? hid_buf : main_buf;
for (int i = 0; i < count_of(main_buf); i++) {
pio_sm_put_blocking(pio0, 0, buf[i] << 8u);
}
}
void rgb_set_colors(const uint32_t *colors, unsigned index, size_t num)
{
if (index >= count_of(rgb_buf)) {
if (index >= count_of(main_buf)) {
return;
}
if (index + num > count_of(rgb_buf)) {
num = count_of(rgb_buf) - index;
if (index + num > count_of(main_buf)) {
num = count_of(main_buf) - index;
}
memcpy(&rgb_buf[index], colors, num * sizeof(*colors));
memcpy(&main_buf[index], colors, num * sizeof(*colors));
}
static inline uint32_t apply_level(uint32_t color, uint8_t level)
@@ -123,8 +130,8 @@ static inline uint32_t apply_level(uint32_t color, uint8_t level)
void rgb_button_color(unsigned index, uint32_t color)
{
if (index < count_of(button_rgb)) {
rgb_buf[button_rgb[index]] = apply_level(color, diva_cfg->light.level.button);
if (index < count_of(button_rgb_map)) {
main_buf[button_rgb_map[index]] = apply_level(color, diva_cfg->light.level.button);
}
}
@@ -133,7 +140,51 @@ void rgb_slider_color(unsigned index, uint32_t color)
if (index > 32) {
return;
}
rgb_buf[5 + index] = apply_level(color, diva_cfg->light.level.slider);
main_buf[5 + index] = apply_level(color, diva_cfg->light.level.slider);
}
static inline void update_hid_timeout()
{
hid_timeout = time_us_64() + HID_LIGHT_TIMEOUT_MS * 1000;
}
static inline uint32_t led_rgb(const uint8_t *grb, int index)
{
const uint8_t *src = grb + index * 3;
return rgb32(src[1], src[0], src[2], false);
}
void rgb_set_hid_slider(unsigned index, unsigned num, const uint8_t *grb)
{
for (int i = 0; i < num; i++) {
if (index + i >= 32) {
return;
}
uint32_t *dest = &hid_buf[5 + index + i];
*dest = apply_level(led_rgb(grb, i), diva_cfg->light.level.slider);
}
update_hid_timeout();
}
void rgb_set_hid_button(const uint8_t *scale)
{
uint32_t button_colors[] = {
rgb32(0, 0xff, 0, false),
rgb32(0xe0, 0x10, 0xe0, false),
rgb32(0, 0, 0xff, false),
rgb32(0xff, 0, 0, false),
};
for (int i = 0; i < 4; i++) {
int button_led = button_rgb_map[i];
uint32_t color = apply_level(button_colors[i], scale[i]);
hid_buf[button_led] = apply_level(color, diva_cfg->light.level.button);
}
hid_buf[4] = main_buf[4]; // HID doesn't have start button light
update_hid_timeout();
}
void rgb_init()
@@ -144,6 +195,7 @@ void rgb_init()
ws2812_program_init(pio0, 0, pio0_offset, RGB_PIN, 800000, false);
}
void rgb_update()
{
drive_led();
+3
View File
@@ -22,4 +22,7 @@ void rgb_set_colors(const uint32_t *colors, unsigned index, size_t num);
void rgb_button_color(unsigned index, uint32_t color);
void rgb_slider_color(unsigned index, uint32_t color);
void rgb_set_hid_slider(unsigned index, unsigned num, const uint8_t *grb);
void rgb_set_hid_button(const uint8_t *scale);
#endif
+5
View File
@@ -102,6 +102,11 @@ bool slider_touched(unsigned zone)
return touch_bits & (1 << zone);
}
uint32_t slider_touch_bits()
{
return touch_bits;
}
unsigned slider_count(unsigned zone)
{
if (zone >= zone_num) {
+2
View File
@@ -13,6 +13,8 @@ void slider_init();
void slider_update();
uint16_t slider_zone_num();
bool slider_touched(unsigned zone);
uint32_t slider_touch_bits();
const uint16_t *slider_raw();
void slider_update_config();
unsigned slider_count(unsigned zone);
-3
View File
@@ -102,9 +102,6 @@ extern "C" {
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
// HID buffer size Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_EP_BUFSIZE 64
#define CFG_TUD_VENDOR 0
// HID buffer size Should be sufficient to hold ID (if any) + Data
+15 -24
View File
@@ -78,8 +78,13 @@ uint8_t const desc_hid_report_joy[] = {
DIVAPICO_REPORT_DESC_JOYSTICK,
};
uint8_t const desc_hid_report_nkro[] = {
DIVAPICO_REPORT_DESC_NKRO,
uint8_t const desc_hid_report_led[] = {
DIVAPICO_LED_HEADER,
DIVAPICO_REPORT_DESC_LED_SLIDER_1,
DIVAPICO_REPORT_DESC_LED_SLIDER_2,
DIVAPICO_REPORT_DESC_LED_BUTTON,
DIVAPICO_REPORT_DESC_LED_COMPRESSED,
DIVAPICO_LED_FOOTER
};
// Invoked when received GET HID REPORT DESCRIPTOR
@@ -91,7 +96,7 @@ uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf)
case 0:
return desc_hid_report_joy;
case 1:
return desc_hid_report_nkro;
return desc_hid_report_led;
default:
return NULL;
}
@@ -100,7 +105,7 @@ uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf)
// Configuration Descriptor
//--------------------------------------------------------------------+
enum { ITF_NUM_JOY, ITF_NUM_NKRO,
enum { ITF_NUM_JOY, ITF_NUM_LED,
ITF_NUM_CLI, ITF_NUM_CLI_DATA,
ITF_NUM_TOTAL };
@@ -112,8 +117,7 @@ enum { ITF_NUM_JOY, ITF_NUM_NKRO,
#define EPNUM_JOY_OUT 0x01
#define EPNUM_JOY_IN 0x81
//#define EPNUM_LED 0x86
#define EPNUM_KEY 0x87
#define EPNUM_LED 0x86
#define EPNUM_CLI_NOTIF 0x89
#define EPNUM_CLI_OUT 0x0a
@@ -131,9 +135,9 @@ uint8_t const desc_configuration_joy[] = {
sizeof(desc_hid_report_joy), EPNUM_JOY_OUT, EPNUM_JOY_IN,
CFG_TUD_HID_EP_BUFSIZE, 1),
TUD_HID_DESCRIPTOR(ITF_NUM_NKRO, 5, HID_ITF_PROTOCOL_NONE,
sizeof(desc_hid_report_nkro), EPNUM_KEY,
CFG_TUD_HID_EP_BUFSIZE, 1),
TUD_HID_DESCRIPTOR(ITF_NUM_LED, 5, HID_ITF_PROTOCOL_NONE,
sizeof(desc_hid_report_led), EPNUM_LED,
CFG_TUD_HID_EP_BUFSIZE, 4),
TUD_CDC_DESCRIPTOR(ITF_NUM_CLI, 6, EPNUM_CLI_NOTIF,
8, EPNUM_CLI_OUT, EPNUM_CLI_IN, 64),
@@ -157,7 +161,7 @@ const char *string_desc_arr[] = {
"Diva Pico Controller", // 2: Product
"123456", // 3: Serials, should use chip ID
"Diva Pico Joystick",
"Diva Pico NKRO",
"Diva Pico LED",
"Diva Pico CLI Port",
};
@@ -175,25 +179,12 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
}
const size_t base_num = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]);
const char *colors[] = {"Blue", "Red", "Green"};
char str[64];
if (index < base_num) {
strcpy(str, string_desc_arr[index]);
} else if (index < base_num + 48 + 45) {
const char *names[] = {"Key ", "Splitter "};
int led = index - base_num;
int id = led / 6 + 1;
int type = led / 3 % 2;
int brg = led % 3;
sprintf(str, "%s%02d %s", names[type], id, colors[brg]);
} else if (index < base_num + 48 + 45 + 18) {
int led = index - base_num - 48 - 45;
int id = led / 3 + 1;
int brg = led % 3;
sprintf(str, "Tower %02d %s", id, colors[brg]);
} else {
sprintf(str, "Unknown %d", index);
sprintf(str, "B%02d", index - base_num);
}
uint8_t chr_count = strlen(str);
+48 -32
View File
@@ -6,6 +6,10 @@
enum {
REPORT_ID_JOYSTICK = 1,
REPORT_ID_LED_SLIDER_1 = 4,
REPORT_ID_LED_SLIDER_2 = 5,
REPORT_ID_LED_BUTTON = 6,
REPORT_ID_LED_COMPRESSED = 11,
};
// because they are missing from tusb_hid.h
@@ -58,40 +62,52 @@ enum {
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
#define DIVAPICO_REPORT_DESC_NKRO \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_KEYBOARD), \
#define DIVAPICO_LED_HEADER \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
/* Modifier */ \
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_DATA | HID_VARIABLE | HID_ABSOLUTE), \
/* LED output that we don't care */ \
HID_REPORT_COUNT(5), \
HID_REPORT_SIZE(1), \
HID_USAGE_PAGE(HID_USAGE_PAGE_LED), \
HID_USAGE_MIN(1), \
HID_USAGE_MAX(5), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_REPORT_COUNT(1), \
HID_REPORT_SIZE(3), \
HID_OUTPUT(HID_CONSTANT), \
/* Full Keyboard Bitmap */ \
HID_REPORT_SIZE(1), \
HID_REPORT_COUNT(120), \
HID_LOGICAL_MIN(0), \
HID_LOGICAL_MAX(1), \
HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), \
HID_USAGE_MIN(0), \
HID_USAGE_MAX(119), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \
HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE)
#define DIVAPICO_LED_FOOTER \
HID_COLLECTION_END
// HID_REPORT_ID(REPORT_ID_NKRO)
// Slider First 16 LEDs (48 rgb zones, BRG order)
#define DIVAPICO_REPORT_DESC_LED_SLIDER_1 \
HID_REPORT_ID(REPORT_ID_LED_SLIDER_1) \
HID_REPORT_COUNT(48), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(1), HID_USAGE_MAX(48), \
HID_STRING_MINIMUM(8), HID_STRING_MAXIMUM(55), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
// Slider Remaining 16 LEDs (48 rgb zones, BRG order)
#define DIVAPICO_REPORT_DESC_LED_SLIDER_2 \
HID_REPORT_ID(REPORT_ID_LED_SLIDER_2) \
HID_REPORT_COUNT(48), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(49), HID_USAGE_MAX(96), \
HID_STRING_MINIMUM(56), HID_STRING_MAXIMUM(103), /* Delta to previous */ \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
// Button LEDs (18 rgb zones, BRG order)
#define DIVAPICO_REPORT_DESC_LED_BUTTON \
HID_REPORT_ID(REPORT_ID_LED_BUTTON) \
HID_REPORT_COUNT(4), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(97), HID_USAGE_MAX(100), \
HID_STRING_MINIMUM(104), HID_STRING_MAXIMUM(107), /* Delta to previous */ \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
// LEDs Compressed
#define DIVAPICO_REPORT_DESC_LED_COMPRESSED \
HID_REPORT_ID(REPORT_ID_LED_COMPRESSED) \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE(0x00), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(63), \
HID_FEATURE(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
#endif /* USB_DESCRIPTORS_H_ */