thermostat/src/usb.rs
2024-10-14 13:25:49 +08:00

106 lines
2.6 KiB
Rust

use core::{
fmt::{self, Write},
mem::MaybeUninit,
};
use cortex_m::interrupt::free;
use log::{Log, Metadata, Record};
use stm32f4xx_hal::{
otg_fs::{UsbBus as Bus, USB},
stm32::{interrupt, Interrupt, NVIC},
};
use usb_device::{
class_prelude::UsbBusAllocator,
prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid},
};
use usbd_serial::SerialPort;
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(())
}
}