diff --git a/boards/boardsource-blok/Cargo.toml b/boards/boardsource-blok/Cargo.toml index 701654f3..7f798ded 100644 --- a/boards/boardsource-blok/Cargo.toml +++ b/boards/boardsource-blok/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/rp-rs/rp-hal-boards.git" [dependencies] cortex-m = "0.7.7" rp2040-boot2 = { version = "0.3.0", optional = true} -rp2040-hal = { version = "0.8.1"} +rp2040-hal = { version = "0.8.2"} cortex-m-rt = { version = "0.7.3", optional = true} fugit = "0.3.5" @@ -21,9 +21,11 @@ fugit = "0.3.5" panic-halt = "0.2.0" embedded-hal = "0.2.7" nb = "1.0" +heapless = "0.7.16" smart-leds = "0.3.0" ws2812-pio = "0.6.0" usb-device = "0.2.9" +usbd-serial = "0.1.1" usbd-hid = "0.5.2" critical-section = "1.1.1" diff --git a/boards/boardsource-blok/README.md b/boards/boardsource-blok/README.md index 191138a5..028d3694 100644 --- a/boards/boardsource-blok/README.md +++ b/boards/boardsource-blok/README.md @@ -95,6 +95,12 @@ Resets the Blok after 10 seconds to usb boot mode. Demonstrates emulating a USB Human Input Device (HID) Keyboard. The keyboard will type "HELLO" five times. +### [blok_usb_serial](./examples/blok_usb_serial.rs) + +Demonstrates creating a USB Serial device. +The device will loop 10 times, where on each loop the current loop number is printed. +If the device reads "stop", the blok will reset to usb boot mode. + ## Contributing Contributions are what make the open source community such an amazing place to diff --git a/boards/boardsource-blok/examples/blok_usb_serial.rs b/boards/boardsource-blok/examples/blok_usb_serial.rs new file mode 100644 index 00000000..6cc6a591 --- /dev/null +++ b/boards/boardsource-blok/examples/blok_usb_serial.rs @@ -0,0 +1,154 @@ +//! # USB Serial Example for the Blok +//! +//! Creates a USB Serial device on a blok board, with the USB driver running in +//! the USB interrupt and all writes being done in the main loop with the usage +//! of critical section. +//! +//! This will create a USB Serial device and then write to it. +//! A loop will write its current loop number 10 times after which +//! the Blok will reset to usb boot mode. +//! If "stop" is read by the serial device, the Blok will also +//! reset to usb boot mode. +//! +//! See the `Cargo.toml` file for Copyright and license details. + +#![no_std] +#![no_main] + +use boardsource_blok::{entry, hal}; +use boardsource_blok::{ + hal::{ + clocks::{init_clocks_and_plls, Clock}, + pac, + pac::interrupt, + timer::Timer, + watchdog::Watchdog, + Sio, + }, + Pins, XOSC_CRYSTAL_FREQ, +}; +use core::fmt::Write; +use heapless::String; +use panic_halt as _; +use usb_device::{ + bus::UsbBusAllocator, device::UsbDevice, device::UsbDeviceBuilder, device::UsbVidPid, +}; +use usbd_serial::SerialPort; + +// shared with the interrupt +static mut USB_DEVICE: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +#[entry] +fn main() -> ! { + let mut pac = pac::Peripherals::take().unwrap(); + + let mut watchdog = Watchdog::new(pac.WATCHDOG); + + let 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(); + + let sio = Sio::new(pac.SIO); + let _pins = Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + let _timer = Timer::new(pac.TIMER, &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, + )); + unsafe { + USB_BUS = Some(usb_bus); + } + + let bus_ref = unsafe { USB_BUS.as_ref().unwrap() }; + + let serial = SerialPort::new(bus_ref); + unsafe { + USB_SERIAL = Some(serial); + } + + let usb_device = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x1209, 0x0001)) + .product("serial port") + .device_class(2) // from: https://www.usb.org/defined-class-codes + .build(); + unsafe { + USB_DEVICE = Some(usb_device); + } + + unsafe { + pac::NVIC::unmask(hal::pac::Interrupt::USBCTRL_IRQ); + } + + let core = pac::CorePeripherals::take().unwrap(); + let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()); + + let mut i: u8 = 0; + + loop { + delay.delay_ms(2_000); + + let mut text: String<20> = String::new(); + writeln!(&mut text, "loop number: {}\r\n", i).unwrap(); + + write_serial(text.as_bytes()); + + i += 1; + if i >= 10 { + hal::rom_data::reset_to_usb_boot(0, 0); + } + } +} + +/// Writes to the serial port. +/// +/// We do this with interrupts disabled, to avoid a race hazard with the USB IRQ. +fn write_serial(byte_array: &[u8]) { + let _ = critical_section::with(|_| unsafe { + USB_SERIAL.as_mut().map(|serial| serial.write(byte_array)) + }) + .unwrap(); +} + +/// This function is called whenever the USB Hardware generates +/// an Interrupt Request +#[allow(non_snake_case)] +#[interrupt] +unsafe fn USBCTRL_IRQ() { + let usb_device = USB_DEVICE.as_mut().unwrap(); + let serial = USB_SERIAL.as_mut().unwrap(); + + if usb_device.poll(&mut [serial]) { + let mut buf = [0u8; 64]; + + match serial.read(&mut buf) { + Err(_e) => {} + Ok(_count) => { + // gets the first 4 bytes of buf + if &buf[0..4] == b"stop" { + hal::rom_data::reset_to_usb_boot(0, 0); + } else { + let _ = serial.write("write stop to reset to usb boot\r\n".as_bytes()); + } + } + } + } +}