mirror of
https://github.com/ravinrabbid/DonCon2040.git
synced 2025-02-12 08:53:02 +01:00
Add support for additional controller buttons
This commit is contained in:
parent
d1de918629
commit
68457c60fe
@ -22,8 +22,14 @@ target_include_directories(${PROJECT_NAME}
|
|||||||
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
|
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${PROJECT_NAME} PUBLIC tinyusb_device tinyusb_board pico_stdlib hardware_adc
|
${PROJECT_NAME}
|
||||||
pico_multicore pio_ws2812)
|
PUBLIC tinyusb_device
|
||||||
|
tinyusb_board
|
||||||
|
pico_stdlib
|
||||||
|
hardware_adc
|
||||||
|
pico_multicore
|
||||||
|
pio_ws2812
|
||||||
|
mcp23017)
|
||||||
|
|
||||||
pico_enable_stdio_usb(${PROJECT_NAME} 1)
|
pico_enable_stdio_usb(${PROJECT_NAME} 1)
|
||||||
pico_enable_stdio_uart(${PROJECT_NAME} 0)
|
pico_enable_stdio_uart(${PROJECT_NAME} 0)
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#ifndef _GLOBALCONFIGURATION_H_
|
#ifndef _GLOBALCONFIGURATION_H_
|
||||||
#define _GLOBALCONFIGURATION_H_
|
#define _GLOBALCONFIGURATION_H_
|
||||||
|
|
||||||
|
#include "peripherals/Buttons.h"
|
||||||
#include "peripherals/Drum.h"
|
#include "peripherals/Drum.h"
|
||||||
#include "peripherals/StatusLed.h"
|
#include "peripherals/StatusLed.h"
|
||||||
|
|
||||||
|
#include "hardware/i2c.h"
|
||||||
|
|
||||||
namespace Doncon::Config::Default {
|
namespace Doncon::Config::Default {
|
||||||
|
|
||||||
const Peripherals::Drum::Config drum_config = {
|
const Peripherals::Drum::Config drum_config = {
|
||||||
@ -19,6 +22,41 @@ const Peripherals::Drum::Config drum_config = {
|
|||||||
17, // Debounce delay in milliseconds
|
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 = {
|
const Peripherals::StatusLed::Config led_config = {
|
||||||
{255, 0, 0}, // Don Left Color
|
{255, 0, 0}, // Don Left Color
|
||||||
{0, 0, 255}, // Ka Left Color
|
{0, 0, 255}, // Ka Left Color
|
||||||
|
110
include/peripherals/Buttons.h
Normal file
110
include/peripherals/Buttons.h
Normal 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_
|
@ -20,8 +20,20 @@ struct InputState {
|
|||||||
Pad don_left, ka_left, don_right, ka_right;
|
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:
|
public:
|
||||||
Drum drum;
|
Drum drum;
|
||||||
|
DPad dpad;
|
||||||
|
Buttons buttons;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xinput_report_t m_xinput_report;
|
xinput_report_t m_xinput_report;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include "peripherals/Buttons.h"
|
||||||
#include "peripherals/Drum.h"
|
#include "peripherals/Drum.h"
|
||||||
#include "peripherals/StatusLed.h"
|
#include "peripherals/StatusLed.h"
|
||||||
#include "usb/usb_driver.h"
|
#include "usb/usb_driver.h"
|
||||||
@ -36,6 +37,7 @@ int main() {
|
|||||||
|
|
||||||
Utils::InputState input_state;
|
Utils::InputState input_state;
|
||||||
Peripherals::Drum drum(Config::Default::drum_config);
|
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_mode_t mode = USB_MODE_XBOX360;
|
||||||
|
|
||||||
usb_driver_init(mode);
|
usb_driver_init(mode);
|
||||||
@ -46,6 +48,7 @@ int main() {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
drum.updateInputState(input_state);
|
drum.updateInputState(input_state);
|
||||||
|
buttons.updateInputState(input_state);
|
||||||
|
|
||||||
usb_driver_send_and_receive_report(input_state.getReport(mode));
|
usb_driver_send_and_receive_report(input_state.getReport(mode));
|
||||||
usb_driver_task();
|
usb_driver_task();
|
||||||
|
102
src/peripherals/Buttons.cpp
Normal file
102
src/peripherals/Buttons.cpp
Normal 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
|
@ -6,7 +6,8 @@
|
|||||||
namespace Doncon::Utils {
|
namespace Doncon::Utils {
|
||||||
|
|
||||||
InputState::InputState()
|
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, {}}) {}
|
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) {
|
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() {
|
usb_report_t InputState::getXinputReport() {
|
||||||
m_xinput_report.buttons1 = 0 //
|
m_xinput_report.buttons1 = 0 //
|
||||||
| (false ? (1 << 0) : 0) // Dpad Up
|
| (dpad.up ? (1 << 0) : 0) // Dpad Up
|
||||||
| (drum.don_left.triggered ? (1 << 1) : 0) // Dpad Down
|
| ((dpad.down || drum.don_left.triggered) ? (1 << 1) : 0) // Dpad Down
|
||||||
| (drum.ka_left.triggered ? (1 << 2) : 0) // Dpad Left
|
| ((dpad.left || drum.ka_left.triggered) ? (1 << 2) : 0) // Dpad Left
|
||||||
| (false ? (1 << 3) : 0) // Dpad Right
|
| (dpad.right ? (1 << 3) : 0) // Dpad Right
|
||||||
| (false ? (1 << 4) : 0) // Start
|
| (buttons.start ? (1 << 4) : 0) // Start
|
||||||
| (false ? (1 << 5) : 0) // Select
|
| (buttons.select ? (1 << 5) : 0) // Select
|
||||||
| (false ? (1 << 6) : 0) // L3
|
| (false ? (1 << 6) : 0) // L3
|
||||||
| (false ? (1 << 7) : 0); // R3
|
| (false ? (1 << 7) : 0); // R3
|
||||||
|
|
||||||
m_xinput_report.buttons2 = 0 //
|
m_xinput_report.buttons2 = 0 //
|
||||||
| (false ? (1 << 0) : 0) // L1
|
| (buttons.l ? (1 << 0) : 0) // L1
|
||||||
| (false ? (1 << 1) : 0) // R1
|
| (buttons.r ? (1 << 1) : 0) // R1
|
||||||
| (false ? (1 << 2) : 0) // Guide
|
| (buttons.home ? (1 << 2) : 0) // Guide
|
||||||
| (drum.don_right.triggered ? (1 << 4) : 0) // A
|
| ((buttons.south || drum.don_right.triggered) ? (1 << 4) : 0) // A
|
||||||
| (drum.ka_right.triggered ? (1 << 5) : 0) // B
|
| ((buttons.east || drum.ka_right.triggered) ? (1 << 5) : 0) // B
|
||||||
| (false ? (1 << 6) : 0) // X
|
| (buttons.west ? (1 << 6) : 0) // X
|
||||||
| (false ? (1 << 7) : 0); // Y
|
| (buttons.north ? (1 << 7) : 0); // Y
|
||||||
|
|
||||||
m_xinput_report.lt = 0;
|
m_xinput_report.lt = 0;
|
||||||
m_xinput_report.rt = 0;
|
m_xinput_report.rt = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user