diff --git a/embassy-hal-common/src/usb/mod.rs b/embassy-hal-common/src/usb/mod.rs index 70a74bd52..bab72d8b6 100644 --- a/embassy-hal-common/src/usb/mod.rs +++ b/embassy-hal-common/src/usb/mod.rs @@ -11,7 +11,7 @@ pub mod usb_serial; use crate::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy::interrupt::Interrupt; -use usb_serial::{ReadInterface, UsbSerial, WriteInterface}; +pub use usb_serial::{ReadInterface, UsbSerial, WriteInterface}; /// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction. pub unsafe trait USBInterrupt: Interrupt + Send {} diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index dae9f26ae..3d0c171f3 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -59,6 +59,8 @@ rand_core = "0.6.3" fixed = "1.10.0" embedded-storage = "0.2.0" cfg-if = "1.0.0" +nrf-usbd = {version = "0.1.1"} +usb-device = "0.2.8" nrf52805-pac = { version = "0.10.1", optional = true, features = [ "rt" ] } nrf52810-pac = { version = "0.10.1", optional = true, features = [ "rt" ] } diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 128e1503f..aa2b2e61d 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -7,6 +7,9 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 512; pub const FLASH_SIZE: usize = 256 * 1024; embassy_hal_common::peripherals! { + // USB + USBD, + // RTC RTC0, RTC1, @@ -122,6 +125,8 @@ embassy_hal_common::peripherals! { TEMP, } +impl_usb!(USBD, USBD, USBD); + impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 7c7198dfd..498a3c307 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -7,6 +7,9 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 512; pub const FLASH_SIZE: usize = 512 * 1024; embassy_hal_common::peripherals! { + // USB + USBD, + // RTC RTC0, RTC1, @@ -154,6 +157,8 @@ embassy_hal_common::peripherals! { TEMP, } +impl_usb!(USBD, USBD, USBD); + impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_uarte!(UARTE1, UARTE1, UARTE1); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index f5b90cd5a..411768146 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -7,6 +7,9 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 512; pub const FLASH_SIZE: usize = 1024 * 1024; embassy_hal_common::peripherals! { + // USB + USBD, + // RTC RTC0, RTC1, @@ -157,6 +160,8 @@ embassy_hal_common::peripherals! { TEMP, } +impl_usb!(USBD, USBD, USBD); + impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_uarte!(UARTE1, UARTE1, UARTE1); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index ca761893f..4fcb742e8 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -211,6 +211,9 @@ pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; embassy_hal_common::peripherals! { + // USB + USBD, + // RTC RTC0, RTC1, @@ -342,6 +345,8 @@ embassy_hal_common::peripherals! { P1_15, } +impl_usb!(USBD, USBD, USBD); + impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); impl_uarte!(UARTETWISPI1, UARTE1, SERIAL1); impl_uarte!(UARTETWISPI2, UARTE2, SERIAL2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index b8adab473..8e05d9b6a 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,6 +47,13 @@ pub mod temp; pub mod timer; pub mod twim; pub mod uarte; +#[cfg(any( + feature = "_nrf5340-app", + feature = "nrf52820", + feature = "nrf52833", + feature = "nrf52840" +))] +pub mod usb; #[cfg(not(feature = "_nrf5340"))] pub mod wdt; diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs new file mode 100644 index 000000000..deab94544 --- /dev/null +++ b/embassy-nrf/src/usb.rs @@ -0,0 +1,65 @@ +#![macro_use] + +use crate::interrupt::Interrupt; +use crate::pac; + +use core::marker::PhantomData; +use embassy::util::Unborrow; +use nrf_usbd::{UsbPeripheral, Usbd}; +use usb_device::bus::UsbBusAllocator; + +pub use embassy_hal_common::usb::*; + +pub struct UsbBus<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, +} + +unsafe impl<'d, T: Instance> UsbPeripheral for UsbBus<'d, T> { + // todo how to use T::regs + const REGISTERS: *const () = pac::USBD::ptr() as *const (); +} + +impl<'d, T: Instance> UsbBus<'d, T> { + pub fn new(_usb: impl Unborrow + 'd) -> UsbBusAllocator>> { + let r = T::regs(); + + r.intenset.write(|w| { + w.sof().set_bit(); + w.usbevent().set_bit(); + w.ep0datadone().set_bit(); + w.ep0setup().set_bit(); + w.usbreset().set_bit() + }); + + Usbd::new(UsbBus { + phantom: PhantomData, + }) + } +} + +unsafe impl embassy_hal_common::usb::USBInterrupt for crate::interrupt::USBD {} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + fn regs() -> &'static pac::usbd::RegisterBlock; + } +} + +pub trait Instance: Unborrow + sealed::Instance + 'static + Send { + type Interrupt: Interrupt; +} + +macro_rules! impl_usb { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::usb::sealed::Instance for peripherals::$type { + fn regs() -> &'static pac::usbd::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + } + impl crate::usb::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 5c58541a6..ca013f8b7 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -20,3 +20,6 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand = { version = "0.8.4", default-features = false } embedded-storage = "0.2.0" + +usb-device = "0.2" +usbd-serial = "0.1.1" diff --git a/examples/nrf/src/bin/usb_uart.rs b/examples/nrf/src/bin/usb_uart.rs new file mode 100644 index 000000000..c345df9e3 --- /dev/null +++ b/examples/nrf/src/bin/usb_uart.rs @@ -0,0 +1,92 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; + +use defmt::{info, unwrap}; +use defmt_rtt as _; // global logger +use embassy::interrupt::InterruptExt; +use futures::pin_mut; +use panic_probe as _; // print out panic messages + +use embassy::executor::Spawner; +use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; +use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial}; +use embassy_nrf::{interrupt, Peripherals}; +use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + let mut rx_buffer = [0u8; 64]; + // we send back input + cr + lf + let mut tx_buffer = [0u8; 66]; + + let usb_bus = UsbBus::new(p.USBD); + + let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); + + let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(0x02) + .build(); + + let irq = interrupt::take!(USBD); + irq.set_priority(interrupt::Priority::P3); + + let mut state = State::new(); + let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; + pin_mut!(usb); + + let (mut reader, mut writer) = usb.as_ref().take_serial_0(); + + info!("usb initialized!"); + + unwrap!( + writer + .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") + .await + ); + + let mut buf = [0u8; 64]; + loop { + let mut n = 0; + + async { + loop { + let char = unwrap!(reader.read_byte().await); + + // throw away, read more on cr, exit on lf + if char == b'\r' { + continue; + } else if char == b'\n' { + break; + } + + buf[n] = char; + n += 1; + + // stop if we're out of room + if n == buf.len() { + break; + } + } + } + .await; + + if n > 0 { + for char in buf[..n].iter_mut() { + // upper case + if 0x61 <= *char && *char <= 0x7a { + *char &= !0x20; + } + } + unwrap!(writer.write_all(&buf[..n]).await); + unwrap!(writer.write_all(b"\r\n").await); + unwrap!(writer.flush().await); + } + } +} diff --git a/examples/nrf/src/bin/usb_uart_io.rs b/examples/nrf/src/bin/usb_uart_io.rs new file mode 100644 index 000000000..8fc615262 --- /dev/null +++ b/examples/nrf/src/bin/usb_uart_io.rs @@ -0,0 +1,69 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; + +use defmt::{info, unwrap}; +use defmt_rtt as _; // global logger +use panic_probe as _; // print out panic messages + +use embassy::executor::Spawner; +use embassy::interrupt::InterruptExt; +use embassy::io::{read_line, AsyncWriteExt}; +use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial}; +use embassy_nrf::{interrupt, Peripherals}; +use futures::pin_mut; +use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + let mut rx_buffer = [0u8; 64]; + // we send back input + cr + lf + let mut tx_buffer = [0u8; 66]; + + let usb_bus = UsbBus::new(p.USBD); + + let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); + + let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(0x02) + .build(); + + let irq = interrupt::take!(USBD); + irq.set_priority(interrupt::Priority::P3); + + let mut state = State::new(); + let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; + pin_mut!(usb); + + let (mut reader, mut writer) = usb.as_ref().take_serial_0(); + + info!("usb initialized!"); + + unwrap!( + writer + .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") + .await + ); + + let mut buf = [0u8; 64]; + loop { + let n = unwrap!(read_line(&mut reader, &mut buf).await); + + for char in buf[..n].iter_mut() { + // upper case + if 0x61 <= *char && *char <= 0x7a { + *char &= !0x20; + } + } + + unwrap!(writer.write_all(&buf[..n]).await); + unwrap!(writer.write_all(b"\r\n").await); + unwrap!(writer.flush().await); + } +}