mirror of
https://github.com/justinian/edmfd_firmware.git
synced 2025-12-09 16:24:31 -08:00
Multiple button groups piping to HID
This commit is contained in:
@@ -3,8 +3,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "hardware/i2c.h"
|
||||
|
||||
#include "tusb_config.h"
|
||||
#include "bsp/board_api.h"
|
||||
@@ -14,8 +12,13 @@
|
||||
|
||||
#include "blink.hh"
|
||||
#include "hid.hh"
|
||||
#include "i2c.hh"
|
||||
#include "mcp23017.hh"
|
||||
|
||||
using mcp23017::reg;
|
||||
|
||||
static constexpr unsigned i2c_baud = 1200 * 1000;
|
||||
|
||||
extern "C" {
|
||||
// TinyUSB callbacks
|
||||
void tud_mount_cb();
|
||||
@@ -24,73 +27,47 @@ extern "C" {
|
||||
void tud_resume_cb();
|
||||
}
|
||||
|
||||
// I2C reserves some addresses for special purposes. We exclude these from the scan.
|
||||
// These are any addresses of the form 000 0xxx or 111 1xxx
|
||||
bool reserved_addr(uint8_t addr) {
|
||||
return (addr & 0x78) == 0 || (addr & 0x78) == 0x78;
|
||||
}
|
||||
static constexpr unsigned bank_leds = 0;
|
||||
static constexpr unsigned bank_buttons = 1;
|
||||
|
||||
void setup_i2c()
|
||||
{
|
||||
i2c_init(i2c_default, 100 * 1000);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
|
||||
static constexpr unsigned hid_update_ms = 1000/20; // 20Hz
|
||||
|
||||
// Make the I2C pins available to picotool
|
||||
bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));
|
||||
}
|
||||
static constexpr unsigned buttons_per_group = 5;
|
||||
static constexpr unsigned button_group_count = 2;
|
||||
static const unsigned button_group_irqs[] = {5, 6};
|
||||
mcp23017::extender button_groups[] ={ {0x20}, {0x21} };
|
||||
uint8_t button_states[] = {0, 0};
|
||||
uint8_t button_leds[] = {0, 0};
|
||||
|
||||
int write_mcp(uint8_t addr, uint8_t reg, uint8_t value, const char *regname)
|
||||
{
|
||||
uint8_t txdata[2] = { reg, value };
|
||||
int ret = i2c_write_blocking(i2c_default, addr, txdata, 2, false);
|
||||
if (ret)
|
||||
printf("Error %02x writing %02x to %02x:%s", ret, value, addr, regname);
|
||||
else
|
||||
printf("Wrote %02x to %02x:%s", value, addr, regname);
|
||||
static_assert(sizeof(button_group_irqs) == button_group_count * sizeof(*button_group_irqs));
|
||||
static_assert(sizeof(button_groups) == button_group_count * sizeof(*button_groups));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void setup_mcp()
|
||||
{
|
||||
// A7 is button, B0 is LED
|
||||
static constexpr uint8_t addr = 0x20;
|
||||
if (write_mcp(addr, 0x00, 0xff, "IODIRA")) return;
|
||||
if (write_mcp(addr, 0x10, 0x00, "IODIRB")) return;
|
||||
if (write_mcp(addr, 0x02, 0xff, "GPINTENA")) return;
|
||||
if (write_mcp(addr, 0x06, 0xff, "GPPUA")) return;
|
||||
}
|
||||
|
||||
void i2c_scan()
|
||||
{
|
||||
printf("\nI2C Bus Scan\n");
|
||||
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
|
||||
|
||||
for (int addr = 0; addr < (1 << 7); ++addr) {
|
||||
if (addr % 16 == 0) {
|
||||
printf("%02x ", addr);
|
||||
}
|
||||
|
||||
// Perform a 1-byte dummy read from the probe address. If a slave
|
||||
// acknowledges this address, the function returns the number of bytes
|
||||
// transferred. If the address byte is ignored, the function returns
|
||||
// -1.
|
||||
|
||||
// Skip over any reserved addresses.
|
||||
int ret;
|
||||
uint8_t rxdata;
|
||||
if (reserved_addr(addr))
|
||||
ret = PICO_ERROR_GENERIC;
|
||||
else
|
||||
ret = i2c_read_blocking(i2c_default, addr, &rxdata, 1, false);
|
||||
|
||||
printf(ret < 0 ? "." : "@");
|
||||
printf(addr % 16 == 15 ? "\n" : " ");
|
||||
void irq_handler(unsigned pin, uint32_t events) {
|
||||
for (unsigned i = 0; i < button_group_count; ++i) {
|
||||
if (button_group_irqs[i] != pin) continue;
|
||||
button_states[i] = button_groups[i].get_gpios(bank_buttons);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool hid_update_callback(repeating_timer *timer) {
|
||||
uint32_t buttons = 0;
|
||||
for (unsigned i = 0; i < button_group_count; ++i)
|
||||
buttons |= uint32_t(button_states[i]) << (i*8);
|
||||
|
||||
blink::task();
|
||||
hid::task(buttons);
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_leds_callback(uint32_t leds) {
|
||||
for (unsigned i = 0; i < button_group_count; ++i) {
|
||||
uint8_t desired = (leds >> (i*8)) & 0xff;
|
||||
if (desired != button_leds[i]) {
|
||||
button_groups[i].set_gpios(bank_leds, desired);
|
||||
button_leds[i] = desired;
|
||||
}
|
||||
}
|
||||
printf("Done.\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
@@ -111,27 +88,50 @@ int main(void)
|
||||
board_init_after_tusb();
|
||||
log::info("tinyusb initialized");
|
||||
|
||||
setup_i2c();
|
||||
log::info("i2c initialized");
|
||||
i2c::init(i2c_baud);
|
||||
|
||||
using mcp23017::reg;
|
||||
mcp23017::gpios gpios {0x20};
|
||||
// set up interrupt handlers
|
||||
for (int pin : button_group_irqs) {
|
||||
gpio_init(pin);
|
||||
gpio_pull_up(pin);
|
||||
gpio_set_irq_enabled_with_callback(pin, GPIO_IRQ_EDGE_FALL, true, irq_handler);
|
||||
}
|
||||
|
||||
uint8_t iocon = 0xba;
|
||||
gpios.read(reg::iocona, &iocon);
|
||||
gpios.read(reg::iodira, &iocon);
|
||||
bool have_any_buttons = false;
|
||||
for (unsigned i = 0; i < button_group_count; ++i) {
|
||||
if (!button_groups[i].init()) {
|
||||
log::warn("failed to initialize button group %d", i);
|
||||
continue;
|
||||
}
|
||||
log::info("initializing button group %d", i);
|
||||
|
||||
/*
|
||||
gpios.write(reg::iodira, 0xff);
|
||||
gpios.write(reg::gpintena, 0xff);
|
||||
gpios.write(reg::gppua, 0xff);
|
||||
*/
|
||||
have_any_buttons = true;
|
||||
mcp23017::extender &buttons = button_groups[i];
|
||||
|
||||
using mcp23017::direction;
|
||||
for (unsigned i = 0; i < buttons_per_group; ++i)
|
||||
buttons.set_direction(0, i, direction::out);
|
||||
|
||||
buttons.set_direction(1, 0, direction::in);
|
||||
buttons.set_polarity(1, 0, true);
|
||||
buttons.set_pullup(1, 0, true);
|
||||
buttons.set_irq(1, 0, true);
|
||||
}
|
||||
|
||||
if (!have_any_buttons) {
|
||||
i2c::scan();
|
||||
return 1;
|
||||
}
|
||||
|
||||
hid::init(set_leds_callback);
|
||||
|
||||
repeating_timer hid_timer;
|
||||
add_repeating_timer_ms(hid_update_ms, hid_update_callback, nullptr, &hid_timer);
|
||||
|
||||
// Let timers and iterrupts handle most things
|
||||
while (1) {
|
||||
uint32_t time = board_millis();
|
||||
tud_task(); // tinyusb device task
|
||||
blink::task(time);
|
||||
hid::task(time);
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user