forked from M-Labs/thermostat
usb: add serial logger
This commit is contained in:
parent
c11b71cc0d
commit
42587810cd
47
Cargo.lock
generated
47
Cargo.lock
generated
@ -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"
|
||||
|
@ -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]
|
||||
|
@ -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() {
|
||||
|
11
src/main.rs
11
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();
|
||||
}
|
||||
});
|
||||
|
23
src/pins.rs
23
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
|
||||
|
103
src/usb.rs
Normal file
103
src/usb.rs
Normal 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(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user