diff --git a/boards/adafruit-kb2040/Cargo.toml b/boards/adafruit-kb2040/Cargo.toml index fd70306f..cefd1f47 100644 --- a/boards/adafruit-kb2040/Cargo.toml +++ b/boards/adafruit-kb2040/Cargo.toml @@ -17,12 +17,15 @@ rp2040-boot2 = { workspace = true, optional = true } rp2040-hal.workspace = true [dev-dependencies] -panic-halt.workspace = true -rp2040-boot2.workspace = true -smart-leds.workspace = true +embedded-hal.workspace = true +heapless.workspace = true fugit.workspace = true nb.workspace = true +panic-halt.workspace = true +smart-leds.workspace = true ws2812-pio.workspace = true +usb-device.workspace = true +usbd-serial.workspace = true [features] # This is the set of features we enable by default diff --git a/boards/adafruit-kb2040/examples/adafruit_kb2040_usb_serial.rs b/boards/adafruit-kb2040/examples/adafruit_kb2040_usb_serial.rs new file mode 100644 index 00000000..548da14d --- /dev/null +++ b/boards/adafruit-kb2040/examples/adafruit_kb2040_usb_serial.rs @@ -0,0 +1,155 @@ +//! # Pico USB Serial Example +//! +//! Creates a USB Serial device on a Pico board, with the USB driver running in +//! the main thread. +//! +//! This will create a USB Serial device echoing anything it receives. Incoming +//! ASCII characters are converted to upercase, so you can tell it is working +//! and not just local-echo! +//! +//! See the `Cargo.toml` file for Copyright and license details. + +#![no_std] +#![no_main] + +use adafruit_kb2040 as bsp; + +// The macro for our start-up function +use bsp::entry; + +// Ensure we halt the program on panic (if we don't mention this crate it won't +// be linked) +use panic_halt as _; + +// A shorter alias for the Peripheral Access Crate, which provides low-level +// register access +use bsp::hal::pac; + +// A shorter alias for the Hardware Abstraction Layer, which provides +// higher-level drivers. +use bsp::hal; + +// USB Device support +use usb_device::{class_prelude::*, prelude::*}; + +// USB Communications Class Device support +use usbd_serial::SerialPort; + +// Used to demonstrate writing formatted strings +use core::fmt::Write; +use heapless::String; + +/// Entry point to our bare-metal application. +/// +/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function +/// as soon as all global variables are initialised. +/// +/// The function configures the RP2040 peripherals, then echoes any characters +/// received over USB Serial. +#[entry] +fn main() -> ! { + // Grab our singleton objects + let mut pac = pac::Peripherals::take().unwrap(); + + // Set up the watchdog driver - needed by the clock setup code + let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); + + // Configure the clocks + // + // The default is to generate a 125 MHz system clock + let clocks = hal::clocks::init_clocks_and_plls( + bsp::XOSC_CRYSTAL_FREQ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); + + #[cfg(feature = "rp2040-e5")] + { + let sio = hal::Sio::new(pac.SIO); + let _pins = bsp::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + } + + // Set up the USB driver + let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new( + pac.USBCTRL_REGS, + pac.USBCTRL_DPRAM, + clocks.usb_clock, + true, + &mut pac.RESETS, + )); + + // Set up the USB Communications Class Device driver + let mut serial = SerialPort::new(&usb_bus); + + // Create a USB device with a fake VID and PID + let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(2) // from: https://www.usb.org/defined-class-codes + .build(); + + let mut said_hello = false; + loop { + // A welcome message at the beginning + if !said_hello && timer.get_counter().ticks() >= 2_000_000 { + said_hello = true; + let _ = serial.write(b"Hello, World!\r\n"); + + let time = timer.get_counter().ticks(); + let mut text: String<64> = String::new(); + writeln!(&mut text, "Current timer ticks: {}", time).unwrap(); + + // This only works reliably because the number of bytes written to + // the serial port is smaller than the buffers available to the USB + // peripheral. In general, the return value should be handled, so that + // bytes not transferred yet don't get lost. + let _ = serial.write(text.as_bytes()); + } + + // Check for new data + if usb_dev.poll(&mut [&mut serial]) { + let mut buf = [0u8; 64]; + match serial.read(&mut buf) { + Err(_e) => { + // Do nothing + } + Ok(0) => { + // Do nothing + } + Ok(count) => { + // Convert to upper case + buf.iter_mut().take(count).for_each(|b| { + b.make_ascii_uppercase(); + }); + // Send back to the host + let mut wr_ptr = &buf[..count]; + while !wr_ptr.is_empty() { + match serial.write(wr_ptr) { + Ok(len) => wr_ptr = &wr_ptr[len..], + // On error, just drop unwritten data. + // One possible error is Err(WouldBlock), meaning the USB + // write buffer is full. + Err(_) => break, + }; + } + } + } + } + } +} + +// End of file diff --git a/boards/adafruit-kb2040/src/lib.rs b/boards/adafruit-kb2040/src/lib.rs index 523e2fec..482fb456 100644 --- a/boards/adafruit-kb2040/src/lib.rs +++ b/boards/adafruit-kb2040/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] pub use rp2040_hal as hal; + #[cfg(feature = "rt")] extern crate cortex_m_rt; #[cfg(feature = "rt")]