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",
|
"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]]
|
[[package]]
|
||||||
name = "embedded-hal"
|
name = "embedded-hal"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@ -296,8 +305,6 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "smoltcp"
|
name = "smoltcp"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@ -338,17 +345,18 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "stm32f4xx-hal"
|
name = "stm32f4xx-hal"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/stm32-rs/stm32f4xx-hal.git#4a1a019f8e6e81a69c726bcab1f7caadefdbd32e"
|
||||||
checksum = "b3a2f044469d1e3aff2cd02bee8b2724f3d5d91f3175e5d1ec99770320d16192"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal",
|
"bare-metal",
|
||||||
"cast",
|
"cast",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
|
"embedded-dma",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
"nb 0.1.3",
|
"nb 0.1.3",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"stm32f4",
|
"stm32f4",
|
||||||
|
"synopsys-usb-otg",
|
||||||
"void",
|
"void",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -363,6 +371,17 @@ dependencies = [
|
|||||||
"unicode-xid",
|
"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]]
|
[[package]]
|
||||||
name = "thermostat"
|
name = "thermostat"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@ -375,6 +394,7 @@ dependencies = [
|
|||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"hash2hwaddr",
|
"hash2hwaddr",
|
||||||
"log",
|
"log",
|
||||||
|
"nb 0.1.3",
|
||||||
"nom",
|
"nom",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"panic-abort",
|
"panic-abort",
|
||||||
@ -382,6 +402,8 @@ dependencies = [
|
|||||||
"smoltcp",
|
"smoltcp",
|
||||||
"stm32-eth",
|
"stm32-eth",
|
||||||
"stm32f4xx-hal",
|
"stm32f4xx-hal",
|
||||||
|
"usb-device",
|
||||||
|
"usbd-serial",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -396,6 +418,23 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
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]]
|
[[package]]
|
||||||
name = "vcell"
|
name = "vcell"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -21,7 +21,7 @@ bare-metal = "0.2"
|
|||||||
cortex-m = "0.6"
|
cortex-m = "0.6"
|
||||||
cortex-m-rt = { version = "0.6", features = ["device"] }
|
cortex-m-rt = { version = "0.6", features = ["device"] }
|
||||||
cortex-m-log = { version = "0.6", features = ["log-integration"] }
|
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" }
|
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"] }
|
smoltcp = { version = "0.6.0", default-features = false, features = ["proto-ipv4", "socket-tcp", "log"] }
|
||||||
hash2hwaddr = { version = "0.0", optional = true }
|
hash2hwaddr = { version = "0.0", optional = true }
|
||||||
@ -29,6 +29,12 @@ bit_field = "0.10"
|
|||||||
byteorder = { version = "1", default-features = false }
|
byteorder = { version = "1", default-features = false }
|
||||||
nom = { version = "5", default-features = false }
|
nom = { version = "5", default-features = false }
|
||||||
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
|
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]
|
[features]
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
|
use crate::usb;
|
||||||
|
|
||||||
#[cfg(not(feature = "semihosting"))]
|
#[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")]
|
#[cfg(feature = "semihosting")]
|
||||||
pub fn init_log() {
|
pub fn init_log() {
|
||||||
|
11
src/main.rs
11
src/main.rs
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
#![feature(maybe_uninit_extra, maybe_uninit_ref)]
|
||||||
// TODO: #![deny(warnings, unused)]
|
// TODO: #![deny(warnings, unused)]
|
||||||
|
|
||||||
#[cfg(not(feature = "semihosting"))]
|
#[cfg(not(feature = "semihosting"))]
|
||||||
@ -30,6 +31,7 @@ use smoltcp::{
|
|||||||
|
|
||||||
mod init_log;
|
mod init_log;
|
||||||
use init_log::init_log;
|
use init_log::init_log;
|
||||||
|
mod usb;
|
||||||
mod leds;
|
mod leds;
|
||||||
mod pins;
|
mod pins;
|
||||||
use pins::Pins;
|
use pins::Pins;
|
||||||
@ -90,17 +92,22 @@ fn main() -> ! {
|
|||||||
|
|
||||||
timer::setup(cp.SYST, clocks);
|
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,
|
clocks, dp.TIM1, dp.TIM3,
|
||||||
dp.GPIOA, dp.GPIOB, dp.GPIOC, dp.GPIOD, dp.GPIOE, dp.GPIOF, dp.GPIOG,
|
dp.GPIOA, dp.GPIOB, dp.GPIOC, dp.GPIOD, dp.GPIOE, dp.GPIOF, dp.GPIOG,
|
||||||
dp.SPI2, dp.SPI4, dp.SPI5,
|
dp.SPI2, dp.SPI4, dp.SPI5,
|
||||||
dp.ADC1,
|
dp.ADC1,
|
||||||
|
dp.OTG_FS_GLOBAL,
|
||||||
|
dp.OTG_FS_DEVICE,
|
||||||
|
dp.OTG_FS_PWRCLK,
|
||||||
);
|
);
|
||||||
|
|
||||||
leds.r1.on();
|
leds.r1.on();
|
||||||
leds.g3.off();
|
leds.g3.off();
|
||||||
leds.g4.off();
|
leds.g4.off();
|
||||||
|
|
||||||
|
usb::State::setup(usb);
|
||||||
|
|
||||||
let mut channels = Channels::new(pins);
|
let mut channels = Channels::new(pins);
|
||||||
let adc_calibration = [
|
let adc_calibration = [
|
||||||
channels.adc.get_calibration(0).unwrap(),
|
channels.adc.get_calibration(0).unwrap(),
|
||||||
@ -398,7 +405,7 @@ fn main() -> ! {
|
|||||||
cortex_m::interrupt::free(|cs| {
|
cortex_m::interrupt::free(|cs| {
|
||||||
if !net::is_pending(cs) {
|
if !net::is_pending(cs) {
|
||||||
// Wait for interrupts
|
// Wait for interrupts
|
||||||
// (Ethernet or SysTick)
|
// (Ethernet, SysTick, or USB)
|
||||||
wfi();
|
wfi();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
23
src/pins.rs
23
src/pins.rs
@ -14,10 +14,17 @@ use stm32f4xx_hal::{
|
|||||||
Output, PushPull,
|
Output, PushPull,
|
||||||
Speed::VeryHigh,
|
Speed::VeryHigh,
|
||||||
},
|
},
|
||||||
|
otg_fs::USB,
|
||||||
rcc::Clocks,
|
rcc::Clocks,
|
||||||
pwm::{self, PwmChannels},
|
pwm::{self, PwmChannels},
|
||||||
spi::{Spi, NoMiso},
|
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,
|
time::U32Ext,
|
||||||
};
|
};
|
||||||
use stm32_eth::EthPins;
|
use stm32_eth::EthPins;
|
||||||
@ -103,7 +110,8 @@ impl Pins {
|
|||||||
gpioa: GPIOA, gpiob: GPIOB, gpioc: GPIOC, gpiod: GPIOD, gpioe: GPIOE, gpiof: GPIOF, gpiog: GPIOG,
|
gpioa: GPIOA, gpiob: GPIOB, gpioc: GPIOC, gpiod: GPIOD, gpioe: GPIOE, gpiof: GPIOF, gpiog: GPIOG,
|
||||||
spi2: SPI2, spi4: SPI4, spi5: SPI5,
|
spi2: SPI2, spi4: SPI4, spi5: SPI5,
|
||||||
adc1: ADC1,
|
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 gpioa = gpioa.split();
|
||||||
let gpiob = gpiob.split();
|
let gpiob = gpiob.split();
|
||||||
let gpioc = gpioc.split();
|
let gpioc = gpioc.split();
|
||||||
@ -186,7 +194,16 @@ impl Pins {
|
|||||||
rx_d1: gpioc.pc5,
|
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
|
/// 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