usb: add serial logger

pull/20/head
Astro 2020-09-10 23:17:31 +02:00
parent c11b71cc0d
commit 42587810cd
6 changed files with 190 additions and 11 deletions

47
Cargo.lock generated
View File

@ -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"

View File

@ -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]

View File

@ -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() {

View File

@ -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();
}
});

View File

@ -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

103
src/usb.rs Normal file
View File

@ -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<UsbBusAllocator<Bus<USB>>> = MaybeUninit::uninit();
// static mut SERIAL_DEV: Option<(SerialPort<'static, Bus<USB>>, UsbDevice<'static, Bus<USB>>)> = None;
static mut STATE: Option<State> = None;
pub struct State {
serial: SerialPort<'static, Bus<USB>>,
dev: UsbDevice<'static, Bus<USB>>,
}
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(())
}
}