diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5c811c6..f033dc5 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -4,6 +4,9 @@ jobs: build_and_test: name: Build and test runs-on: ubuntu-latest + defaults: + run: + working-directory: wii-ext strategy: matrix: toolchain: diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index f2e72d0..bde0511 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -4,6 +4,9 @@ jobs: clippy_check: name: Run Clippy runs-on: ubuntu-20.04 + defaults: + run: + working-directory: wii-ext env: RUSTFLAGS: "-D warnings" steps: diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 9d7b806..ac2b110 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -4,6 +4,9 @@ jobs: fmt: name: Rustfmt runs-on: ubuntu-20.04 + defaults: + run: + working-directory: wii-ext env: RUSTFLAGS: "-D warnings" steps: diff --git a/examples/rp2040-hal-blocking/.cargo/config.toml b/examples/rp2040-hal-blocking/.cargo/config.toml new file mode 100644 index 0000000..730b910 --- /dev/null +++ b/examples/rp2040-hal-blocking/.cargo/config.toml @@ -0,0 +1,15 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs run --chip RP2040 --protocol swd" +# runner = "elf2uf2-rs -d" + +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", +] + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "debug" diff --git a/examples/rp2040-hal-blocking/.gitignore b/examples/rp2040-hal-blocking/.gitignore new file mode 100644 index 0000000..3a0e903 --- /dev/null +++ b/examples/rp2040-hal-blocking/.gitignore @@ -0,0 +1,14 @@ +**/*.rs.bk +.#* +.gdb_history +Cargo.lock +target/ + +# editor files +.vscode/* +!.vscode/*.md +!.vscode/*.svd +!.vscode/launch.json +!.vscode/tasks.json +!.vscode/extensions.json +!.vscode/settings.json \ No newline at end of file diff --git a/examples/rp2040-hal-blocking/.vscode/settings.json b/examples/rp2040-hal-blocking/.vscode/settings.json new file mode 100644 index 0000000..2c5e3da --- /dev/null +++ b/examples/rp2040-hal-blocking/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv6m-none-eabi", + "rust-analyzer.checkOnSave.allTargets": false, + "editor.formatOnSave": true, + "[toml]": { + "editor.formatOnSave": false, + } +} \ No newline at end of file diff --git a/examples/rp2040-hal-blocking/Cargo.toml b/examples/rp2040-hal-blocking/Cargo.toml new file mode 100644 index 0000000..759b52c --- /dev/null +++ b/examples/rp2040-hal-blocking/Cargo.toml @@ -0,0 +1,25 @@ +[package] +authors = ["9names"] +edition = "2018" +readme = "README.md" +name = "wii-ext_blocking_demo" +version = "0.1.0" +resolver = "2" +publish = false + +[dependencies] +cortex-m = "0.7.3" +cortex-m-rt = "0.7.0" +embedded-hal = { version = "0.2.7", features = ["unproven"] } +embedded-time = "0.12.0" +defmt = "0.3.0" +defmt-rtt = "0.4.0" +panic-probe = { version = "0.3.0", features = ["print-defmt"] } +fugit = "0.3.6" +usb-device = "0.2" +usbd-human-interface-device = "0.4.5" +wii-ext = { version = "0.3.0", features = ["defmt_print",], path = "../../wii-ext" } +rp-pico = "0.8.0" + +[profile.release] +debug = 2 \ No newline at end of file diff --git a/examples/rp2040-hal-blocking/README.md b/examples/rp2040-hal-blocking/README.md new file mode 100644 index 0000000..e535713 --- /dev/null +++ b/examples/rp2040-hal-blocking/README.md @@ -0,0 +1,4 @@ +# Example project for wii-ext using Raspberry Pi Pico + +Just a simple example of how to use wii-ext-rs. +Grab a pico, wire up a classic controller or nunchuk and test it out! diff --git a/examples/rp2040-hal-blocking/build.rs b/examples/rp2040-hal-blocking/build.rs new file mode 100644 index 0000000..d534cc3 --- /dev/null +++ b/examples/rp2040-hal-blocking/build.rs @@ -0,0 +1,31 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/examples/rp2040-hal-blocking/memory.x b/examples/rp2040-hal-blocking/memory.x new file mode 100644 index 0000000..070eac7 --- /dev/null +++ b/examples/rp2040-hal-blocking/memory.x @@ -0,0 +1,15 @@ +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; \ No newline at end of file diff --git a/examples/rp2040-hal-blocking/src/main.rs b/examples/rp2040-hal-blocking/src/main.rs new file mode 100644 index 0000000..7144d7c --- /dev/null +++ b/examples/rp2040-hal-blocking/src/main.rs @@ -0,0 +1,152 @@ +//! Interact with a Wii extension controller via the wii-ext crate on a Pico board +//! +//! It will enumerate as a USB joystick, which you can use to control a game +#![no_std] +#![no_main] + +use defmt::*; +use defmt_rtt as _; +use panic_probe as _; + +use rp_pico as bsp; +use bsp::hal::{ + self, + entry, + clocks::{init_clocks_and_plls, Clock}, + gpio::FunctionI2C, + pac, + sio::Sio, + watchdog::Watchdog, +}; +use fugit::RateExtU32; +use wii_ext::{classic_sync::Classic, core::classic::ClassicReadingCalibrated}; + +use usb_device::class_prelude::*; +use usb_device::prelude::*; +use usbd_human_interface_device::device::joystick::JoystickReport; +use usbd_human_interface_device::prelude::*; + +#[entry] +fn main() -> ! { + info!("Program start"); + let mut pac = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::take().unwrap(); + let mut watchdog = Watchdog::new(pac.WATCHDOG); + let sio = Sio::new(pac.SIO); + + // External high-speed crystal on the pico board is 12Mhz + let external_xtal_freq_hz = 12_000_000u32; + let clocks = init_clocks_and_plls( + external_xtal_freq_hz, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()); + + let pins = bsp::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new( + pac.USBCTRL_REGS, + pac.USBCTRL_DPRAM, + clocks.usb_clock, + true, + &mut pac.RESETS, + )); + + let mut joy = UsbHidClassBuilder::new() + .add_device(usbd_human_interface_device::device::joystick::JoystickConfig::default()) + .build(&usb_bus); + + //https://pid.codes + let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x1209, 0x0001)) + .manufacturer("usbd-human-interface-device") + .product("Rusty joystick") + .serial_number("TEST") + .build(); + + let sda_pin = pins.gpio8.into_function::(); + let scl_pin = pins.gpio9.into_function::(); + + let i2c = bsp::hal::I2C::i2c0( + pac.I2C0, + sda_pin, + scl_pin, + 100.kHz(), + &mut pac.RESETS, + &clocks.peripheral_clock, + ); + + // Create, initialise and calibrate the controller + let mut controller = Classic::new(i2c, &mut delay).unwrap(); + + let hi_res = false; + + // Enable hi-resolution mode. This also updates calibration + // Don't really need it for this single stick mode. Plus it might make recovery easier... + if hi_res { + controller.enable_hires(&mut delay).unwrap(); + } + + // If you have a Nunchuk controller, use this instead. + // let mut controller = Nunchuk::new(i2c, &mut delay).unwrap(); + loop { + // Some controllers need a delay between reads or they become unhappy + // delay.delay_ms(10); + + // Capture the current button and axis values + let input = controller.read_blocking(&mut delay); + if let Ok(input) = input { + match joy.device().write_report(&get_report(&input)) { + Err(UsbHidError::WouldBlock) => {} + Ok(_) => {} + Err(e) => { + core::panic!("Failed to write joystick report: {:?}", e) + } + } + // Print inputs from the controller + debug!("{:?}", input); + } else { + // re-init controller on failure + let _ = controller.init(&mut delay); + if hi_res { + let _ = controller.enable_hires(&mut delay); + } + } + + if usb_dev.poll(&mut [&mut joy]) {} + } +} + +fn get_report(input: &ClassicReadingCalibrated) -> JoystickReport { + // Read out buttons first + let mut buttons = 0; + + buttons += input.button_b as u8; + buttons += (input.button_a as u8) << 1; + buttons += (input.button_y as u8) << 2; + buttons += (input.button_x as u8) << 3; + buttons += (input.button_trigger_l as u8) << 4; + buttons += (input.button_trigger_r as u8) << 5; + buttons += (input.button_minus as u8) << 6; + buttons += (input.button_plus as u8) << 7; + + JoystickReport { + buttons, + x: input.joystick_left_x, + y: -input.joystick_left_y, + } +} + +// End of file diff --git a/Cargo.toml b/wii-ext/Cargo.toml similarity index 100% rename from Cargo.toml rename to wii-ext/Cargo.toml diff --git a/src/classic_async.rs b/wii-ext/src/classic_async.rs similarity index 100% rename from src/classic_async.rs rename to wii-ext/src/classic_async.rs diff --git a/src/classic_sync.rs b/wii-ext/src/classic_sync.rs similarity index 100% rename from src/classic_sync.rs rename to wii-ext/src/classic_sync.rs diff --git a/src/common.rs b/wii-ext/src/common.rs similarity index 100% rename from src/common.rs rename to wii-ext/src/common.rs diff --git a/src/core.rs b/wii-ext/src/core.rs similarity index 100% rename from src/core.rs rename to wii-ext/src/core.rs diff --git a/src/core/classic.rs b/wii-ext/src/core/classic.rs similarity index 100% rename from src/core/classic.rs rename to wii-ext/src/core/classic.rs diff --git a/src/lib.rs b/wii-ext/src/lib.rs similarity index 100% rename from src/lib.rs rename to wii-ext/src/lib.rs diff --git a/src/nunchuk.rs b/wii-ext/src/nunchuk.rs similarity index 100% rename from src/nunchuk.rs rename to wii-ext/src/nunchuk.rs diff --git a/src/test_data.rs b/wii-ext/src/test_data.rs similarity index 100% rename from src/test_data.rs rename to wii-ext/src/test_data.rs diff --git a/tests/classic_hd.rs b/wii-ext/tests/classic_hd.rs similarity index 100% rename from tests/classic_hd.rs rename to wii-ext/tests/classic_hd.rs diff --git a/tests/classic_pdp_clone.rs b/wii-ext/tests/classic_pdp_clone.rs similarity index 100% rename from tests/classic_pdp_clone.rs rename to wii-ext/tests/classic_pdp_clone.rs diff --git a/tests/classic_pdp_clone_hd.rs b/wii-ext/tests/classic_pdp_clone_hd.rs similarity index 100% rename from tests/classic_pdp_clone_hd.rs rename to wii-ext/tests/classic_pdp_clone_hd.rs diff --git a/tests/classic_pro.rs b/wii-ext/tests/classic_pro.rs similarity index 100% rename from tests/classic_pro.rs rename to wii-ext/tests/classic_pro.rs diff --git a/tests/classic_pro_hd.rs b/wii-ext/tests/classic_pro_hd.rs similarity index 100% rename from tests/classic_pro_hd.rs rename to wii-ext/tests/classic_pro_hd.rs diff --git a/tests/classic_regular.rs b/wii-ext/tests/classic_regular.rs similarity index 100% rename from tests/classic_regular.rs rename to wii-ext/tests/classic_regular.rs