implement rudimentary button handling, prepare SPI
This commit is contained in:
parent
32fff25889
commit
6ba86985d5
4 changed files with 265 additions and 81 deletions
|
@ -83,7 +83,7 @@
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
nativeBuildInputs = builtins.attrValues {
|
nativeBuildInputs = builtins.attrValues {
|
||||||
inherit rustToolchain;
|
inherit rustToolchain;
|
||||||
inherit (pkgs) gcc-arm-embedded flip-link elf2uf2-rs picotool probe-rs;
|
inherit (pkgs) gcc-arm-embedded flip-link elf2uf2-rs picotool probe-rs cargo-expand;
|
||||||
};
|
};
|
||||||
|
|
||||||
CARGO_TARGET_THUMBV6M_NONE_EABI_RUNNER = "probe-rs run --chip RP2040 --protocol swd";
|
CARGO_TARGET_THUMBV6M_NONE_EABI_RUNNER = "probe-rs run --chip RP2040 --protocol swd";
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
use core::default::Default;
|
use core::default::Default;
|
||||||
|
|
||||||
use defmt::{error, info, unwrap};
|
use defmt::{error, info, unwrap, Debug2Format};
|
||||||
use packed_struct::{derive::PackedStruct, prelude::packed_bits, types::Integer, PackedStruct};
|
use embedded_hal::timer::CountDown as _;
|
||||||
use usb_device::bus::{UsbBus, UsbBusAllocator};
|
use fugit::ExtU32;
|
||||||
|
use packed_struct::{derive::PackedStruct, PackedStruct};
|
||||||
|
use rp2040_hal::timer::CountDown;
|
||||||
|
use usb_device::{
|
||||||
|
bus::{UsbBus, UsbBusAllocator},
|
||||||
|
device::{UsbDeviceBuilder, UsbVidPid},
|
||||||
|
};
|
||||||
use usbd_human_interface_device::{
|
use usbd_human_interface_device::{
|
||||||
descriptor::InterfaceProtocol,
|
descriptor::InterfaceProtocol,
|
||||||
device::DeviceClass,
|
device::DeviceClass,
|
||||||
|
@ -10,10 +16,11 @@ use usbd_human_interface_device::{
|
||||||
InBytes64, Interface, InterfaceBuilder, InterfaceConfig, OutBytes64, ReportSingle,
|
InBytes64, Interface, InterfaceBuilder, InterfaceConfig, OutBytes64, ReportSingle,
|
||||||
UsbAllocatable,
|
UsbAllocatable,
|
||||||
},
|
},
|
||||||
|
usb_class::UsbHidClassBuilder,
|
||||||
UsbHidError,
|
UsbHidError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use fugit::ExtU32;
|
use crate::input::GCC_STATE;
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub const GCC_REPORT_DESCRIPTOR: &[u8] = &[
|
pub const GCC_REPORT_DESCRIPTOR: &[u8] = &[
|
||||||
|
@ -105,7 +112,7 @@ pub struct Buttons2 {
|
||||||
#[packed_field(bits = "3")]
|
#[packed_field(bits = "3")]
|
||||||
pub button_l: bool,
|
pub button_l: bool,
|
||||||
#[packed_field(bits = "4..=7")]
|
#[packed_field(bits = "4..=7")]
|
||||||
pub blank1: Integer<u8, packed_bits::Bits<4>>,
|
pub blank1: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct)]
|
||||||
|
@ -236,3 +243,59 @@ fn get_gcinput_hid_report(input_state: &GcReport) -> [u8; 37] {
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn usb_transfer_loop<'a, T: UsbBus>(
|
||||||
|
usb_bus: UsbBusAllocator<T>,
|
||||||
|
mut poll_timer: CountDown<'a>,
|
||||||
|
) -> ! {
|
||||||
|
info!("Got to this point");
|
||||||
|
let mut gcc = UsbHidClassBuilder::new()
|
||||||
|
.add_device(GcConfig::default())
|
||||||
|
.build(&usb_bus);
|
||||||
|
|
||||||
|
info!("Got the gc");
|
||||||
|
|
||||||
|
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x057e, 0x0337))
|
||||||
|
.manufacturer("Naxdy")
|
||||||
|
.product("NaxGCC")
|
||||||
|
.serial_number("fleeb") // TODO: Get this from the flash unique id
|
||||||
|
.device_class(0)
|
||||||
|
.device_protocol(0)
|
||||||
|
.device_sub_class(0)
|
||||||
|
.self_powered(false)
|
||||||
|
.max_power(500)
|
||||||
|
.max_packet_size_0(64)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
poll_timer.start(1.millis());
|
||||||
|
|
||||||
|
info!("Got here");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if poll_timer.wait().is_ok() {
|
||||||
|
match gcc.device().write_report(&(unsafe { GCC_STATE })) {
|
||||||
|
Err(UsbHidError::WouldBlock) => {}
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error: {:?}", Debug2Format(&e));
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if usb_dev.poll(&mut [&mut gcc]) {
|
||||||
|
match gcc.device().read_report() {
|
||||||
|
Err(UsbHidError::WouldBlock) => {}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to read report: {:?}", Debug2Format(&e));
|
||||||
|
}
|
||||||
|
Ok(report) => {
|
||||||
|
info!("Received report: {:08x}", report.packet);
|
||||||
|
// rumble packet
|
||||||
|
if report.packet[0] == 0x11 {
|
||||||
|
info!("Received rumble info: Controller1 ({:08x}) Controller2 ({:08x}) Controller3 ({:08x}) Controller4 ({:08x})", report.packet[1], report.packet[2], report.packet[3], report.packet[4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
113
src/input.rs
Normal file
113
src/input.rs
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
use embedded_hal::digital::{v2::InputPin, v2::IoPin};
|
||||||
|
use rp2040_hal::gpio::{FunctionSpi, Pin, PinId, PullDown};
|
||||||
|
|
||||||
|
use crate::gcc_hid::{Buttons1, Buttons2, GcReport};
|
||||||
|
|
||||||
|
pub static mut GCC_STATE: GcReport = GcReport {
|
||||||
|
buttons_1: Buttons1 {
|
||||||
|
button_a: false,
|
||||||
|
button_b: false,
|
||||||
|
button_x: false,
|
||||||
|
button_y: false,
|
||||||
|
dpad_left: false,
|
||||||
|
dpad_right: false,
|
||||||
|
dpad_down: false,
|
||||||
|
dpad_up: false,
|
||||||
|
},
|
||||||
|
buttons_2: Buttons2 {
|
||||||
|
button_start: false,
|
||||||
|
button_z: false,
|
||||||
|
button_r: false,
|
||||||
|
button_l: false,
|
||||||
|
blank1: 0,
|
||||||
|
},
|
||||||
|
stick_x: 0,
|
||||||
|
stick_y: 0,
|
||||||
|
cstick_x: 0,
|
||||||
|
cstick_y: 0,
|
||||||
|
trigger_l: 0,
|
||||||
|
trigger_r: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! pin_inputs {
|
||||||
|
($x:tt {$($f:tt: $g:tt),*}) => {
|
||||||
|
pub struct $x<$($g,)*>
|
||||||
|
where
|
||||||
|
$(
|
||||||
|
$g: InputPin,
|
||||||
|
)*
|
||||||
|
{
|
||||||
|
$(
|
||||||
|
pub $f: $g,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assign_pins {
|
||||||
|
($gcc:expr, $inputs:tt, {$($p:tt.$c:tt),*}) => {
|
||||||
|
$(
|
||||||
|
$gcc.$p.$c = $inputs.$c.is_low().map_err(|_| "").unwrap();
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_inputs!(BasicInputs {
|
||||||
|
button_a: A,
|
||||||
|
button_b: B,
|
||||||
|
button_x: X,
|
||||||
|
button_y: Y,
|
||||||
|
dpad_left: Dl,
|
||||||
|
dpad_right: Dr,
|
||||||
|
dpad_down: Dd,
|
||||||
|
dpad_up: Du,
|
||||||
|
button_start: S,
|
||||||
|
button_z: Z,
|
||||||
|
button_r: R,
|
||||||
|
button_l: L
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn input_loop<
|
||||||
|
A: InputPin,
|
||||||
|
B: InputPin,
|
||||||
|
X: InputPin,
|
||||||
|
Y: InputPin,
|
||||||
|
Dl: InputPin,
|
||||||
|
Dr: InputPin,
|
||||||
|
Dd: InputPin,
|
||||||
|
Du: InputPin,
|
||||||
|
S: InputPin,
|
||||||
|
Z: InputPin,
|
||||||
|
R: InputPin,
|
||||||
|
L: InputPin,
|
||||||
|
>(
|
||||||
|
basic_inputs: BasicInputs<A, B, X, Y, Dl, Dr, Dd, Du, S, Z, R, L>,
|
||||||
|
) -> ! {
|
||||||
|
let update_gcc_state = || unsafe {
|
||||||
|
// simple booleans
|
||||||
|
assign_pins!(GCC_STATE, basic_inputs, {
|
||||||
|
buttons_1.button_a,
|
||||||
|
buttons_1.button_b,
|
||||||
|
buttons_1.button_x,
|
||||||
|
buttons_1.button_y,
|
||||||
|
buttons_1.dpad_left,
|
||||||
|
buttons_1.dpad_right,
|
||||||
|
buttons_1.dpad_down,
|
||||||
|
buttons_1.dpad_up,
|
||||||
|
buttons_2.button_start,
|
||||||
|
buttons_2.button_z,
|
||||||
|
buttons_2.button_r,
|
||||||
|
buttons_2.button_l
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: sticks
|
||||||
|
GCC_STATE.cstick_x = 127;
|
||||||
|
GCC_STATE.cstick_y = 127;
|
||||||
|
GCC_STATE.stick_x = 127;
|
||||||
|
GCC_STATE.stick_y = 127;
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
update_gcc_state();
|
||||||
|
}
|
||||||
|
}
|
154
src/main.rs
154
src/main.rs
|
@ -10,17 +10,16 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
mod flash_mem;
|
mod flash_mem;
|
||||||
mod gcc_hid;
|
mod gcc_hid;
|
||||||
|
mod input;
|
||||||
|
|
||||||
use core::fmt::Write;
|
use defmt::{error, info};
|
||||||
use defmt::{error, info, Debug2Format};
|
use gcc_hid::GcConfig;
|
||||||
use gcc_hid::{GcConfig, GcReport};
|
|
||||||
|
|
||||||
use fugit::ExtU32;
|
use fugit::{ExtU32, RateExtU32};
|
||||||
|
|
||||||
// Ensure we halt the program on panic (if we don't mention this crate it won't
|
// Ensure we halt the program on panic (if we don't mention this crate it won't
|
||||||
// be linked)
|
// be linked)
|
||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
use packed_struct::PackedStruct;
|
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
|
|
||||||
// Alias for our HAL crate
|
// Alias for our HAL crate
|
||||||
|
@ -29,25 +28,32 @@ use rp2040_hal as hal;
|
||||||
// A shorter alias for the Peripheral Access Crate, which provides low-level
|
// A shorter alias for the Peripheral Access Crate, which provides low-level
|
||||||
// register access
|
// register access
|
||||||
use hal::{
|
use hal::{
|
||||||
gpio::FunctionUart,
|
gpio::FunctionSpi,
|
||||||
pac,
|
multicore::{Multicore, Stack},
|
||||||
uart::{UartConfig, UartPeripheral},
|
pac, Spi,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Some traits we need
|
// Some traits we need
|
||||||
use embedded_hal::{
|
use embedded_hal::{
|
||||||
blocking::delay::DelayMs,
|
blocking::spi::Transfer,
|
||||||
digital::v2::{InputPin, OutputPin},
|
digital::v2::OutputPin,
|
||||||
|
spi::{FullDuplex, MODE_0},
|
||||||
timer::CountDown,
|
timer::CountDown,
|
||||||
};
|
};
|
||||||
use rp2040_hal::Clock;
|
|
||||||
use usb_device::{
|
use usb_device::{
|
||||||
bus::UsbBusAllocator,
|
bus::UsbBusAllocator,
|
||||||
device::{UsbDeviceBuilder, UsbDeviceState, UsbVidPid},
|
device::{UsbDeviceBuilder, UsbVidPid},
|
||||||
};
|
};
|
||||||
use usbd_human_interface_device::{usb_class::UsbHidClassBuilder, UsbHidError};
|
use usbd_human_interface_device::usb_class::UsbHidClassBuilder;
|
||||||
|
|
||||||
use crate::flash_mem::{read_from_flash, write_to_flash};
|
use crate::{
|
||||||
|
flash_mem::{read_from_flash, write_to_flash},
|
||||||
|
gcc_hid::usb_transfer_loop,
|
||||||
|
input::{input_loop, BasicInputs},
|
||||||
|
};
|
||||||
|
|
||||||
|
static mut CORE1_STACK: Stack<4096> = Stack::new();
|
||||||
|
|
||||||
/// The linker will place this boot block at the start of our program image. We
|
/// The linker will place this boot block at the start of our program image. We
|
||||||
/// need this to help the ROM bootloader get our code up and running.
|
/// need this to help the ROM bootloader get our code up and running.
|
||||||
|
@ -89,13 +95,10 @@ fn main() -> ! {
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut timer = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
|
let timer = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
|
||||||
|
|
||||||
let mut poll_timer = timer.count_down();
|
|
||||||
poll_timer.start(1.millis());
|
|
||||||
|
|
||||||
// The single-cycle I/O block controls our GPIO pins
|
// The single-cycle I/O block controls our GPIO pins
|
||||||
let sio = hal::Sio::new(pac.SIO);
|
let mut sio = hal::Sio::new(pac.SIO);
|
||||||
|
|
||||||
// Set the pins to their default state
|
// Set the pins to their default state
|
||||||
let pins = hal::gpio::Pins::new(
|
let pins = hal::gpio::Pins::new(
|
||||||
|
@ -105,8 +108,6 @@ fn main() -> ! {
|
||||||
&mut pac.RESETS,
|
&mut pac.RESETS,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut gcc_state = GcReport::default();
|
|
||||||
|
|
||||||
// usb parts
|
// usb parts
|
||||||
let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
|
let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
|
||||||
pac.USBCTRL_REGS,
|
pac.USBCTRL_REGS,
|
||||||
|
@ -116,30 +117,6 @@ fn main() -> ! {
|
||||||
&mut pac.RESETS,
|
&mut pac.RESETS,
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut gcc = UsbHidClassBuilder::new()
|
|
||||||
.add_device(GcConfig::default())
|
|
||||||
.build(&usb_bus);
|
|
||||||
|
|
||||||
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x057e, 0x0337))
|
|
||||||
.manufacturer("Naxdy")
|
|
||||||
.product("NaxGCC")
|
|
||||||
.serial_number("fleeb")
|
|
||||||
.device_class(0)
|
|
||||||
.device_protocol(0)
|
|
||||||
.device_sub_class(0)
|
|
||||||
.self_powered(false)
|
|
||||||
.max_power(500)
|
|
||||||
.max_packet_size_0(64)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
gcc_state.stick_x = 0;
|
|
||||||
gcc_state.stick_y = 0;
|
|
||||||
gcc_state.trigger_l = 21;
|
|
||||||
gcc_state.cstick_x = 127;
|
|
||||||
gcc_state.cstick_y = 127;
|
|
||||||
|
|
||||||
let btn_pin = pins.gpio15.into_pull_up_input();
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let some_byte: u8 = 0xAB;
|
let some_byte: u8 = 0xAB;
|
||||||
info!("Byte to be written is {:02X}", some_byte);
|
info!("Byte to be written is {:02X}", some_byte);
|
||||||
|
@ -148,38 +125,69 @@ fn main() -> ! {
|
||||||
info!("Byte read from flash is {:02X}", r);
|
info!("Byte read from flash is {:02X}", r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo);
|
||||||
|
let cores = mc.cores();
|
||||||
|
let core1 = &mut cores[1];
|
||||||
|
|
||||||
|
let _transfer_loop = core1
|
||||||
|
.spawn(unsafe { &mut CORE1_STACK.mem }, move || {
|
||||||
|
let mut poll_timer = timer.count_down();
|
||||||
|
poll_timer.start(1.millis());
|
||||||
|
|
||||||
|
usb_transfer_loop(usb_bus, poll_timer)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
info!("Initialized");
|
info!("Initialized");
|
||||||
|
|
||||||
// Configure GPIO25 as an output
|
let mut ccs = pins.gpio23.into_push_pull_output();
|
||||||
let mut led_pin = pins.gpio25.into_push_pull_output();
|
let mut acs = pins.gpio24.into_push_pull_output();
|
||||||
loop {
|
|
||||||
if poll_timer.wait().is_ok() {
|
ccs.set_low();
|
||||||
match gcc.device().write_report(&gcc_state) {
|
acs.set_low();
|
||||||
Err(UsbHidError::WouldBlock) => {}
|
|
||||||
Ok(_) => {}
|
let spi_device = pac.SPI0;
|
||||||
|
|
||||||
|
let clk = pins.gpio6.into_function::<FunctionSpi>();
|
||||||
|
let tx = pins.gpio7.into_function::<FunctionSpi>();
|
||||||
|
let rx = pins.gpio4.into_function::<FunctionSpi>();
|
||||||
|
|
||||||
|
let spi_pin_layout = (tx, rx, clk);
|
||||||
|
|
||||||
|
let mut spi = Spi::<_, _, _, 8>::new(spi_device, spi_pin_layout).init(
|
||||||
|
&mut pac.RESETS,
|
||||||
|
3_000_000u32.Hz(),
|
||||||
|
3_000_000u32.Hz(),
|
||||||
|
MODE_0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut w = [0b11010000u8; 3];
|
||||||
|
|
||||||
|
info!("W is {}", w);
|
||||||
|
|
||||||
|
let r = spi.transfer(&mut w);
|
||||||
|
|
||||||
|
match r {
|
||||||
|
Ok(t) => {
|
||||||
|
info!("T is {}", t)
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
led_pin.set_high().unwrap();
|
error!("SPI transfer failed: {}", e);
|
||||||
error!("Error: {:?}", Debug2Format(&e));
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if usb_dev.poll(&mut [&mut gcc]) {
|
|
||||||
match gcc.device().read_report() {
|
|
||||||
Err(UsbHidError::WouldBlock) => {}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to read report: {:?}", Debug2Format(&e));
|
|
||||||
}
|
|
||||||
Ok(report) => {
|
|
||||||
info!("Received report: {:08x}", report.packet);
|
|
||||||
// rumble packet
|
|
||||||
if report.packet[0] == 0x11 {
|
|
||||||
info!("Received rumble info: Controller1 ({:08x}) Controller2 ({:08x}) Controller3 ({:08x}) Controller4 ({:08x})", report.packet[1], report.packet[2], report.packet[3], report.packet[4]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gcc_state.buttons_2.button_z = btn_pin.is_low().unwrap();
|
input_loop(BasicInputs {
|
||||||
}
|
button_a: pins.gpio17.into_pull_up_input(),
|
||||||
|
button_b: pins.gpio16.into_pull_up_input(),
|
||||||
|
button_x: pins.gpio18.into_pull_up_input(),
|
||||||
|
button_y: pins.gpio19.into_pull_up_input(),
|
||||||
|
button_z: pins.gpio20.into_pull_up_input(),
|
||||||
|
button_r: pins.gpio21.into_pull_up_input(),
|
||||||
|
button_l: pins.gpio22.into_pull_up_input(),
|
||||||
|
dpad_left: pins.gpio8.into_pull_up_input(),
|
||||||
|
dpad_up: pins.gpio9.into_pull_up_input(),
|
||||||
|
dpad_down: pins.gpio10.into_pull_up_input(),
|
||||||
|
dpad_right: pins.gpio11.into_pull_up_input(),
|
||||||
|
button_start: pins.gpio5.into_pull_up_input(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue