Cleanup headers

This commit is contained in:
whowechina
2024-04-24 20:50:24 +08:00
parent 7c1c90d121
commit 7bab3ef3f0
6 changed files with 719 additions and 730 deletions
+124 -125
View File
@@ -1,125 +1,124 @@
/*
* Chu Pico Air Sensor
* WHowe <github.com/whowechina>
*
* Use ToF (Sharp GP2Y0E03) to detect air keys
*/
#include "air.h"
#include <stdint.h>
#include <stdbool.h>
#include "bsp/board.h"
#include "hardware/gpio.h"
#include "board_defs.h"
#include "config.h"
#include "gp2y0e.h"
#include "vl53l0x.h"
#include "i2c_hub.h"
static const uint8_t TOF_LIST[] = TOF_MUX_LIST;
static uint8_t tof_model[sizeof(TOF_LIST)];
static uint16_t distances[sizeof(TOF_LIST)];
void air_init()
{
i2c_init(I2C_PORT, I2C_FREQ);
gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA);
gpio_pull_up(I2C_SCL);
i2c_hub_init();
vl53l0x_init(I2C_PORT, 0);
for (int i = 0; i < sizeof(TOF_LIST); i++) {
i2c_select(I2C_PORT, 1 << TOF_LIST[i]);
if (vl53l0x_is_present()) {
tof_model[i] = 1;
} else if (gp2y0e_is_present(I2C_PORT)) {
tof_model[i] = 2;
} else {
tof_model[i] = 0;
}
if (tof_model[i] == 1) {
vl53l0x_init_tof(i);
vl53l0x_start_continuous(i);
} else if (tof_model[i] == 2) {
gp2y03_init(I2C_PORT);
}
}
}
size_t air_num()
{
return sizeof(TOF_LIST);
}
static inline uint8_t air_bits(int dist, int offset)
{
if ((dist < offset) || (dist == 4095)) {
return 0;
}
int pitch = chu_cfg->tof.pitch * 10;
int index = (dist - offset) / pitch;
if (index >= 6) {
return 0;
}
return 1 << index;
}
uint8_t air_bitmap()
{
int offset = chu_cfg->tof.offset * 10;
int pitch = chu_cfg->tof.pitch * 10;
uint8_t bitmap = 0;
for (int i = 0; i < sizeof(TOF_LIST); i++) {
bitmap |= air_bits(distances[i], offset) |
air_bits(distances[i], offset + pitch);
}
return bitmap;
}
unsigned air_value(uint8_t index)
{
if (index >= sizeof(TOF_LIST)) {
return 0;
}
int offset = chu_cfg->tof.offset * 10;
int pitch = chu_cfg->tof.pitch * 10;
uint8_t bitmap = air_bits(distances[index], offset) |
air_bits(distances[index], offset + pitch);
for (int i = 0; i < 6; i++) {
if (bitmap & (1 << i)) {
return i + 1; // lowest detected position
}
}
return 0;
}
void air_update()
{
for (int i = 0; i < sizeof(TOF_LIST); i++) {
i2c_select(I2C_PORT, 1 << TOF_LIST[i]);
if (tof_model[i] == 1) {
distances[i] = readRangeContinuousMillimeters(i) * 10;
} else if (tof_model[i] == 2) {
distances[i] = gp2y0e_dist16_mm(I2C_PORT) * 10;
}
}
}
uint16_t air_raw(uint8_t index)
{
if (index >= sizeof(TOF_LIST)) {
return 0;
}
return distances[index];
}
/*
* Chu Pico Air Sensor
* WHowe <github.com/whowechina>
*
* Use ToF (Sharp GP2Y0E03) to detect air keys
*/
#include "air.h"
#include <stdint.h>
#include <stdbool.h>
#include "hardware/gpio.h"
#include "board_defs.h"
#include "config.h"
#include "gp2y0e.h"
#include "vl53l0x.h"
#include "i2c_hub.h"
static const uint8_t TOF_LIST[] = TOF_MUX_LIST;
static uint8_t tof_model[sizeof(TOF_LIST)];
static uint16_t distances[sizeof(TOF_LIST)];
void air_init()
{
i2c_init(I2C_PORT, I2C_FREQ);
gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA);
gpio_pull_up(I2C_SCL);
i2c_hub_init();
vl53l0x_init(I2C_PORT, 0);
for (int i = 0; i < sizeof(TOF_LIST); i++) {
i2c_select(I2C_PORT, 1 << TOF_LIST[i]);
if (vl53l0x_is_present()) {
tof_model[i] = 1;
} else if (gp2y0e_is_present(I2C_PORT)) {
tof_model[i] = 2;
} else {
tof_model[i] = 0;
}
if (tof_model[i] == 1) {
vl53l0x_init_tof(i);
vl53l0x_start_continuous(i);
} else if (tof_model[i] == 2) {
gp2y03_init(I2C_PORT);
}
}
}
size_t air_num()
{
return sizeof(TOF_LIST);
}
static inline uint8_t air_bits(int dist, int offset)
{
if ((dist < offset) || (dist == 4095)) {
return 0;
}
int pitch = chu_cfg->tof.pitch * 10;
int index = (dist - offset) / pitch;
if (index >= 6) {
return 0;
}
return 1 << index;
}
uint8_t air_bitmap()
{
int offset = chu_cfg->tof.offset * 10;
int pitch = chu_cfg->tof.pitch * 10;
uint8_t bitmap = 0;
for (int i = 0; i < sizeof(TOF_LIST); i++) {
bitmap |= air_bits(distances[i], offset) |
air_bits(distances[i], offset + pitch);
}
return bitmap;
}
unsigned air_value(uint8_t index)
{
if (index >= sizeof(TOF_LIST)) {
return 0;
}
int offset = chu_cfg->tof.offset * 10;
int pitch = chu_cfg->tof.pitch * 10;
uint8_t bitmap = air_bits(distances[index], offset) |
air_bits(distances[index], offset + pitch);
for (int i = 0; i < 6; i++) {
if (bitmap & (1 << i)) {
return i + 1; // lowest detected position
}
}
return 0;
}
void air_update()
{
for (int i = 0; i < sizeof(TOF_LIST); i++) {
i2c_select(I2C_PORT, 1 << TOF_LIST[i]);
if (tof_model[i] == 1) {
distances[i] = readRangeContinuousMillimeters(i) * 10;
} else if (tof_model[i] == 2) {
distances[i] = gp2y0e_dist16_mm(I2C_PORT) * 10;
}
}
}
uint16_t air_raw(uint8_t index)
{
if (index >= sizeof(TOF_LIST)) {
return 0;
}
return distances[index];
}
+177 -178
View File
@@ -1,178 +1,177 @@
/*
* RGB LED (WS2812) Strip control
* WHowe <github.com/whowechina>
*
*/
#include "rgb.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "bsp/board.h"
#include "hardware/pio.h"
#include "hardware/timer.h"
#include "ws2812.pio.h"
#include "board_defs.h"
#include "config.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static uint32_t rgb_buf[47]; // 16(Keys) + 15(Gaps) + 16(maximum ToF indicators)
#define _MAP_LED(x) _MAKE_MAPPER(x)
#define _MAKE_MAPPER(x) MAP_LED_##x
#define MAP_LED_RGB { c1 = r; c2 = g; c3 = b; }
#define MAP_LED_GRB { c1 = g; c2 = r; c3 = b; }
#define REMAP_BUTTON_RGB _MAP_LED(BUTTON_RGB_ORDER)
#define REMAP_TT_RGB _MAP_LED(TT_RGB_ORDER)
static inline uint32_t _rgb32(uint32_t c1, uint32_t c2, uint32_t c3, bool gamma_fix)
{
if (gamma_fix) {
c1 = ((c1 + 1) * (c1 + 1) - 1) >> 8;
c2 = ((c2 + 1) * (c2 + 1) - 1) >> 8;
c3 = ((c3 + 1) * (c3 + 1) - 1) >> 8;
}
return (c1 << 16) | (c2 << 8) | (c3 << 0);
}
uint32_t rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix)
{
#if BUTTON_RGB_ORDER == GRB
return _rgb32(g, r, b, gamma_fix);
#else
return _rgb32(r, g, b, gamma_fix);
#endif
}
uint32_t rgb32_from_hsv(uint8_t h, uint8_t s, uint8_t v)
{
uint32_t region, remainder, p, q, t;
if (s == 0) {
return v << 16 | v << 8 | v;
}
region = h / 43;
remainder = (h % 43) * 6;
p = (v * (255 - s)) >> 8;
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
switch (region) {
case 0:
return v << 16 | t << 8 | p;
case 1:
return q << 16 | v << 8 | p;
case 2:
return p << 16 | v << 8 | t;
case 3:
return p << 16 | q << 8 | v;
case 4:
return t << 16 | p << 8 | v;
default:
return v << 16 | p << 8 | q;
}
}
static void drive_led()
{
static uint64_t last = 0;
uint64_t now = time_us_64();
if (now - last < 4000) { // no faster than 250Hz
return;
}
last = now;
for (int i = 30; i >= 0; i--) {
pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u);
}
for (int i = 31; i < ARRAY_SIZE(rgb_buf); i++) {
pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u);
}
}
void rgb_set_colors(const uint32_t *colors, unsigned index, size_t num)
{
if (index >= ARRAY_SIZE(rgb_buf)) {
return;
}
if (index + num > ARRAY_SIZE(rgb_buf)) {
num = ARRAY_SIZE(rgb_buf) - index;
}
memcpy(&rgb_buf[index], colors, num * sizeof(*colors));
}
static inline uint32_t apply_level(uint32_t color)
{
unsigned r = (color >> 16) & 0xff;
unsigned g = (color >> 8) & 0xff;
unsigned b = color & 0xff;
r = r * chu_cfg->style.level / 255;
g = g * chu_cfg->style.level / 255;
b = b * chu_cfg->style.level / 255;
return r << 16 | g << 8 | b;
}
void rgb_set_color(unsigned index, uint32_t color)
{
if (index >= ARRAY_SIZE(rgb_buf)) {
return;
}
rgb_buf[index] = apply_level(color);
}
void rgb_key_color(unsigned index, uint32_t color)
{
if (index > 16) {
return;
}
rgb_buf[index * 2] = apply_level(color);
}
void rgb_gap_color(unsigned index, uint32_t color)
{
if (index > 15) {
return;
}
rgb_buf[index * 2 + 1] = apply_level(color);
}
void rgb_set_brg(unsigned index, const uint8_t *brg_array, size_t num)
{
if (index >= ARRAY_SIZE(rgb_buf)) {
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));
}
}
void rgb_init()
{
uint pio0_offset = pio_add_program(pio0, &ws2812_program);
gpio_set_drive_strength(RGB_PIN, GPIO_DRIVE_STRENGTH_2MA);
ws2812_program_init(pio0, 0, pio0_offset, RGB_PIN, 800000, false);
}
void rgb_update()
{
drive_led();
}
/*
* RGB LED (WS2812) Strip control
* WHowe <github.com/whowechina>
*
*/
#include "rgb.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "hardware/pio.h"
#include "hardware/timer.h"
#include "ws2812.pio.h"
#include "board_defs.h"
#include "config.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static uint32_t rgb_buf[47]; // 16(Keys) + 15(Gaps) + 16(maximum ToF indicators)
#define _MAP_LED(x) _MAKE_MAPPER(x)
#define _MAKE_MAPPER(x) MAP_LED_##x
#define MAP_LED_RGB { c1 = r; c2 = g; c3 = b; }
#define MAP_LED_GRB { c1 = g; c2 = r; c3 = b; }
#define REMAP_BUTTON_RGB _MAP_LED(BUTTON_RGB_ORDER)
#define REMAP_TT_RGB _MAP_LED(TT_RGB_ORDER)
static inline uint32_t _rgb32(uint32_t c1, uint32_t c2, uint32_t c3, bool gamma_fix)
{
if (gamma_fix) {
c1 = ((c1 + 1) * (c1 + 1) - 1) >> 8;
c2 = ((c2 + 1) * (c2 + 1) - 1) >> 8;
c3 = ((c3 + 1) * (c3 + 1) - 1) >> 8;
}
return (c1 << 16) | (c2 << 8) | (c3 << 0);
}
uint32_t rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix)
{
#if BUTTON_RGB_ORDER == GRB
return _rgb32(g, r, b, gamma_fix);
#else
return _rgb32(r, g, b, gamma_fix);
#endif
}
uint32_t rgb32_from_hsv(uint8_t h, uint8_t s, uint8_t v)
{
uint32_t region, remainder, p, q, t;
if (s == 0) {
return v << 16 | v << 8 | v;
}
region = h / 43;
remainder = (h % 43) * 6;
p = (v * (255 - s)) >> 8;
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
switch (region) {
case 0:
return v << 16 | t << 8 | p;
case 1:
return q << 16 | v << 8 | p;
case 2:
return p << 16 | v << 8 | t;
case 3:
return p << 16 | q << 8 | v;
case 4:
return t << 16 | p << 8 | v;
default:
return v << 16 | p << 8 | q;
}
}
static void drive_led()
{
static uint64_t last = 0;
uint64_t now = time_us_64();
if (now - last < 4000) { // no faster than 250Hz
return;
}
last = now;
for (int i = 30; i >= 0; i--) {
pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u);
}
for (int i = 31; i < ARRAY_SIZE(rgb_buf); i++) {
pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u);
}
}
void rgb_set_colors(const uint32_t *colors, unsigned index, size_t num)
{
if (index >= ARRAY_SIZE(rgb_buf)) {
return;
}
if (index + num > ARRAY_SIZE(rgb_buf)) {
num = ARRAY_SIZE(rgb_buf) - index;
}
memcpy(&rgb_buf[index], colors, num * sizeof(*colors));
}
static inline uint32_t apply_level(uint32_t color)
{
unsigned r = (color >> 16) & 0xff;
unsigned g = (color >> 8) & 0xff;
unsigned b = color & 0xff;
r = r * chu_cfg->style.level / 255;
g = g * chu_cfg->style.level / 255;
b = b * chu_cfg->style.level / 255;
return r << 16 | g << 8 | b;
}
void rgb_set_color(unsigned index, uint32_t color)
{
if (index >= ARRAY_SIZE(rgb_buf)) {
return;
}
rgb_buf[index] = apply_level(color);
}
void rgb_key_color(unsigned index, uint32_t color)
{
if (index > 16) {
return;
}
rgb_buf[index * 2] = apply_level(color);
}
void rgb_gap_color(unsigned index, uint32_t color)
{
if (index > 15) {
return;
}
rgb_buf[index * 2 + 1] = apply_level(color);
}
void rgb_set_brg(unsigned index, const uint8_t *brg_array, size_t num)
{
if (index >= ARRAY_SIZE(rgb_buf)) {
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));
}
}
void rgb_init()
{
uint pio0_offset = pio_add_program(pio0, &ws2812_program);
gpio_set_drive_strength(RGB_PIN, GPIO_DRIVE_STRENGTH_2MA);
ws2812_program_init(pio0, 0, pio0_offset, RGB_PIN, 800000, false);
}
void rgb_update()
{
drive_led();
}
+177 -178
View File
@@ -1,178 +1,177 @@
/*
* Controller Config Save and Load
* WHowe <github.com/whowechina>
*
* Config is stored in last sector of flash
*/
#include "save.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <memory.h>
#include "bsp/board.h"
#include "pico/bootrom.h"
#include "pico/stdio.h"
#include "hardware/flash.h"
#include "pico/multicore.h"
#include "pico/unique_id.h"
static struct {
size_t size;
size_t offset;
void (*after_load)();
} modules[8] = {0};
static int module_num = 0;
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)) {
uint32_t magic;
uint8_t data[FLASH_PAGE_SIZE - 4];
} page_t;
static page_t old_data = {0};
static page_t new_data = {0};
static page_t default_data = {0};
static int data_page = -1;
static bool requesting_save = false;
static uint64_t requesting_time = 0;
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("\nProgram Flash %d %8lx\n", data_page, old_data.magic);
if (mutex_enter_timeout_us(io_lock, 100000)) {
sleep_ms(10); /* 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);
} else {
printf("Program Flash Failed.\n");
}
}
static void load_default()
{
printf("Load Default\n");
new_data = default_data;
new_data.magic = my_magic;
}
static const page_t *get_page(int id)
{
int addr = XIP_BASE + SAVE_SECTOR_OFFSET;
return (page_t *)(addr + FLASH_PAGE_SIZE * id);
}
static void save_load()
{
for (int i = 0; i < FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE; i++) {
if (get_page(i)->magic != my_magic) {
break;
}
data_page = i;
}
if (data_page < 0) {
load_default();
save_request(false);
return;
}
old_data = *get_page(data_page);
new_data = old_data;
printf("Page Loaded %d %8lx\n", data_page, new_data.magic);
}
static void save_loaded()
{
for (int i = 0; i < module_num; i++) {
modules[i].after_load();
}
}
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();
save_loaded();
}
void save_loop()
{
if (requesting_save && (time_us_64() - requesting_time > SAVE_TIMEOUT_US)) {
requesting_save = false;
/* only when data is actually changed */
if (memcmp(&old_data, &new_data, sizeof(old_data)) == 0) {
return;
}
save_program();
}
}
void *save_alloc(size_t size, void *def, void (*after_load)())
{
modules[module_num].size = size;
size_t offset = module_num > 0 ? modules[module_num - 1].offset + size : 0;
modules[module_num].offset = offset;
modules[module_num].after_load = after_load;
module_num++;
memcpy(default_data.data + offset, def, size); // backup the default
return new_data.data + offset;
}
void save_request(bool immediately)
{
if (!requesting_save) {
printf("Save requested.\n");
requesting_save = true;
new_data.magic = my_magic;
requesting_time = time_us_64();
}
if (immediately) {
requesting_time = 0;
save_loop();
}
}
/*
* Controller Config Save and Load
* WHowe <github.com/whowechina>
*
* Config is stored in last sector of flash
*/
#include "save.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "pico/bootrom.h"
#include "pico/stdio.h"
#include "hardware/flash.h"
#include "pico/multicore.h"
#include "pico/unique_id.h"
static struct {
size_t size;
size_t offset;
void (*after_load)();
} modules[8] = {0};
static int module_num = 0;
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)) {
uint32_t magic;
uint8_t data[FLASH_PAGE_SIZE - 4];
} page_t;
static page_t old_data = {0};
static page_t new_data = {0};
static page_t default_data = {0};
static int data_page = -1;
static bool requesting_save = false;
static uint64_t requesting_time = 0;
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("\nProgram Flash %d %8lx\n", data_page, old_data.magic);
if (mutex_enter_timeout_us(io_lock, 100000)) {
sleep_ms(10); /* 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);
} else {
printf("Program Flash Failed.\n");
}
}
static void load_default()
{
printf("Load Default\n");
new_data = default_data;
new_data.magic = my_magic;
}
static const page_t *get_page(int id)
{
int addr = XIP_BASE + SAVE_SECTOR_OFFSET;
return (page_t *)(addr + FLASH_PAGE_SIZE * id);
}
static void save_load()
{
for (int i = 0; i < FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE; i++) {
if (get_page(i)->magic != my_magic) {
break;
}
data_page = i;
}
if (data_page < 0) {
load_default();
save_request(false);
return;
}
old_data = *get_page(data_page);
new_data = old_data;
printf("Page Loaded %d %8lx\n", data_page, new_data.magic);
}
static void save_loaded()
{
for (int i = 0; i < module_num; i++) {
modules[i].after_load();
}
}
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();
save_loaded();
}
void save_loop()
{
if (requesting_save && (time_us_64() - requesting_time > SAVE_TIMEOUT_US)) {
requesting_save = false;
/* only when data is actually changed */
if (memcmp(&old_data, &new_data, sizeof(old_data)) == 0) {
return;
}
save_program();
}
}
void *save_alloc(size_t size, void *def, void (*after_load)())
{
modules[module_num].size = size;
size_t offset = module_num > 0 ? modules[module_num - 1].offset + size : 0;
modules[module_num].offset = offset;
modules[module_num].after_load = after_load;
module_num++;
memcpy(default_data.data + offset, def, size); // backup the default
return new_data.data + offset;
}
void save_request(bool immediately)
{
if (!requesting_save) {
printf("Save requested.\n");
requesting_save = true;
new_data.magic = my_magic;
requesting_time = time_us_64();
}
if (immediately) {
requesting_time = 0;
save_loop();
}
}
+102 -103
View File
@@ -1,103 +1,102 @@
/*
* Chu Pico Silder Keys
* WHowe <github.com/whowechina>
*
* MPR121 CapSense based Keys
*/
#include "slider.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "bsp/board.h"
#include "hardware/gpio.h"
#include "hardware/i2c.h"
#include "board_defs.h"
#include "config.h"
#include "mpr121.h"
#define MPR121_ADDR 0x5A
static uint16_t readout[36];
static uint16_t touch[3];
static unsigned touch_count[36];
void slider_init()
{
i2c_init(I2C_PORT, I2C_FREQ);
gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA);
gpio_pull_up(I2C_SCL);
for (int m = 0; m < 3; m++) {
mpr121_init(MPR121_ADDR + m);
}
slider_update_config();
}
void slider_update()
{
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);
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_count[m * 12 + i]++;
}
}
}
}
const uint16_t *slider_raw()
{
mpr121_raw(MPR121_ADDR, readout, 12);
mpr121_raw(MPR121_ADDR + 1, readout + 12, 12);
mpr121_raw(MPR121_ADDR + 2, readout + 24, 12);
return readout;
}
bool slider_touched(unsigned key)
{
if (key >= 32) {
return 0;
}
return touch[key / 12] & (1 << (key % 12));
}
unsigned slider_count(unsigned key)
{
if (key >= 32) {
return 0;
}
return touch_count[key];
}
void slider_reset_stat()
{
memset(touch_count, 0, sizeof(touch_count));
}
void slider_update_config()
{
for (int m = 0; m < 3; m++) {
mpr121_debounce(MPR121_ADDR + m, chu_cfg->sense.debounce_touch,
chu_cfg->sense.debounce_release);
mpr121_sense(MPR121_ADDR + m, chu_cfg->sense.global,
chu_cfg->sense.keys + m * 12,
m != 2 ? 12 : 8);
mpr121_filter(MPR121_ADDR + m, chu_cfg->sense.filter >> 6,
(chu_cfg->sense.filter >> 4) & 0x03,
chu_cfg->sense.filter & 0x07);
}
}
/*
* Chu Pico Silder Keys
* WHowe <github.com/whowechina>
*
* MPR121 CapSense based Keys
*/
#include "slider.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "hardware/gpio.h"
#include "hardware/i2c.h"
#include "board_defs.h"
#include "config.h"
#include "mpr121.h"
#define MPR121_ADDR 0x5A
static uint16_t readout[36];
static uint16_t touch[3];
static unsigned touch_count[36];
void slider_init()
{
i2c_init(I2C_PORT, I2C_FREQ);
gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA);
gpio_pull_up(I2C_SCL);
for (int m = 0; m < 3; m++) {
mpr121_init(MPR121_ADDR + m);
}
slider_update_config();
}
void slider_update()
{
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);
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_count[m * 12 + i]++;
}
}
}
}
const uint16_t *slider_raw()
{
mpr121_raw(MPR121_ADDR, readout, 12);
mpr121_raw(MPR121_ADDR + 1, readout + 12, 12);
mpr121_raw(MPR121_ADDR + 2, readout + 24, 12);
return readout;
}
bool slider_touched(unsigned key)
{
if (key >= 32) {
return 0;
}
return touch[key / 12] & (1 << (key % 12));
}
unsigned slider_count(unsigned key)
{
if (key >= 32) {
return 0;
}
return touch_count[key];
}
void slider_reset_stat()
{
memset(touch_count, 0, sizeof(touch_count));
}
void slider_update_config()
{
for (int m = 0; m < 3; m++) {
mpr121_debounce(MPR121_ADDR + m, chu_cfg->sense.debounce_touch,
chu_cfg->sense.debounce_release);
mpr121_sense(MPR121_ADDR + m, chu_cfg->sense.global,
chu_cfg->sense.keys + m * 12,
m != 2 ? 12 : 8);
mpr121_filter(MPR121_ADDR + m, chu_cfg->sense.filter >> 6,
(chu_cfg->sense.filter >> 4) & 0x03,
chu_cfg->sense.filter & 0x07);
}
}
-5
View File
@@ -105,11 +105,6 @@ extern "C" {
// 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
#define CFG_TUD_HID_EP_BUFSIZE 64
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 128)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 128)
+139 -141
View File
@@ -1,141 +1,139 @@
#ifndef USB_DESCRIPTORS_H_
#define USB_DESCRIPTORS_H_
#include "common/tusb_common.h"
#include "device/usbd.h"
enum {
REPORT_ID_JOYSTICK = 1,
REPORT_ID_LED_SLIDER_16 = 4,
REPORT_ID_LED_SLIDER_15 = 5,
REPORT_ID_LED_TOWER_6 = 6,
REPORT_ID_LED_COMPRESSED = 11,
};
// because they are missing from tusb_hid.h
#define HID_STRING_INDEX(x) HID_REPORT_ITEM(x, 7, RI_TYPE_LOCAL, 1)
#define HID_STRING_INDEX_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_LOCAL, n)
#define HID_STRING_MINIMUM(x) HID_REPORT_ITEM(x, 8, RI_TYPE_LOCAL, 1)
#define HID_STRING_MINIMUM_N(x, n) HID_REPORT_ITEM(x, 8, RI_TYPE_LOCAL, n)
#define HID_STRING_MAXIMUM(x) HID_REPORT_ITEM(x, 9, RI_TYPE_LOCAL, 1)
#define HID_STRING_MAXIMUM_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_LOCAL, n)
// Joystick Report Descriptor Template - Based off Drewol/rp2040-gamecon
// Button Map | X | Y
#define CHUPICO_REPORT_DESC_JOYSTICK \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_JOYSTICK), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_ID(REPORT_ID_JOYSTICK) \
HID_USAGE_PAGE(HID_USAGE_PAGE_BUTTON), \
HID_USAGE_MIN(1), HID_USAGE_MAX(16), \
HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \
HID_REPORT_COUNT(16), HID_REPORT_SIZE(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_HAT_SWITCH), \
HID_LOGICAL_MIN(1), HID_LOGICAL_MAX(8), \
HID_PHYSICAL_MIN(0), HID_PHYSICAL_MAX_N(315, 2), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
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_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), /* Analog */ \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(4), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE_N(HID_USAGE_PAGE_VENDOR, 2), \
HID_USAGE(0), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
#define CHUPICO_LED_HEADER \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \
HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE)
#define CHUPICO_LED_FOOTER \
HID_COLLECTION_END
// Slider First 16 LEDs (48 rgb zones, BRG order)
#define CHUPICO_REPORT_DESC_LED_SLIDER_16 \
HID_REPORT_ID(REPORT_ID_LED_SLIDER_16) \
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 15 LEDs (45 rgb zones, BRG order)
#define CHUPICO_REPORT_DESC_LED_SLIDER_15 \
HID_REPORT_ID(REPORT_ID_LED_SLIDER_15) \
HID_REPORT_COUNT(45), 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(93), \
HID_STRING_MINIMUM(8), HID_STRING_MAXIMUM(52), /* Delta to previous */ \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
// Tower LEDs (18 rgb zones, BRG order)
#define CHUPICO_REPORT_DESC_LED_TOWER_6 \
HID_REPORT_ID(REPORT_ID_LED_TOWER_6) \
HID_REPORT_COUNT(18), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(94), HID_USAGE_MAX(111), \
HID_STRING_MINIMUM(8), HID_STRING_MAXIMUM(25), /* Delta to previous */ \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
// LEDs Compressed
#define CHUPICO_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)
#define CHUPICO_REPORT_DESC_NKRO \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_KEYBOARD), \
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_COLLECTION_END
// HID_REPORT_ID(REPORT_ID_NKRO)
#endif /* USB_DESCRIPTORS_H_ */
#ifndef USB_DESCRIPTORS_H_
#define USB_DESCRIPTORS_H_
#include "common/tusb_common.h"
#include "device/usbd.h"
enum {
REPORT_ID_JOYSTICK = 1,
REPORT_ID_LED_SLIDER_16 = 4,
REPORT_ID_LED_SLIDER_15 = 5,
REPORT_ID_LED_TOWER_6 = 6,
REPORT_ID_LED_COMPRESSED = 11,
};
// because they are missing from tusb_hid.h
#define HID_STRING_INDEX(x) HID_REPORT_ITEM(x, 7, RI_TYPE_LOCAL, 1)
#define HID_STRING_INDEX_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_LOCAL, n)
#define HID_STRING_MINIMUM(x) HID_REPORT_ITEM(x, 8, RI_TYPE_LOCAL, 1)
#define HID_STRING_MINIMUM_N(x, n) HID_REPORT_ITEM(x, 8, RI_TYPE_LOCAL, n)
#define HID_STRING_MAXIMUM(x) HID_REPORT_ITEM(x, 9, RI_TYPE_LOCAL, 1)
#define HID_STRING_MAXIMUM_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_LOCAL, n)
// Joystick Report Descriptor Template - Based off Drewol/rp2040-gamecon
// Button Map | X | Y
#define CHUPICO_REPORT_DESC_JOYSTICK \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_JOYSTICK), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_ID(REPORT_ID_JOYSTICK) \
HID_USAGE_PAGE(HID_USAGE_PAGE_BUTTON), \
HID_USAGE_MIN(1), HID_USAGE_MAX(16), \
HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \
HID_REPORT_COUNT(16), HID_REPORT_SIZE(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_HAT_SWITCH), \
HID_LOGICAL_MIN(1), HID_LOGICAL_MAX(8), \
HID_PHYSICAL_MIN(0), HID_PHYSICAL_MAX_N(315, 2), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
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_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), /* Analog */ \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(4), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
\
HID_USAGE_PAGE_N(HID_USAGE_PAGE_VENDOR, 2), \
HID_USAGE(0), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), \
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
#define CHUPICO_LED_HEADER \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \
HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE)
#define CHUPICO_LED_FOOTER \
HID_COLLECTION_END
// Slider First 16 LEDs (48 rgb zones, BRG order)
#define CHUPICO_REPORT_DESC_LED_SLIDER_16 \
HID_REPORT_ID(REPORT_ID_LED_SLIDER_16) \
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 15 LEDs (45 rgb zones, BRG order)
#define CHUPICO_REPORT_DESC_LED_SLIDER_15 \
HID_REPORT_ID(REPORT_ID_LED_SLIDER_15) \
HID_REPORT_COUNT(45), 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(93), \
HID_STRING_MINIMUM(8), HID_STRING_MAXIMUM(52), /* Delta to previous */ \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
// Tower LEDs (18 rgb zones, BRG order)
#define CHUPICO_REPORT_DESC_LED_TOWER_6 \
HID_REPORT_ID(REPORT_ID_LED_TOWER_6) \
HID_REPORT_COUNT(18), HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(94), HID_USAGE_MAX(111), \
HID_STRING_MINIMUM(8), HID_STRING_MAXIMUM(25), /* Delta to previous */ \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE)
// LEDs Compressed
#define CHUPICO_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)
#define CHUPICO_REPORT_DESC_NKRO \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(HID_USAGE_DESKTOP_KEYBOARD), \
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_COLLECTION_END
#endif /* USB_DESCRIPTORS_H_ */