diff --git a/examples/usb/.gdbinit b/examples/usb/.gdbinit new file mode 100644 index 00000000..c80bd21b --- /dev/null +++ b/examples/usb/.gdbinit @@ -0,0 +1,13 @@ +# disable "are you sure you want to quit?" +define hook-quit + set confirm off +end + +target remote :3333 + +# print demangled symbols by default +set print asm-demangle on + +monitor arm semihosting enable +load +cont diff --git a/examples/usb/Cargo.toml b/examples/usb/Cargo.toml new file mode 100644 index 00000000..90fbd95e --- /dev/null +++ b/examples/usb/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "usb" +version = "0.1.0" +authors = ["Jonas Schievink "] +edition = "2018" + +[dependencies] +cortex-m-rt = "0.6.12" +panic-semihosting = "0.5.3" +nrf52840-pac = "0.9.0" +usb-device = "0.2.7" +usbd-serial = "0.1.0" + +[dependencies.nrf52840-hal] +version = "0.12.0" +path = "../../nrf52840-hal" + diff --git a/examples/usb/Embed.toml b/examples/usb/Embed.toml new file mode 100755 index 00000000..fda29b6b --- /dev/null +++ b/examples/usb/Embed.toml @@ -0,0 +1,63 @@ +[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 = 1337 + +[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" + +[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 = "nRF52840" +# 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" + +[default.rtt] +# Whether or not an RTTUI should be opened after flashing. +# This is exclusive and cannot be used with GDB at the moment. +enabled = true +# A list of channel associations to be displayed. If left empty, all channels are displayed. +channels = [ + # { up = 0, down = 0, name = "name", format = "String" } +] +# 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. +# This is exclusive and cannot be used with RTT at the moment. +enabled = false +# The connection string in host:port format wher the GDB server will open a socket. +# gdb_connection_string diff --git a/examples/usb/README.md b/examples/usb/README.md new file mode 100644 index 00000000..66d725fe --- /dev/null +++ b/examples/usb/README.md @@ -0,0 +1,6 @@ +# USB examples + +This demo provides two binaries: + +- `serial`: a USB serial "echo" example. +- `test_class`: exposes the test device from the `usb-device` crate. diff --git a/examples/usb/src/bin/serial.rs b/examples/usb/src/bin/serial.rs new file mode 100644 index 00000000..e64cdd6a --- /dev/null +++ b/examples/usb/src/bin/serial.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +use panic_semihosting as _; + +use cortex_m_rt::entry; +use nrf52840_hal::clocks::Clocks; +use nrf52840_hal::usbd::{UsbPeripheral, Usbd}; +use nrf52840_pac::Peripherals; +use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +#[entry] +fn main() -> ! { + let periph = Peripherals::take().unwrap(); + let clocks = Clocks::new(periph.CLOCK); + let clocks = clocks.enable_ext_hfosc(); + + let usb_bus = Usbd::new(UsbPeripheral::new(periph.USBD, &clocks)); + let mut serial = SerialPort::new(&usb_bus); + + let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .max_packet_size_0(64) // (makes control transfers 8x faster) + .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 => { + // 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; + } + _ => {} + } + } + } + _ => {} + } + } +} diff --git a/examples/usb/src/bin/test_class.rs b/examples/usb/src/bin/test_class.rs new file mode 100644 index 00000000..0980d06c --- /dev/null +++ b/examples/usb/src/bin/test_class.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] + +use panic_semihosting as _; + +use cortex_m_rt::entry; +use nrf52840_hal::clocks::Clocks; +use nrf52840_hal::usbd::{UsbPeripheral, Usbd}; +use nrf52840_pac::Peripherals; +use usb_device::test_class::TestClass; + +#[entry] +fn main() -> ! { + let periph = Peripherals::take().unwrap(); + let clocks = Clocks::new(periph.CLOCK); + let clocks = clocks.enable_ext_hfosc(); + + let usb_bus = Usbd::new(UsbPeripheral::new(periph.USBD, &clocks)); + + let mut test = TestClass::new(&usb_bus); + + let mut usb_dev = { test.make_device(&usb_bus) }; + + loop { + if usb_dev.poll(&mut [&mut test]) { + test.poll(); + } + } +} diff --git a/nrf-hal-common/Cargo.toml b/nrf-hal-common/Cargo.toml index 343af98b..33de3844 100644 --- a/nrf-hal-common/Cargo.toml +++ b/nrf-hal-common/Cargo.toml @@ -63,6 +63,10 @@ version = "0.9.0" optional = true version = "0.2.1" +[dependencies.nrf-usbd] +version = "0.1.0" +optional = true + [dependencies.embedded-hal] features = ["unproven"] version = "0.2.4" @@ -73,6 +77,6 @@ doc = [] 52810 = ["nrf52810-pac"] 52811 = ["nrf52811-pac"] 52832 = ["nrf52832-pac"] -52833 = ["nrf52833-pac"] -52840 = ["nrf52840-pac"] +52833 = ["nrf52833-pac", "nrf-usbd"] +52840 = ["nrf52840-pac", "nrf-usbd"] 9160 = ["nrf9160-pac"] diff --git a/nrf-hal-common/src/lib.rs b/nrf-hal-common/src/lib.rs index 3130e660..6d240329 100644 --- a/nrf-hal-common/src/lib.rs +++ b/nrf-hal-common/src/lib.rs @@ -80,6 +80,8 @@ pub mod uart; pub mod uarte; #[cfg(not(feature = "9160"))] pub mod uicr; +#[cfg(feature = "nrf-usbd")] +pub mod usbd; pub mod wdt; pub mod prelude { diff --git a/nrf-hal-common/src/usbd.rs b/nrf-hal-common/src/usbd.rs new file mode 100644 index 00000000..42b666cf --- /dev/null +++ b/nrf-hal-common/src/usbd.rs @@ -0,0 +1,25 @@ +use core::marker::PhantomData; + +use crate::clocks::ExternalOscillator; +use crate::pac::USBD; +use crate::Clocks; + +pub use nrf_usbd::Usbd; + +pub struct UsbPeripheral<'a> { + _usbd: USBD, + _clocks: PhantomData<&'a ()>, +} + +impl<'a> UsbPeripheral<'a> { + pub fn new(usbd: USBD, _clocks: &'a Clocks) -> Self { + Self { + _usbd: usbd, + _clocks: PhantomData, + } + } +} + +unsafe impl<'a> nrf_usbd::UsbPeripheral for UsbPeripheral<'a> { + const REGISTERS: *const () = USBD::ptr() as *const _; +} diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 147ad35b..b9276d8f 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -38,6 +38,7 @@ pub static EXAMPLES: &[(&str, &[&str])] = &[ ("twim-demo", &[]), ("twis-demo", &[]), ("twis-dma-demo", &[]), + ("usb", &[]), ("wdt-demo", &[]), ];