Add support for additional controller buttons

This commit is contained in:
Frederik Walk 2023-05-20 20:17:26 +02:00
parent d1de918629
commit 68457c60fe
7 changed files with 293 additions and 21 deletions

View File

@ -22,8 +22,14 @@ target_include_directories(${PROJECT_NAME}
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(
${PROJECT_NAME} PUBLIC tinyusb_device tinyusb_board pico_stdlib hardware_adc
pico_multicore pio_ws2812)
${PROJECT_NAME}
PUBLIC tinyusb_device
tinyusb_board
pico_stdlib
hardware_adc
pico_multicore
pio_ws2812
mcp23017)
pico_enable_stdio_usb(${PROJECT_NAME} 1)
pico_enable_stdio_uart(${PROJECT_NAME} 0)

View File

@ -1,9 +1,12 @@
#ifndef _GLOBALCONFIGURATION_H_
#define _GLOBALCONFIGURATION_H_
#include "peripherals/Buttons.h"
#include "peripherals/Drum.h"
#include "peripherals/StatusLed.h"
#include "hardware/i2c.h"
namespace Doncon::Config::Default {
const Peripherals::Drum::Config drum_config = {
@ -19,6 +22,41 @@ const Peripherals::Drum::Config drum_config = {
17, // Debounce delay in milliseconds
};
const Peripherals::Buttons::Config button_config = {
// I2C config
{
6, // SDA Pin
7, // SCL Pin
i2c1, // Block
1000000, // Speed
0x20, // Address
},
// Pins
{{
0, // Up
1, // Down
2, // Left
3, // Right
},
{
8, // North
9, // East
10, // South
11, // West
4, // L
12, // R
13, // Start
5, // Select
14, // Home
6, // Share
}},
20, // Debounce delay in milliseconds
};
const Peripherals::StatusLed::Config led_config = {
{255, 0, 0}, // Don Left Color
{0, 0, 255}, // Ka Left Color

View File

@ -0,0 +1,110 @@
#ifndef _PERIPHERALS_BUTTONS_H_
#define _PERIPHERALS_BUTTONS_H_
#include "utils/InputState.h"
#include <mcp23017/Mcp23017.h>
#include <map>
#include <memory>
#include <stdint.h>
namespace Doncon::Peripherals {
class Buttons {
public:
struct Config {
struct {
uint8_t sda_pin;
uint8_t scl_pin;
i2c_inst_t *block;
uint speed_hz;
uint8_t address;
} i2c;
struct {
struct {
uint8_t up;
uint8_t down;
uint8_t left;
uint8_t right;
} dpad;
struct {
uint8_t north;
uint8_t east;
uint8_t south;
uint8_t west;
uint8_t l;
uint8_t r;
uint8_t start;
uint8_t select;
uint8_t home;
uint8_t share;
} buttons;
} pins;
uint8_t debounce_delay_ms;
};
private:
enum class Id {
UP,
DOWN,
LEFT,
RIGHT,
NORTH,
EAST,
SOUTH,
WEST,
L,
R,
START,
SELECT,
HOME,
SHARE,
};
class Button {
private:
uint8_t gpio_pin;
uint16_t gpio_mask;
uint32_t last_change;
bool active;
public:
Button(uint8_t pin);
uint8_t getGpioPin() const { return gpio_pin; };
uint16_t getGpioMask() const { return gpio_mask; };
bool getState() const { return active; };
void setState(bool state, uint8_t debounce_delay);
};
struct SocdState {
Id lastVertical;
Id lastHorizontal;
};
Config m_config;
SocdState m_socd_state;
std::map<Id, Button> m_buttons;
std::unique_ptr<Mcp23017> m_mcp23017;
void socdClean(Utils::InputState &input_state);
public:
Buttons(const Config &config);
void updateInputState(Utils::InputState &input_state);
};
} // namespace Doncon::Peripherals
#endif // _PERIPHERALS_BUTTONS_H_

View File

@ -20,8 +20,20 @@ struct InputState {
Pad don_left, ka_left, don_right, ka_right;
};
struct DPad {
bool up, down, left, right;
};
struct Buttons {
bool north, east, south, west;
bool l, r;
bool start, select, home, share;
};
public:
Drum drum;
DPad dpad;
Buttons buttons;
private:
xinput_report_t m_xinput_report;

View File

@ -1,3 +1,4 @@
#include "peripherals/Buttons.h"
#include "peripherals/Drum.h"
#include "peripherals/StatusLed.h"
#include "usb/usb_driver.h"
@ -36,6 +37,7 @@ int main() {
Utils::InputState input_state;
Peripherals::Drum drum(Config::Default::drum_config);
Peripherals::Buttons buttons(Config::Default::button_config); // Move to core 1?
usb_mode_t mode = USB_MODE_XBOX360;
usb_driver_init(mode);
@ -46,6 +48,7 @@ int main() {
while (true) {
drum.updateInputState(input_state);
buttons.updateInputState(input_state);
usb_driver_send_and_receive_report(input_state.getReport(mode));
usb_driver_task();

102
src/peripherals/Buttons.cpp Normal file
View File

@ -0,0 +1,102 @@
#include "peripherals/Buttons.h"
#include "hardware/gpio.h"
#include "pico/time.h"
namespace Doncon::Peripherals {
Buttons::Button::Button(uint8_t pin) : gpio_pin(pin), gpio_mask(1 << pin), last_change(0), active(false) {}
void Buttons::Button::setState(bool state, uint8_t debounce_delay) {
if (active == state) {
return;
}
// Immediately change the input state, but only allow a change every debounce_delay milliseconds.
uint32_t now = to_ms_since_boot(get_absolute_time());
if (last_change + debounce_delay <= now) {
active = state;
last_change = now;
}
}
void Buttons::socdClean(Utils::InputState &input_state) {
// Last input has priority
if (input_state.dpad.up && input_state.dpad.down) {
if (m_socd_state.lastVertical == Id::DOWN) {
input_state.dpad.down = false;
} else if (m_socd_state.lastVertical == Id::UP) {
input_state.dpad.up = false;
}
} else if (input_state.dpad.up) {
m_socd_state.lastVertical = Id::UP;
} else {
m_socd_state.lastVertical = Id::DOWN;
}
if (input_state.dpad.left && input_state.dpad.right) {
if (m_socd_state.lastHorizontal == Id::RIGHT) {
input_state.dpad.right = false;
} else if (m_socd_state.lastHorizontal == Id::LEFT) {
input_state.dpad.left = false;
}
} else if (input_state.dpad.left) {
m_socd_state.lastHorizontal = Id::LEFT;
} else {
m_socd_state.lastHorizontal = Id::RIGHT;
}
}
Buttons::Buttons(const Config &config) : m_config(config), m_socd_state{Id::DOWN, Id::RIGHT} {
i2c_init(m_config.i2c.block, m_config.i2c.speed_hz);
gpio_set_function(m_config.i2c.sda_pin, GPIO_FUNC_I2C);
gpio_set_function(m_config.i2c.scl_pin, GPIO_FUNC_I2C);
gpio_pull_up(m_config.i2c.sda_pin);
gpio_pull_up(m_config.i2c.scl_pin);
m_mcp23017 = std::make_unique<Mcp23017>(m_config.i2c.address, m_config.i2c.block);
m_mcp23017->setDirection(0xFFFF); // All inputs
m_mcp23017->setPullup(0xFFFF); // All on
m_buttons.emplace(Id::UP, config.pins.dpad.up);
m_buttons.emplace(Id::DOWN, config.pins.dpad.down);
m_buttons.emplace(Id::LEFT, config.pins.dpad.left);
m_buttons.emplace(Id::RIGHT, config.pins.dpad.right);
m_buttons.emplace(Id::NORTH, config.pins.buttons.north);
m_buttons.emplace(Id::EAST, config.pins.buttons.east);
m_buttons.emplace(Id::SOUTH, config.pins.buttons.south);
m_buttons.emplace(Id::WEST, config.pins.buttons.west);
m_buttons.emplace(Id::L, config.pins.buttons.l);
m_buttons.emplace(Id::R, config.pins.buttons.r);
m_buttons.emplace(Id::START, config.pins.buttons.start);
m_buttons.emplace(Id::SELECT, config.pins.buttons.select);
m_buttons.emplace(Id::HOME, config.pins.buttons.home);
m_buttons.emplace(Id::SHARE, config.pins.buttons.share);
}
void Buttons::updateInputState(Utils::InputState &input_state) {
uint16_t gpio_state = ~m_mcp23017->read();
for (auto &button : m_buttons) {
button.second.setState(gpio_state & button.second.getGpioMask(), m_config.debounce_delay_ms);
}
input_state.dpad.up = m_buttons.at(Id::UP).getState();
input_state.dpad.down = m_buttons.at(Id::DOWN).getState();
input_state.dpad.left = m_buttons.at(Id::LEFT).getState();
input_state.dpad.right = m_buttons.at(Id::RIGHT).getState();
input_state.buttons.north = m_buttons.at(Id::NORTH).getState();
input_state.buttons.east = m_buttons.at(Id::EAST).getState();
input_state.buttons.south = m_buttons.at(Id::SOUTH).getState();
input_state.buttons.west = m_buttons.at(Id::WEST).getState();
input_state.buttons.l = m_buttons.at(Id::L).getState();
input_state.buttons.r = m_buttons.at(Id::R).getState();
input_state.buttons.start = m_buttons.at(Id::START).getState();
input_state.buttons.select = m_buttons.at(Id::SELECT).getState();
input_state.buttons.home = m_buttons.at(Id::HOME).getState();
input_state.buttons.share = m_buttons.at(Id::SHARE).getState();
socdClean(input_state);
}
} // namespace Doncon::Peripherals

View File

@ -6,7 +6,8 @@
namespace Doncon::Utils {
InputState::InputState()
: drum({{false, 0}, {false, 0}, {false, 0}, {false, 0}}),
: drum({{false, 0}, {false, 0}, {false, 0}, {false, 0}}), dpad({false, false, false, false}),
buttons({false, false, false, false, false, false, false, false, false, false}),
m_xinput_report({0x00, sizeof(xinput_report_t), 0, 0, 0, 0, 0, 0, 0, 0, {}}) {}
usb_report_t InputState::getReport(usb_mode_t mode) {
@ -29,23 +30,23 @@ usb_report_t InputState::getReport(usb_mode_t mode) {
usb_report_t InputState::getXinputReport() {
m_xinput_report.buttons1 = 0 //
| (false ? (1 << 0) : 0) // Dpad Up
| (drum.don_left.triggered ? (1 << 1) : 0) // Dpad Down
| (drum.ka_left.triggered ? (1 << 2) : 0) // Dpad Left
| (false ? (1 << 3) : 0) // Dpad Right
| (false ? (1 << 4) : 0) // Start
| (false ? (1 << 5) : 0) // Select
| (dpad.up ? (1 << 0) : 0) // Dpad Up
| ((dpad.down || drum.don_left.triggered) ? (1 << 1) : 0) // Dpad Down
| ((dpad.left || drum.ka_left.triggered) ? (1 << 2) : 0) // Dpad Left
| (dpad.right ? (1 << 3) : 0) // Dpad Right
| (buttons.start ? (1 << 4) : 0) // Start
| (buttons.select ? (1 << 5) : 0) // Select
| (false ? (1 << 6) : 0) // L3
| (false ? (1 << 7) : 0); // R3
m_xinput_report.buttons2 = 0 //
| (false ? (1 << 0) : 0) // L1
| (false ? (1 << 1) : 0) // R1
| (false ? (1 << 2) : 0) // Guide
| (drum.don_right.triggered ? (1 << 4) : 0) // A
| (drum.ka_right.triggered ? (1 << 5) : 0) // B
| (false ? (1 << 6) : 0) // X
| (false ? (1 << 7) : 0); // Y
| (buttons.l ? (1 << 0) : 0) // L1
| (buttons.r ? (1 << 1) : 0) // R1
| (buttons.home ? (1 << 2) : 0) // Guide
| ((buttons.south || drum.don_right.triggered) ? (1 << 4) : 0) // A
| ((buttons.east || drum.ka_right.triggered) ? (1 << 5) : 0) // B
| (buttons.west ? (1 << 6) : 0) // X
| (buttons.north ? (1 << 7) : 0); // Y
m_xinput_report.lt = 0;
m_xinput_report.rt = 0;