From 42587810cdbf84ddefb3c3b2802975c92d051661 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 10 Sep 2020 23:17:31 +0200 Subject: [PATCH] usb: add serial logger --- Cargo.lock | 47 ++++++++++++++++++++-- Cargo.toml | 8 +++- src/init_log.rs | 9 ++++- src/main.rs | 11 +++++- src/pins.rs | 23 +++++++++-- src/usb.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 190 insertions(+), 11 deletions(-) create mode 100644 src/usb.rs diff --git a/Cargo.lock b/Cargo.lock index ae8a0da..abcb848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,15 @@ dependencies = [ "cortex-m", ] +[[package]] +name = "embedded-dma" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25093adf4f2a76d429644c1f6f0abc10b79d00784a13e5d0cfabcd52b55b76" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "embedded-hal" version = "0.2.4" @@ -296,8 +305,6 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "smoltcp" version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" dependencies = [ "bitflags", "byteorder", @@ -338,17 +345,18 @@ dependencies = [ [[package]] name = "stm32f4xx-hal" version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a2f044469d1e3aff2cd02bee8b2724f3d5d91f3175e5d1ec99770320d16192" +source = "git+https://github.com/stm32-rs/stm32f4xx-hal.git#4a1a019f8e6e81a69c726bcab1f7caadefdbd32e" dependencies = [ "bare-metal", "cast", "cortex-m", "cortex-m-rt", + "embedded-dma", "embedded-hal", "nb 0.1.3", "rand_core", "stm32f4", + "synopsys-usb-otg", "void", ] @@ -363,6 +371,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "synopsys-usb-otg" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25c7f146100fd844a8bbd4ecf9e825f40e5ddfbf5a6c8af9e15e8623a72c6255" +dependencies = [ + "cortex-m", + "usb-device", + "vcell", +] + [[package]] name = "thermostat" version = "0.0.0" @@ -375,6 +394,7 @@ dependencies = [ "cortex-m-rt", "hash2hwaddr", "log", + "nb 0.1.3", "nom", "num-traits", "panic-abort", @@ -382,6 +402,8 @@ dependencies = [ "smoltcp", "stm32-eth", "stm32f4xx-hal", + "usb-device", + "usbd-serial", ] [[package]] @@ -396,6 +418,23 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "usb-device" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e5e2b9ba23f0d9ef7a34e498b6581c9d67944a1916542bfc7238bf1dc0d6acd" + +[[package]] +name = "usbd-serial" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45051be4bc25e6f85caacb1d3f45ace644fcc6e662b3d976330912542b7f69e" +dependencies = [ + "embedded-hal", + "nb 0.1.3", + "usb-device", +] + [[package]] name = "vcell" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index 0915b2e..3cf69b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ bare-metal = "0.2" cortex-m = "0.6" cortex-m-rt = { version = "0.6", features = ["device"] } cortex-m-log = { version = "0.6", features = ["log-integration"] } -stm32f4xx-hal = { version = "0.8", features = ["rt", "stm32f427"] } +stm32f4xx-hal = { version = "0.8", features = ["rt", "stm32f427", "usb_fs"] } stm32-eth = { version = "0.2", features = ["stm32f427", "smoltcp-phy"], git = "https://github.com/stm32-rs/stm32-eth.git" } smoltcp = { version = "0.6.0", default-features = false, features = ["proto-ipv4", "socket-tcp", "log"] } hash2hwaddr = { version = "0.0", optional = true } @@ -29,6 +29,12 @@ bit_field = "0.10" byteorder = { version = "1", default-features = false } nom = { version = "5", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["libm"] } +usb-device = "0.2" +usbd-serial = "0.1" +nb = "0.1" + +[patch.crates-io] +stm32f4xx-hal = { git = "https://github.com/stm32-rs/stm32f4xx-hal.git" } [features] diff --git a/src/init_log.rs b/src/init_log.rs index fc736df..0935a6b 100644 --- a/src/init_log.rs +++ b/src/init_log.rs @@ -1,5 +1,12 @@ +use crate::usb; + #[cfg(not(feature = "semihosting"))] -pub fn init_log() {} +static USB_LOGGER: usb::Logger = usb::Logger; + +#[cfg(not(feature = "semihosting"))] +pub fn init_log() { + log::set_logger(&USB_LOGGER); +} #[cfg(feature = "semihosting")] pub fn init_log() { diff --git a/src/main.rs b/src/main.rs index c649f59..ec289c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![no_std] #![no_main] +#![feature(maybe_uninit_extra, maybe_uninit_ref)] // TODO: #![deny(warnings, unused)] #[cfg(not(feature = "semihosting"))] @@ -30,6 +31,7 @@ use smoltcp::{ mod init_log; use init_log::init_log; +mod usb; mod leds; mod pins; use pins::Pins; @@ -90,17 +92,22 @@ fn main() -> ! { timer::setup(cp.SYST, clocks); - let (pins, mut leds, eth_pins) = Pins::setup( + let (pins, mut leds, eth_pins, usb) = Pins::setup( clocks, dp.TIM1, dp.TIM3, dp.GPIOA, dp.GPIOB, dp.GPIOC, dp.GPIOD, dp.GPIOE, dp.GPIOF, dp.GPIOG, dp.SPI2, dp.SPI4, dp.SPI5, dp.ADC1, + dp.OTG_FS_GLOBAL, + dp.OTG_FS_DEVICE, + dp.OTG_FS_PWRCLK, ); leds.r1.on(); leds.g3.off(); leds.g4.off(); + usb::State::setup(usb); + let mut channels = Channels::new(pins); let adc_calibration = [ channels.adc.get_calibration(0).unwrap(), @@ -398,7 +405,7 @@ fn main() -> ! { cortex_m::interrupt::free(|cs| { if !net::is_pending(cs) { // Wait for interrupts - // (Ethernet or SysTick) + // (Ethernet, SysTick, or USB) wfi(); } }); diff --git a/src/pins.rs b/src/pins.rs index 7ccdf5b..3628aae 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -14,10 +14,17 @@ use stm32f4xx_hal::{ Output, PushPull, Speed::VeryHigh, }, + otg_fs::USB, rcc::Clocks, pwm::{self, PwmChannels}, spi::{Spi, NoMiso}, - stm32::{ADC1, GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, SPI2, SPI4, SPI5, TIM1, TIM3}, + stm32::{ + ADC1, + GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, + OTG_FS_GLOBAL, OTG_FS_DEVICE, OTG_FS_PWRCLK, + SPI2, SPI4, SPI5, + TIM1, TIM3, + }, time::U32Ext, }; use stm32_eth::EthPins; @@ -103,7 +110,8 @@ impl Pins { gpioa: GPIOA, gpiob: GPIOB, gpioc: GPIOC, gpiod: GPIOD, gpioe: GPIOE, gpiof: GPIOF, gpiog: GPIOG, spi2: SPI2, spi4: SPI4, spi5: SPI5, adc1: ADC1, - ) -> (Self, Leds, EthernetPins) { + otg_fs_global: OTG_FS_GLOBAL, otg_fs_device: OTG_FS_DEVICE, otg_fs_pwrclk: OTG_FS_PWRCLK, + ) -> (Self, Leds, EthernetPins, USB) { let gpioa = gpioa.split(); let gpiob = gpiob.split(); let gpioc = gpioc.split(); @@ -186,7 +194,16 @@ impl Pins { rx_d1: gpioc.pc5, }; - (pins, leds, eth_pins) + let usb = USB { + usb_global: otg_fs_global, + usb_device: otg_fs_device, + usb_pwrclk: otg_fs_pwrclk, + pin_dm: gpioa.pa11.into_alternate_af10(), + pin_dp: gpioa.pa12.into_alternate_af10(), + hclk: clocks.hclk(), + }; + + (pins, leds, eth_pins, usb) } /// Configure the GPIO pins for SPI operation, and initialize SPI diff --git a/src/usb.rs b/src/usb.rs new file mode 100644 index 0000000..8166538 --- /dev/null +++ b/src/usb.rs @@ -0,0 +1,103 @@ +use core::{fmt::{self, Write}, mem::MaybeUninit}; +use cortex_m::interrupt::free; +use stm32f4xx_hal::{ + otg_fs::{USB, UsbBus as Bus}, + stm32::{interrupt, Interrupt, NVIC}, +}; +use usb_device::{ + class_prelude::{UsbBusAllocator}, + prelude::{UsbError, UsbDevice, UsbDeviceBuilder, UsbVidPid}, +}; +use usbd_serial::SerialPort; +use log::{Record, Level, Log, Metadata}; + +static mut EP_MEMORY: [u32; 1024] = [0; 1024]; + +static mut BUS: MaybeUninit>> = MaybeUninit::uninit(); +// static mut SERIAL_DEV: Option<(SerialPort<'static, Bus>, UsbDevice<'static, Bus>)> = None; +static mut STATE: Option = None; + +pub struct State { + serial: SerialPort<'static, Bus>, + dev: UsbDevice<'static, Bus>, +} + +impl State { + pub fn setup(usb: USB) { + unsafe { BUS.write(Bus::new(usb, &mut EP_MEMORY)) }; + + let bus = unsafe { BUS.assume_init_ref() }; + let serial = SerialPort::new(bus); + let dev = UsbDeviceBuilder::new(bus, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("M-Labs") + .product("thermostat") + .device_release(0x20) + .self_powered(true) + .device_class(usbd_serial::USB_CLASS_CDC) + .build(); + + free(|_| { + unsafe { STATE = Some(State { serial, dev }); } + }); + + unsafe { + NVIC::unmask(Interrupt::OTG_FS); + } + } + + pub fn get() -> Option<&'static mut Self> { + unsafe { STATE.as_mut() } + } + + pub fn poll() { + if let Some(ref mut s) = Self::get() { + if s.dev.poll(&mut [&mut s.serial]) { + // discard any input + let mut buf = [0u8; 64]; + let _ = s.serial.read(&mut buf); + } + } + } +} + +#[interrupt] +fn OTG_FS() { + free(|_| { + State::poll(); + }); +} + +pub struct Logger; + +impl Log for Logger { + fn enabled(&self, _: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let mut output = SerialOutput; + let _ = writeln!(&mut output, "{} - {}", record.level(), record.args()); + } + } + + fn flush(&self) { + if let Some(ref mut state) = State::get() { + let _ = free(|_| state.serial.flush()); + } + } +} + +pub struct SerialOutput; + +impl Write for SerialOutput { + fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> { + if let Some(ref mut state) = State::get() { + for chunk in s.as_bytes().chunks(16) { + free(|_| state.serial.write(chunk)) + .map_err(|_| fmt::Error)?; + } + } + Ok(()) + } +}