diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..1acd241 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,29 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# Choose a default "cargo run" tool: +# - probe-run provides flashing and defmt via a hardware debugger +# - cargo embed offers flashing, rtt, defmt and a gdb server via a hardware debugger +# it is configured via the Embed.toml in the root of this project +# - elf2uf2-rs loads firmware over USB when the rp2040 is in boot mode +# runner = "probe-run --chip RP2040" +# runner = "cargo embed" +runner = "elf2uf2-rs -d" + +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + + # Code-size optimizations. + # trap unreachable can save a lot of space, but requires nightly compiler. + # uncomment the next line if you wish to enable it + # "-Z", "trap-unreachable=no", + "-C", "inline-threshold=5", + "-C", "no-vectorize-loops", +] + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "debug" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..869df07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bcb7e03 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "workbench.colorCustomizations": { + "activityBar.background": "#2B1C76", + "titleBar.activeBackground": "#3D28A6", + "titleBar.activeForeground": "#FCFCFE" + }, + "rust-analyzer.check.allTargets": false, +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6dcf380 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "projekt_maus" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cortex-m = "0.7.7" +cortex-m-rt = "0.7.3" +rp-pico = "0.7.0" +panic-halt = "0.2.0" +defmt = "0.3.2" +usb-device = "0.2.9" +usbd-hid = "0.6.1" +critical-section = "1.1.1" diff --git a/Embed.toml b/Embed.toml new file mode 100644 index 0000000..9365af2 --- /dev/null +++ b/Embed.toml @@ -0,0 +1,82 @@ +[default.probe] +# USB vendor ID +# usb_vid = "1337" +# USB product ID +# usb_pid = "1337" +# Serial number +# serial = "12345678" +# The protocol to be used for communicating with the target. +protocol = "Swd" +# The speed in kHz of the data link to the target. +speed = 20000 + +[default.flashing] +# Whether or not the target should be flashed. +enabled = true +# Whether or not the target should be halted after reset. +# DEPRECATED, moved to reset section +halt_afterwards = false +# Whether or not bytes erased but not rewritten with data from the ELF +# should be restored with their contents before erasing. +restore_unwritten_bytes = false +# The path where an SVG of the assembled flash layout should be written to. +# flash_layout_output_path = "out.svg" +# Triggers a full chip erase instead of a page by page erase. +do_chip_erase = false + +[default.reset] +# Whether or not the target should be reset. +# When flashing is enabled as well, the target will be reset after flashing. +enabled = true +# Whether or not the target should be halted after reset. +halt_afterwards = false + +[default.general] +# The chip name of the chip to be debugged. +chip = "RP2040" +# A list of chip descriptions to be loaded during runtime. +chip_descriptions = [] +# The default log level to be used. Possible values are one of: +# "OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" +log_level = "WARN" +# Use this flag to assert the nreset & ntrst pins during attaching the probe to the chip. +connect_under_reset = false + +[default.rtt] +# Whether or not an RTTUI should be opened after flashing. +enabled = true +# How the target handles RTT outputs that won't fit in the buffer. This can be +# overridden per-channel. If left unset, the firmware will determine the default +# for each RTT up channel. +# NoBlockSkip - Skip writing the data completely if it doesn't fit in its +# entirety. +# NoBlockTrim - Write as much as possible of the data and ignore the rest. +# BlockIfFull - Spin until the host reads data. Can result in app freezing. +# +up_mode = "NoBlockSkip" + +# A list of channel associations to be displayed. If left empty, all channels are displayed. +# up, down (Optional) - RTT channel numbers +# name (Optional) - String to be displayed in the RTTUI tab +# up_mode (Optional) - RTT channel specific as described above +# format (Required) - How to interpret data from target firmware. One of: +# String - Directly show output from the target +# Defmt - Format output on the host, see https://defmt.ferrous-systems.com/ +# BinaryLE - Display as raw hex +channels = [ + { up = 0, down = 0, name = "name", up_mode = "NoBlockSkip", format = "Defmt" }, +] +# The duration in ms for which the logger should retry to attach to RTT. +timeout = 3000 +# Whether timestamps in the RTTUI are enabled +show_timestamps = true +# Whether to save rtt history buffer on exit. +log_enabled = false +# Where to save rtt history buffer relative to manifest path. +log_path = "./logs" + +[default.gdb] +# Whether or not a GDB server should be opened after flashing. +enabled = false +# The connection string in host:port format wher the GDB server will open a socket. +gdb_connection_string = "127.0.0.1:1337" \ No newline at end of file diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..070eac7 --- /dev/null +++ b/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/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..43a6a39 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "stable" +components = ["rustfmt"] +targets = ["thumbv6m-none-eabi"] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9c6d897 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,129 @@ +#![no_std] +#![no_main] + +// Ensure we halt the program on panic (if we don't mention this crate it won't +// be linked) +use panic_halt as _; + +use rp_pico as bsp; + +use bsp::{ + entry, + hal::{ + self, pac, + pac::{interrupt, Interrupt}, + prelude::*, + usb::UsbBus, + }, + XOSC_CRYSTAL_FREQ, +}; + +use cortex_m::delay::Delay; + +use usb_device::{class_prelude::*, prelude::*}; +use usbd_hid::{ + descriptor::{MouseReport, SerializedDescriptor}, + hid_class::HIDClass, +}; + +/// The USB Device Driver (shared with the interrupt). +static mut USB_DEVICE: Option> = None; + +/// The USB Bus Driver (shared with the interrupt). +static mut USB_BUS: Option> = None; + +/// The USB Human Interface Device Driver (shared with the interrupt). +static mut USB_HID: Option> = None; + +#[entry] +fn main() -> ! { + // Grab our singleton objects + let mut pac = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::take().unwrap(); + + // Set up the watchdog driver - needed by the clock setup code + let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); + + // Configure the clocks + let clocks = hal::clocks::init_clocks_and_plls( + XOSC_CRYSTAL_FREQ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + unsafe { + USB_BUS = Some(UsbBusAllocator::new(UsbBus::new( + pac.USBCTRL_REGS, + pac.USBCTRL_DPRAM, + clocks.usb_clock, + true, + &mut pac.RESETS, + ))) + }; + + unsafe { + USB_HID = Some(HIDClass::new( + USB_BUS.as_ref().unwrap(), + MouseReport::desc(), + 60, + )); + } + + unsafe { + USB_DEVICE = Some( + UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x046d, 0x101b)) + .manufacturer("Logitech") + .product("Marathon Mouse/Performance Plus M705") + .serial_number("B14D65DA") + .device_class(0) + .build(), + ); + } + + unsafe { + pac::NVIC::unmask(Interrupt::USBCTRL_IRQ); + } + + let mut delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()); + const PIXEL: i8 = 2; + const WAIT_TIME: u32 = 10000; + + loop { + mouse_move(&mut delay, WAIT_TIME, PIXEL); + mouse_move(&mut delay, 50, -PIXEL); + } +} + +fn mouse_move(delay: &mut Delay, wait_time: u32, v: i8) { + delay.delay_ms(wait_time); + + let rep = MouseReport { + x: 0, + y: v, + buttons: 0, + wheel: 0, + pan: 0, + }; + push_mouse_movement(rep).unwrap_or(0); +} + +/// with interrupts disabled, to avoid a race hazard with the USB IRQ. +fn push_mouse_movement(report: MouseReport) -> Result { + critical_section::with(|_| unsafe { USB_HID.as_mut().map(|hid| hid.push_input(&report)) }) + .unwrap() +} + +#[allow(non_snake_case)] +#[interrupt] +unsafe fn USBCTRL_IRQ() { + // Handle USB request + let usb_dev = USB_DEVICE.as_mut().unwrap(); + let usb_hid = USB_HID.as_mut().unwrap(); + usb_dev.poll(&mut [usb_hid]); +}