Skip to content

Commit

Permalink
Merge pull request #23 from decaday/feat/usbd
Browse files Browse the repository at this point in the history
`usb-device` impl and example
  • Loading branch information
decaday authored Dec 26, 2024
2 parents d43b0b5 + 90f1a8d commit 71384c2
Show file tree
Hide file tree
Showing 10 changed files with 348 additions and 31 deletions.
16 changes: 9 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@ log = { version = "0.4", optional = true }
critical-section = "1.2"
cfg-if = "1.0.0"
portable-atomic = { version = "1", features = ["unsafe-assume-single-core", "require-cas"], optional = true }
# musb = { version = "0.1.0", optional = true, features = ["prebuild"] }
# musb = { version = "0.2.0", optional = true, features = ["prebuild"] }
musb = { git = "https://github.com/decaday/musb.git", optional = true, features = ["prebuild"] }
# musb = { path = "../musb", optional = true , features = ["prebuild"] }

embassy-usb-driver = {version = "0.1.0", optional = true }
usb-device = {version = "0.3.2", optional = true }


futures-util = { version = "0.3.30", default-features = false }
embassy-hal-internal = { version = "0.2.0", features = [
"cortex-m",
Expand All @@ -60,7 +64,7 @@ embassy-executor = { version = "0.6", features = [
"arch-cortex-m",
] }
embassy-embedded-hal = { version = "0.2.0", default-features = false }
embassy-usb-driver = {version = "0.1.0" }


[build-dependencies]
# py32-metapac = { path = "../py32-data/build/py32-metapac", default-features = false, features = [
Expand All @@ -77,7 +81,7 @@ default = ["rt", "memory-x", "defmt", "embassy", "time", "exti"]

rt = ["py32-metapac/rt"]

defmt = ["dep:defmt", "dep:defmt-rtt", "embassy-usb-driver/defmt"]
defmt = ["dep:defmt", "dep:defmt-rtt", "embassy-usb-driver/defmt", "musb?/defmt"]

memory-x = ["py32-metapac/memory-x"]

Expand All @@ -87,10 +91,8 @@ time = ["dep:embassy-time", "embassy-embedded-hal/time"]

exti = []

# PY32F07x: the IN and OUT buffers of the same endpoint being shared
# When this feature is enabled, the In and Out of an endpoint will not be used at the same time, except for ep0.
# PY32F403: IN and OUT do not share FIFO, this feature is invalid
allow-ep-shared-fifo = ["musb/allow-ep-shared-fifo"]
embassy-usb-driver-impl = ["dep:musb","dep:embassy-usb-driver", "musb/embassy-usb-driver-impl"]
usb-device-impl = ["dep:musb","dep:usb-device", "musb/usb-device-impl"]

py32f030k28 = ["py32-metapac/py32f030k28"]
py32f030f16 = ["py32-metapac/py32f030f16"]
Expand Down
2 changes: 1 addition & 1 deletion examples/py32f072/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime
embedded-io = { version = "0.6.0" }
embedded-io-async = { version = "0.6.1" }

py32-hal = { path = "../../", features = [ "time-driver-tim15", "py32f072c1b"]}
py32-hal = { path = "../../", features = [ "time-driver-tim15", "py32f072c1b", "embassy-usb-driver-impl"]}

defmt = "0.3"
defmt-rtt = "0.4"
Expand Down
13 changes: 13 additions & 0 deletions examples/usbd-f072/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# TODO(2) replace `$CHIP` with your chip's name (see `probe-rs chip list` output)
runner = "probe-rs run --chip PY32F072xB"

# rustflags = [
# "-C", "linker=flip-link",
# ]

[build]
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+

[env]
DEFMT_LOG = "trace"
55 changes: 55 additions & 0 deletions examples/usbd-f072/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[package]
name = "py32f072-usbd-examples"
version = "0.2.0"
edition = "2021"

[dependencies]
panic-halt = "1.0.0"
cortex-m = { version = "0.7.7", features = [
"critical-section-single-core",
"critical-section",
] }
cortex-m-rt = "0.7.3"
cortex-m-semihosting = { version = "0.5" }
panic-probe = { version = "0.3", features = ["print-defmt"] }

py32-hal = { path = "../../", default-features = false,features = [ "py32f072c1b",
"time-driver-tim15",
"defmt",
"rt",
"memory-x",
"usb-device-impl",
"embassy"
]}

defmt = "0.3"
defmt-rtt = "0.4"
embassy-futures = "0.1.1"
embassy-usb = { version = "0.3.0", features = [ "defmt"]}
usbd-human-interface-device = { version = "0.5.0", features = [ "defmt"]}

portable-atomic = { version = "1.5", features = ["critical-section"] }
static_cell = "2.1"
usbd-serial = "0.2.2"
usb-device = "0.3.2"


# cargo build/run
[profile.dev]
codegen-units = 1
debug = 2
debug-assertions = true # <-
incremental = false
opt-level = 'z' # <-
overflow-checks = true # <-

# cargo build/run --release
[profile.release]
codegen-units = 1
debug = 2
debug-assertions = false # <-
incremental = false
lto = 'fat'
opt-level = 3 # <-
overflow-checks = false # <-

13 changes: 13 additions & 0 deletions examples/usbd-f072/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# `usb-device` (`usbd`) Demo

> [!WARNING]
>
> This demo is intended to showcase the [usb-device](https://crates.io/crates/usb-device) crate, not the [embassy-usb](https://crates.io/crates/embassy-usb) crate.
>
> If you're looking for examples of USB async drivers, please refer to the [py32f072 examples](https://chatgpt.com/py32f072/src/bin).
## Usage

You need to enable the `usb-device-impl` feature and disable the `embassy-usb-driver-impl` feature.

The PY32 uses a stripped-down version of the MUSB IP. For more information, check out the [musb](https://crates.io/crates/musb) crate. It includes implementations of both [embassy-usb-driver](https://crates.io/crates/embassy-usb-driver) and [usb-device](https://crates.io/crates/usb-device).
10 changes: 10 additions & 0 deletions examples/usbd-f072/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fn main() {
// `--nmagic` is required if memory section addresses are not aligned to 0x10000,
// for example the FLASH and RAM sections in your `memory.x`.
// See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
println!("cargo:rustc-link-arg=--nmagic");

println!("cargo:rustc-link-arg=-Tlink.x");

println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
}
111 changes: 111 additions & 0 deletions examples/usbd-f072/src/bin/hid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#![no_std]
#![no_main]
#![feature(impl_trait_in_assoc_type)]

use defmt::*;
use py32_hal::bind_interrupts;
use py32_hal::gpio::{Input, Level, Output, Pull, Speed};
use py32_hal::rcc::{Pll, PllMul, PllSource, Sysclk};
use py32_hal::time::Hertz;
use py32_hal::usb::{self, InterruptHandler};
use {defmt_rtt as _, panic_probe as _};

use usb_device::{class_prelude::*, prelude::*};
use usbd_human_interface_device::page::Keyboard;
use usbd_human_interface_device::prelude::*;

bind_interrupts!(struct Irqs {
USB => InterruptHandler<py32_hal::peripherals::USB>;
});

#[cortex_m_rt::entry]
fn main() -> ! {
let mut cfg: py32_hal::Config = Default::default();

// PY32 USB uses PLL as the clock source and can only run at 48Mhz.
cfg.rcc.hsi = Some(Hertz::mhz(16));
cfg.rcc.pll = Some(Pll {
src: PllSource::HSI,
mul: PllMul::MUL3,
});
cfg.rcc.sys = Sysclk::PLL;
let p = py32_hal::init(cfg);

let mut led = Output::new(p.PB2, Level::High, Speed::Low);
let button = Input::new(p.PB0, Pull::Up);

let usb_bus = usb::new_bus(p.USB, Irqs, p.PA12, p.PA11);

let usb_bus_allocator = UsbBusAllocator::new(usb_bus);

let mut keyboard = UsbHidClassBuilder::new()
.add_device(usbd_human_interface_device::device::keyboard::BootKeyboardConfig::default())
.build(&usb_bus_allocator);

//https://pid.codes
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus_allocator, UsbVidPid(0x1209, 0x0001))
.strings(&[StringDescriptors::default()
.manufacturer("py32-rs team")
.product("Boot Keyboard")
.serial_number("TEST")])
.unwrap()
.build();

let mut button_pressed = false;

loop {
if button.is_high() {
if button_pressed {
// Button was just released
button_pressed = false;
// Send release report with no keys pressed
match keyboard.device().write_report([Keyboard::NoEventIndicated]) {
Err(UsbHidError::WouldBlock) => {}
Err(UsbHidError::Duplicate) => {}
Ok(_) => {}
Err(e) => {
core::panic!("Failed to write keyboard report: {:?}", e)
}
};
}
} else {
if !button_pressed {
// Button was just pressed
button_pressed = true;
info!("Button pressed");
// Send press report with 'A' key
match keyboard.device().write_report([Keyboard::A]) {
Err(UsbHidError::WouldBlock) => {}
Err(UsbHidError::Duplicate) => {}
Ok(_) => {}
Err(e) => {
core::panic!("Failed to write keyboard report: {:?}", e)
}
};
}
}

//Tick once per ms
match keyboard.tick() {
Err(UsbHidError::WouldBlock) => {}
Ok(_) => {}
Err(e) => {
core::panic!("Failed to process keyboard tick: {:?}", e)
}
};

if usb_dev.poll(&mut [&mut keyboard]) {
match keyboard.device().read_report() {
Err(UsbError::WouldBlock) => {
//do nothing
}
Err(e) => {
core::panic!("Failed to read keyboard report: {:?}", e)
}
Ok(leds) => {
led.set_level(Level::from(leds.caps_lock));
}
}
}
}
}
89 changes: 89 additions & 0 deletions examples/usbd-f072/src/bin/serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#![no_std]
#![no_main]
#![feature(impl_trait_in_assoc_type)]

use defmt::*;
use py32_hal::bind_interrupts;
use py32_hal::gpio::{Level, Output, Speed};
use py32_hal::rcc::{Pll, PllMul, PllSource, Sysclk};
use py32_hal::time::Hertz;
use py32_hal::usb::{self, InterruptHandler};
use {defmt_rtt as _, panic_probe as _};

use usb_device::{class_prelude::*, prelude::*};
use usbd_serial::{SerialPort, USB_CLASS_CDC};

bind_interrupts!(struct Irqs {
USB => InterruptHandler<py32_hal::peripherals::USB>;
});

#[cortex_m_rt::entry]
fn main() -> ! {
let mut cfg: py32_hal::Config = Default::default();

// PY32 USB uses PLL as the clock source and can only run at 48Mhz.
cfg.rcc.hsi = Some(Hertz::mhz(16));
cfg.rcc.pll = Some(Pll {
src: PllSource::HSI,
mul: PllMul::MUL3,
});
cfg.rcc.sys = Sysclk::PLL;
let p = py32_hal::init(cfg);

let mut led = Output::new(p.PB2, Level::High, Speed::Low);

let usb_bus = usb::new_bus(p.USB, Irqs, p.PA12, p.PA11);

let usb_bus_allocator = UsbBusAllocator::new(usb_bus);

let mut serial = SerialPort::new(&usb_bus_allocator);

let string_descriptors = StringDescriptors::new(LangID::EN_US)
.manufacturer("py32-rs team")
.product("Serial")
.serial_number("TEST");

let mut usb_dev = UsbDeviceBuilder::new(&usb_bus_allocator, UsbVidPid(0x16c0, 0x27dd))
.strings(&[string_descriptors])
.unwrap()
.max_packet_size_0(64)
.unwrap()
.device_class(USB_CLASS_CDC)
.build();

loop {
if !usb_dev.poll(&mut [&mut serial]) {
continue;
}

let mut buf = [0u8; 64];

match serial.read(&mut buf) {
Ok(count) if count > 0 => {
led.set_high(); // Turn on

info!("data: {:x}", &buf[0..count]);

// Echo back in upper case
for c in buf[0..count].iter_mut() {
if 0x61 <= *c && *c <= 0x7a {
*c &= !0x20;
}
}

let mut write_offset = 0;
while write_offset < count {
match serial.write(&buf[write_offset..count]) {
Ok(len) if len > 0 => {
write_offset += len;
}
_ => {}
}
}
}
_ => {}
}

led.set_low(); // Turn off
}
}
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ pub mod usart;
pub mod gpio;
#[cfg(feature = "time-driver-systick")]
pub mod systick_time_driver;
#[cfg(feature = "py32f072c1b")]

#[cfg(any(feature = "embassy-usb-driver-impl", feature = "usb-device-impl"))]
pub mod usb;

#[cfg(feature = "exti")]
Expand Down
Loading

0 comments on commit 71384c2

Please sign in to comment.