first attempt at gcc

This commit is contained in:
Naxdy 2024-03-04 00:39:12 +01:00
parent e71c521032
commit bed51bf8e3
No known key found for this signature in database
GPG key ID: C0437AAE9755550F
5 changed files with 292 additions and 129 deletions

130
Cargo.lock generated
View file

@ -8,28 +8,13 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bare-metal"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
dependencies = [
"rustc_version 0.2.3",
"rustc_version",
]
[[package]]
@ -242,15 +227,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]]
name = "hash32"
version = "0.3.1"
@ -260,26 +236,13 @@ dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
dependencies = [
"atomic-polyfill",
"hash32 0.2.1",
"rustc_version 0.4.0",
"spin",
"stable_deref_trait",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32 0.3.1",
"hash32",
"stable_deref_trait",
]
@ -292,16 +255,6 @@ dependencies = [
"either",
]
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "naxgcc-fw"
version = "0.1.0"
@ -311,11 +264,13 @@ dependencies = [
"defmt",
"defmt-rtt",
"embedded-hal",
"fugit",
"packed_struct",
"panic-halt",
"panic-probe",
"rp2040-boot2",
"rp2040-hal",
"usb-device 0.3.1",
"usb-device",
"usbd-human-interface-device",
]
@ -343,15 +298,6 @@ dependencies = [
"num_enum_derive 0.5.11",
]
[[package]]
name = "num_enum"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1"
dependencies = [
"num_enum_derive 0.6.1",
]
[[package]]
name = "num_enum"
version = "0.7.2"
@ -372,17 +318,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "num_enum_derive"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "num_enum_derive"
version = "0.7.2"
@ -453,12 +388,6 @@ dependencies = [
"paste",
]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -541,7 +470,7 @@ dependencies = [
"rand_core",
"rp2040-hal-macros",
"rp2040-pac",
"usb-device 0.2.9",
"usb-device",
"vcell",
"void",
]
@ -576,24 +505,9 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver 0.9.0",
"semver",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver 1.0.22",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "0.9.0"
@ -603,27 +517,12 @@ dependencies = [
"semver-parser",
]
[[package]]
name = "semver"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -689,16 +588,8 @@ name = "usb-device"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508"
[[package]]
name = "usb-device"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e73e438f527e567fb3982f2370967821fab4f5aea84c42e218a211dd2002b6a2"
dependencies = [
"heapless 0.7.17",
"num_enum 0.6.1",
"portable-atomic",
"defmt",
]
[[package]]
@ -707,13 +598,14 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d69710303c06f23a1259d086bfb241212ae1ccfb5d582ebd596bb042d662ed73"
dependencies = [
"defmt",
"frunk",
"fugit",
"heapless 0.8.0",
"heapless",
"num_enum 0.7.2",
"option-block",
"packed_struct",
"usb-device 0.2.9",
"usb-device",
]
[[package]]

View file

@ -7,16 +7,18 @@ edition = "2021"
[dependencies]
rp2040-hal = { version = "0.9.2", features = ["rt", "critical-section-impl"] }
usbd-human-interface-device = "0.4.5"
usb-device = "0.3.1"
usbd-human-interface-device = { version = "0.4.5", features = ["defmt"] }
usb-device = "0.2"
cortex-m = "0.7.7"
cortex-m-rt = "0.7.3"
embedded-hal = { version = "0.2.7", features = ["unproven"] }
panic-probe = "0.3.1"
panic-halt = "0.2.0"
defmt = "0.3.6"
defmt-rtt = "0.4.0"
rp2040-boot2 = "0.3.0"
fugit = "0.3.7"
packed_struct = { version = "0.10.1", default_features = false }
panic-halt = "0.2.0"
# cargo build/run
[profile.dev]

View file

@ -83,7 +83,7 @@
devShells.default = pkgs.mkShell {
nativeBuildInputs = builtins.attrValues {
inherit rustToolchain;
inherit (pkgs) gcc-arm-embedded flip-link elf2uf2-rs;
inherit (pkgs) gcc-arm-embedded flip-link elf2uf2-rs picotool;
};
inherit RUSTFLAGS CARGO_BUILD_TARGET;

214
src/gcc_hid.rs Normal file
View file

@ -0,0 +1,214 @@
use core::default::Default;
use defmt::unwrap;
use packed_struct::{derive::PackedStruct, prelude::packed_bits, types::Integer, PackedStruct};
use usb_device::bus::{UsbBus, UsbBusAllocator};
use usbd_human_interface_device::{
descriptor::InterfaceProtocol,
device::DeviceClass,
interface::{
InBytes8, Interface, InterfaceBuilder, InterfaceConfig, OutNone, ReportSingle,
UsbAllocatable,
},
UsbHidError,
};
use fugit::ExtU32;
#[rustfmt::skip]
pub const GCC_REPORT_DESCRIPTOR: &[u8] = &[
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0xA1, 0x03, // Collection (Report)
0x85, 0x11, // Report ID (17)
0x19, 0x00, // Usage Minimum (Undefined)
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x05, // Report Count (5)
0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x03, // Collection (Report)
0x85, 0x21, // Report ID (33)
0x05, 0x00, // Usage Page (Undefined)
0x15, 0x00, // Logical Minimum (0)
0x25, 0xFF, // Logical Maximum (-1)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x08, // Usage Maximum (0x08)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x08, // Report Size (8)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x33, // Usage (Rx)
0x09, 0x34, // Usage (Ry)
0x09, 0x35, // Usage (Rz)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x75, 0x08, // Report Size (8)
0x95, 0x06, // Report Count (6)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xA1, 0x03, // Collection (Report)
0x85, 0x13, // Report ID (19)
0x19, 0x00, // Usage Minimum (Undefined)
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0, // End Collection
];
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct)]
#[packed_struct(endian = "msb", size_bytes = "1")]
pub struct Buttons1 {
#[packed_field(size_bits = "1")]
pub button_a: bool,
#[packed_field(size_bits = "1")]
pub button_b: bool,
#[packed_field(size_bits = "1")]
pub button_x: bool,
#[packed_field(size_bits = "1")]
pub button_y: bool,
#[packed_field(size_bits = "1")]
pub dpad_left: bool,
#[packed_field(size_bits = "1")]
pub dpad_right: bool,
#[packed_field(size_bits = "1")]
pub dpad_down: bool,
#[packed_field(size_bits = "1")]
pub dpad_up: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct)]
#[packed_struct(endian = "msb", size_bytes = "1")]
pub struct Buttons2 {
#[packed_field(size_bits = "1")]
pub button_start: bool,
#[packed_field(size_bits = "1")]
pub button_z: bool,
#[packed_field(size_bits = "1")]
pub button_r: bool,
#[packed_field(size_bits = "1")]
pub button_l: bool,
#[packed_field(size_bits = "4")]
pub blank1: Integer<u8, packed_bits::Bits<4>>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct)]
#[packed_struct(endian = "msb", size_bytes = "8")]
pub struct GcReport {
#[packed_field(size_bits = "8")]
pub buttons_1: Buttons1,
#[packed_field(size_bits = "8")]
pub buttons_2: Buttons2,
#[packed_field(size_bits = "8")]
pub stick_x: u8,
#[packed_field(size_bits = "8")]
pub stick_y: u8,
#[packed_field(size_bits = "8")]
pub cstick_x: u8,
#[packed_field(size_bits = "8")]
pub cstick_y: u8,
#[packed_field(size_bits = "8")]
pub trigger_l: u8,
#[packed_field(size_bits = "8")]
pub trigger_r: u8,
}
pub struct GcConfig<'a> {
interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>,
}
impl<'a> GcConfig<'a> {
#[must_use]
pub fn new(interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>) -> Self {
Self { interface }
}
}
impl<'a> Default for GcConfig<'a> {
#[must_use]
fn default() -> Self {
let i = unwrap!(unwrap!(InterfaceBuilder::new(GCC_REPORT_DESCRIPTOR))
.boot_device(InterfaceProtocol::None)
.description("NaxGCC")
.in_endpoint(10.millis()));
Self::new(i.without_out_endpoint().build())
}
}
impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for GcConfig<'a> {
type Allocated = GcController<'a, B>;
fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
Self::Allocated {
interface: Interface::new(usb_alloc, self.interface),
}
}
}
pub struct GcController<'a, B: UsbBus> {
interface: Interface<'a, B, InBytes8, OutNone, ReportSingle>,
}
impl<'a, B: UsbBus> GcController<'a, B> {
pub fn write_report(&mut self, report: &GcReport) -> Result<(), UsbHidError> {
let report = get_gcinput_hid_report(report);
self.interface
.write_report(&report)
.map(|_| ())
.map_err(UsbHidError::from)
}
}
impl<'a, B: UsbBus> DeviceClass<'a> for GcController<'a, B> {
type I = Interface<'a, B, InBytes8, OutNone, ReportSingle>;
fn interface(&mut self) -> &mut Self::I {
&mut self.interface
}
fn reset(&mut self) {}
fn tick(&mut self) -> Result<(), UsbHidError> {
Ok(())
}
}
fn get_gcinput_hid_report(input_state: &GcReport) -> [u8; 37] {
static mut GC_FIRST: bool = false;
let mut buffer = [0u8; 37];
buffer[0] = 0x21;
let data = input_state.pack().expect("Failed to pack GC input data");
if unsafe { !GC_FIRST } {
buffer[1] |= 0x04;
buffer[10] |= 0x04;
buffer[19] |= 0x04;
buffer[28] |= 0x04;
unsafe { GC_FIRST = true };
} else {
// controller in "port 1"
buffer[2..=10].copy_from_slice(&data[0..=8]);
}
buffer
}

View file

@ -8,9 +8,15 @@
#![no_std]
#![no_main]
mod gcc_hid;
use gcc_hid::{GcConfig, GcReport};
use fugit::ExtU32;
// Ensure we halt the program on panic (if we don't mention this crate it won't
// be linked)
use defmt_rtt as _;
use panic_halt as _;
// Alias for our HAL crate
@ -21,7 +27,12 @@ use rp2040_hal as hal;
use hal::pac;
// Some traits we need
use embedded_hal::{blocking::delay::DelayMs, digital::v2::OutputPin};
use embedded_hal::{digital::v2::OutputPin, timer::CountDown};
use usb_device::{
bus::UsbBusAllocator,
device::{UsbDeviceBuilder, UsbVidPid},
};
use usbd_human_interface_device::{usb_class::UsbHidClassBuilder, UsbHidError};
/// 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.
@ -63,7 +74,10 @@ fn main() -> ! {
.ok()
.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(10.millis());
// The single-cycle I/O block controls our GPIO pins
let sio = hal::Sio::new(pac.SIO);
@ -76,12 +90,53 @@ fn main() -> ! {
&mut pac.RESETS,
);
let mut gcc_state = GcReport::default();
// usb parts
let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
pac.USBCTRL_REGS,
pac.USBCTRL_DPRAM,
clocks.usb_clock,
true,
&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")
.device_class(0)
.device_protocol(0)
.device_sub_class(0)
.self_powered(false)
.max_power(500)
.max_packet_size_0(64)
.build();
// Configure GPIO25 as an output
let mut led_pin = pins.gpio25.into_push_pull_output();
loop {
led_pin.set_high().unwrap();
timer.delay_ms(500);
led_pin.set_low().unwrap();
timer.delay_ms(500);
if poll_timer.wait().is_ok() {
match gcc.device().write_report(&gcc_state) {
Err(UsbHidError::WouldBlock) => {}
Ok(_) => {}
Err(e) => {
panic!("Error: {:?}", e);
}
}
gcc_state.buttons_1.button_a = !gcc_state.buttons_1.button_a;
if gcc_state.buttons_1.button_a {
led_pin.set_high().unwrap();
} else {
led_pin.set_low().unwrap();
}
}
if usb_dev.poll(&mut [&mut gcc]) {}
}
}