diff --git a/Cargo.toml b/Cargo.toml index f3d48a7..9d74713 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,16 @@ rp2040-boot2 = "0.2.1" rp2040-hal = { version = "0.9.0", features = ["rp2040-e5"] } pio-proc = "0.2.2" pio = "0.2.1" + +[dev-dependencies] +rp-pico = { version = "0.8.0", features = ["rp2040-e5"] } +embedded-hal = { version = "0.2.5", features = ["unproven"] } +embedded-alloc = "0.5.0" +cortex-m = { version = "0.7.6"} +cortex-m-rt = "0.7.3" +usb-device = "0.2.9" +usbd-serial = "0.1.1" + +[[example]] +name = "rp-pico-dht22" +path = "example/rp_pico_dht22.rs" \ No newline at end of file diff --git a/example/pico_usb_serial.rs b/example/pico_usb_serial.rs new file mode 100644 index 0000000..dabf414 --- /dev/null +++ b/example/pico_usb_serial.rs @@ -0,0 +1,140 @@ +use alloc::{ + boxed::Box, + string::{String, ToString}, + vec::Vec, +}; +use cortex_m::interrupt::free; +use rp_pico::hal::pac; +use rp_pico::hal::pac::interrupt; +use rp_pico::hal::usb::UsbBus; + +use usb_device::{ + class_prelude::UsbBusAllocator, + prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid}, +}; +use usbd_serial::SerialPort; + +use crate::serial_buffer::SerialBuffer; + +// use crate::{circular_buffer::CircularBuffer, simple_buffer::SimpleBuffer}; + +static WRITE_BUFFER_SIZE: usize = 32; +static mut USB_BUS: Option> = None; +static mut USB_DEVICE: Option> = None; +static mut USB_SERIAL: Option> = None; +static mut USB_BUFFER: Option> = None; + +pub struct PicoUsbSerial {} + +impl PicoUsbSerial { + pub fn init( + bus: UsbBus, + buffer: Box, + manufacturer: &'static str, + product: &'static str, + serial_number: &'static str, + ) -> Result<(), String> { + unsafe { + if USB_BUS.is_some() { + return Err("PicoUsbSerial already initialized.".to_string()); + } + + // Safety: This is safe as interrupts haven't been started yet + USB_BUFFER = Some(buffer); + USB_BUS = Some(UsbBusAllocator::new(bus)); + } + + let bus_ref = unsafe { USB_BUS.as_ref().unwrap() }; + + let serial = SerialPort::new(bus_ref); + let device = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x2E8A, 0x0005)) + .manufacturer(manufacturer) + .product(product) + .serial_number(serial_number) + .device_class(usbd_serial::USB_CLASS_CDC) + .build(); + + unsafe { + USB_SERIAL = Some(serial); + USB_DEVICE = Some(device); + + // Enable the USB interrupt + pac::NVIC::unmask(pac::Interrupt::USBCTRL_IRQ); + }; + + Ok(()) + } + + pub fn get_serial() -> Result { + unsafe { + if USB_BUS.is_none() { + Err("Serial not initialized.".to_string()) + } else { + Ok(Self {}) + } + } + } + + pub fn available_data(&self) -> usize { + unsafe { + return USB_BUFFER.as_ref().unwrap().available_to_read(); + }; + } + + pub fn read(&self) -> u8 { + free(|_cs| unsafe { USB_BUFFER.as_mut().unwrap().read().unwrap_or(0) }) + } + + pub fn write(&self, data: &[u8]) -> usb_device::Result { + free(|_cs| { + let serial = unsafe { USB_SERIAL.as_mut().unwrap() }; + + // let mut wr = data; + let data_chunks = data.chunks(WRITE_BUFFER_SIZE); + + for mut buf in data_chunks { + while !buf.is_empty() { + serial.write(buf).map(|len| { + buf = &buf[len..]; + })?; + } + } + + Ok(data.len()) + }) + } +} + +/// This function is called whenever the USB Hardware generates an Interrupt +/// Request. +/// +/// We do all our USB work under interrupt, so the main thread can continue on +/// knowing nothing about USB. +#[allow(non_snake_case)] +#[interrupt] +unsafe fn USBCTRL_IRQ() { + let buffer = USB_BUFFER.as_mut().unwrap(); + + // Grab the global objects. This is OK as we only access them under interrupt. + let usb_dev = USB_DEVICE.as_mut().unwrap(); + let serial = USB_SERIAL.as_mut().unwrap(); + + // Poll the USB driver with all of our supported USB Classes + if usb_dev.poll(&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| { + buffer.write(b.clone()); + }); + } + } + } +} diff --git a/example/rp_pico_dht22.rs b/example/rp_pico_dht22.rs new file mode 100644 index 0000000..dc61ba4 --- /dev/null +++ b/example/rp_pico_dht22.rs @@ -0,0 +1,112 @@ +#![no_std] +#![no_main] + +extern crate alloc; +extern crate dht_pio; + +use alloc::boxed::Box; +use alloc::format; +use core::panic::PanicInfo; +use cortex_m::interrupt::free; +use dht_pio::Dht22; +use embedded_alloc::Heap; + +use hal::{clocks::init_clocks_and_plls, clocks::ClockSource, pac, usb::UsbBus, Sio, Watchdog}; +use rp_pico::entry; +use rp_pico::hal; +use rp_pico::hal::pio::PIOExt; + +mod pico_usb_serial; +mod serial_buffer; +mod simple_buffer; +use pico_usb_serial::PicoUsbSerial; +use serial_buffer::SerialBuffer; +use simple_buffer::SimpleBuffer; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); +const HEAP_SIZE: usize = 4069; + +#[entry] +fn main() -> ! { + // Initialize the allocator BEFORE you use it + init_heap(); + + let mut pac = pac::Peripherals::take().unwrap(); + let mut watchdog = Watchdog::new(pac.WATCHDOG); + let clocks = init_clocks_and_plls( + rp_pico::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 = rp_pico::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + let core = pac::CorePeripherals::take().unwrap(); + let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.get_freq().to_Hz()); + + PicoUsbSerial::init( + UsbBus::new( + pac.USBCTRL_REGS, + pac.USBCTRL_DPRAM, + clocks.usb_clock, + true, + &mut pac.RESETS, + ), + Box::new(SimpleBuffer::new(1024)), + "MyCorp", + "Test", + "Serial", + ) + .expect("Failed to init Serial"); + + let serial = PicoUsbSerial::get_serial().expect("Failed to get serial!"); + + let (dht_pio, dht_sm, _, _, _) = pac.PIO0.split(&mut pac.RESETS); + let mut dht = Dht22::new(dht_pio, dht_sm, pins.gpio0.into_function()); + + loop { + free(|_cs| match dht.read(&mut delay) { + Ok(result) => { + serial.write(format!("{:#?}\r\n", result).as_bytes()).ok(); + } + Err(e) => { + serial + .write(format!("DHT Error: {:?}\r\n", e).as_bytes()) + .ok(); + } + }); + + delay.delay_ms(1000); + } +} + +fn init_heap() -> () { + use core::mem::MaybeUninit; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }; +} + +#[panic_handler] +fn panic(_: &PanicInfo) -> ! { + let ser = PicoUsbSerial::get_serial(); + + if ser.is_ok() { + let ser = ser.unwrap(); + let _ = ser.write(b"===== PANIC =====\r\n"); + } + + loop {} +} diff --git a/example/serial_buffer.rs b/example/serial_buffer.rs new file mode 100644 index 0000000..aebc70c --- /dev/null +++ b/example/serial_buffer.rs @@ -0,0 +1,10 @@ +use alloc::vec::Vec; + +pub trait SerialBuffer { + fn new(buffer_size: usize) -> Self where Self: Sized; + fn write(&mut self, value: u8) -> (); + fn read(&mut self) -> Option; + fn read_all(&mut self) -> Vec; + fn reset(&mut self) -> (); + fn available_to_read(&self) -> usize; +} \ No newline at end of file diff --git a/example/simple_buffer.rs b/example/simple_buffer.rs new file mode 100644 index 0000000..0929f86 --- /dev/null +++ b/example/simple_buffer.rs @@ -0,0 +1,45 @@ +use alloc::{collections::VecDeque, vec::Vec}; +use crate::serial_buffer::SerialBuffer; + +pub struct SimpleBuffer{ + buf: VecDeque, +} + +#[allow(unused)] +impl SerialBuffer for SimpleBuffer { + fn new(buffer_size: usize) -> Self { + assert!(buffer_size > 0); + + Self { + buf: VecDeque::with_capacity(buffer_size), + } + } + + fn write(&mut self, value: u8) -> (){ + if self.buf.len() < self.buf.capacity(){ + self.buf.push_back(value); + } + } + + fn read(&mut self) -> Option { + self.buf.pop_front() + } + + fn read_all(&mut self) -> Vec{ + let mut data = Vec::::with_capacity(self.available_to_read()); + + for _ in 0..self.buf.len(){ + data.push(self.buf.pop_front().unwrap_or(0)); + } + + return data; + } + + fn reset(&mut self) -> (){ + self.buf.clear(); + } + + fn available_to_read(&self) -> usize { + return self.buf.len(); + } +} diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..070eac7 --- /dev/null +++ b/memory.x @@ -0,0 +1,15 @@ +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; \ No newline at end of file