use core::mem::MaybeUninit; use core::fmt::Write; use stm32f1xx_hal::{ pac, pac::{interrupt, Interrupt}, gpio, timer::CountDownTimer, }; use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType}; use usb_device::{bus::UsbBusAllocator, prelude::*}; use usbd_serial::{SerialPort, USB_CLASS_CDC}; use arrayvec::{ArrayVec, ArrayString}; /* Transmit 17.5 columns per second * With 14 lines per column, this gives 245 lines per second */ pub const TIMER_FREQ_HZ : u32 = 245; const FELDHELL_PATTERN_LEN : usize = 16; const MESSAGE_LEN : usize = 64; static mut FELDHELL_TIMER: MaybeUninit> = MaybeUninit::uninit(); static mut SHARED_USB: MaybeUninit = MaybeUninit::uninit(); static mut USB_BUS: Option> = None; static mut USB_SERIAL: Option> = None; static mut USB_DEVICE: Option> = None; /* Commands * ======== * Commands end with \n * * Switch to transmit: * tx * * Switch to receive: * rx * * Set frequency: * f10121000 * * Append to transmit message * mThe message to transmit * * Clear transmit message * clear * * Set FELDHELL wide font * fontw * * Set FELDHELL narrow font * fontn */ #[derive(Clone, Copy)] enum Font { Narrow, Wide, } // Shared between application and interrupt context struct USBSharedData { incoming_buf: ArrayVec::<[u8; MESSAGE_LEN]>, feldhell_pattern: ArrayVec::<[u16; FELDHELL_PATTERN_LEN]>, // FELDHELL columns to transmit } pub fn enable_interrupts() { unsafe { pac::NVIC::unmask(Interrupt::USB_HP_CAN_TX); pac::NVIC::unmask(Interrupt::USB_LP_CAN_RX0); pac::NVIC::unmask(Interrupt::TIM4); } } pub struct USBData { font : Font, pub frequency : u32, pub transmit : bool, current_str : ArrayVec::<[u8; 96]>, send_ix : usize, } impl USBData { pub fn new( timer4: CountDownTimer, usb: pac::USB, pin_dm: gpio::gpioa::PA11>, pin_dp: gpio::gpioa::PA12>) -> USBData { let peripheral = Peripheral { usb, pin_dm, pin_dp, }; { let timer = unsafe { &mut *FELDHELL_TIMER.as_mut_ptr() }; *timer = timer4; } unsafe { USB_BUS = Some(UsbBus::new(peripheral)); USB_SERIAL = Some(SerialPort::new(USB_BUS.as_ref().unwrap())); let usb_device = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x1d50, 0x5120)) // Openmoko Neo1973 serial .manufacturer("HB9EGM") .product("Beep Machine") .serial_number("1") .device_class(USB_CLASS_CDC) .build(); USB_DEVICE = Some(usb_device); let shared_usb = &mut *SHARED_USB.as_mut_ptr(); *shared_usb = USBSharedData { incoming_buf : ArrayVec::new(), feldhell_pattern : ArrayVec::new(), } } USBData { font : Font::Narrow, frequency : 10121000, transmit : false, current_str : ArrayVec::new(), send_ix : 0, } } fn send_once(&mut self, message : &[u8]) { cortex_m::interrupt::free(|_cs| { let serial = unsafe { USB_SERIAL.as_mut().unwrap() }; serial.write(message).ok(); }); } pub fn send_receive(&mut self) { self.send_once(b"Switch RX\n"); } pub fn send_transmit(&mut self) { self.send_once(b"Switch TX\n"); } pub fn handle(&mut self) { let in_message = cortex_m::interrupt::free(|_cs| { let shared_usb = unsafe { &mut *SHARED_USB.as_mut_ptr() }; let mut message = ArrayString::<[u8; MESSAGE_LEN]>::new(); let mut split_at = None; for (i, c) in shared_usb.incoming_buf.iter().enumerate() { if *c == b'\n' { split_at = Some(i); break; } } match split_at { Some(i) if i+1 == shared_usb.incoming_buf.len() => { write!(&mut message, "{}", core::str::from_utf8(&shared_usb.incoming_buf).unwrap()).ok(); shared_usb.incoming_buf.clear(); } Some(i) => { let (front, back) = shared_usb.incoming_buf.split_at(i+1); write!(&mut message, "{}", core::str::from_utf8(front).unwrap()).ok(); let mut keep = ArrayVec::<[u8; MESSAGE_LEN]>::new(); keep.try_extend_from_slice(back).ok(); shared_usb.incoming_buf.clear(); shared_usb.incoming_buf.try_extend_from_slice(&keep).ok(); } _ => { } }; if message.len() > 0 { Some(message) } else { None } }); let mut outgoing = ArrayVec::<[u8; MESSAGE_LEN]>::new(); if let Some(m) = in_message { if m.as_str() == "tx\n" { outgoing.try_extend_from_slice(b"TX ok\n").ok(); self.transmit = true; } else if m.as_str() == "rx\n" { outgoing.try_extend_from_slice(b"RX ok\n").ok(); self.current_str.clear(); self.transmit = false; } else if m.chars().nth(0).unwrap() == 'm' { self.current_str.clear(); for c in m.bytes().skip(1) { self.current_str.push(c); } outgoing.try_extend_from_slice(b"m ok\n").ok(); } else if m.as_str() == "fontw\n" { self.font = Font::Wide; outgoing.try_extend_from_slice(b"fontw ok\n").ok(); } else if m.as_str() == "fontn\n" { self.font = Font::Narrow; outgoing.try_extend_from_slice(b"fontn ok\n").ok(); } else if m.chars().nth(0).unwrap() == 'f' { let mut as_str = ArrayString::<[u8; MESSAGE_LEN]>::new(); for c in m.chars().skip(1) { as_str.push(c); } match u32::from_str_radix(as_str.as_str().trim(), 10) { Ok(val) => { self.frequency = val; outgoing.try_extend_from_slice(b"f ok\n").ok(); } _ => { outgoing.try_extend_from_slice(b"F").ok(); outgoing.try_extend_from_slice(as_str.as_bytes()).ok(); outgoing.try_extend_from_slice(b" err\n").ok(); } } } else { outgoing.try_extend_from_slice(b"no\n").ok(); } } let feldhell_pattern_length = cortex_m::interrupt::free(|_cs| { let serial = unsafe { USB_SERIAL.as_mut().unwrap() }; serial.write(&outgoing).ok(); let shared_usb = unsafe { &mut *SHARED_USB.as_mut_ptr() }; shared_usb.feldhell_pattern.len() }); if self.current_str.len() > 0 && feldhell_pattern_length < FELDHELL_PATTERN_LEN-8 { match self.current_str.get(self.send_ix) { Some(letter) => { let fat = match self.font { Font::Wide => true, Font::Narrow => false, }; match crate::feldhell_font::get_letter_by_columns(*letter, fat) { Some(columns) => { cortex_m::interrupt::free(|_cs| { let shared_usb = unsafe { &mut *SHARED_USB.as_mut_ptr() }; shared_usb.feldhell_pattern.try_extend_from_slice(&columns).ok(); }); } None => {} } } None => {} } self.send_ix += 1; if self.send_ix >= self.current_str.len() { cortex_m::interrupt::free(|_cs| { let serial = unsafe { USB_SERIAL.as_mut().unwrap() }; let mut outgoing = ArrayString::<[u8; 16]>::new(); write!(&mut outgoing, "FH sent {}\n", self.send_ix).ok(); serial.write(outgoing.as_bytes()).ok(); }); self.send_ix = 0; self.current_str.clear(); } } } pub fn is_transmit(&self) -> bool { self.transmit } } #[allow(non_snake_case)] #[interrupt] fn USB_HP_CAN_TX() { usb_interrupt(); } #[allow(non_snake_case)] #[interrupt] fn USB_LP_CAN_RX0() { usb_interrupt(); } fn usb_interrupt() { let usb_dev = unsafe { USB_DEVICE.as_mut().unwrap() }; let serial = unsafe { USB_SERIAL.as_mut().unwrap() }; if !usb_dev.poll(&mut [serial]) { return; } let mut buf = [0u8; MESSAGE_LEN]; let shared_usb = unsafe { &mut *SHARED_USB.as_mut_ptr() }; match serial.read(&mut buf) { Ok(count) if count > 0 => { if count > MESSAGE_LEN { panic!("illegal count"); } shared_usb.incoming_buf.try_extend_from_slice(&buf[0..count]).ok(); } _ => {} } } #[allow(non_snake_case)] #[interrupt] fn TIM4() { let timer = unsafe { &mut *FELDHELL_TIMER.as_mut_ptr() }; timer.clear_update_interrupt_flag(); let shared_usb = unsafe { &mut *SHARED_USB.as_mut_ptr() }; while shared_usb.feldhell_pattern.len() > 0 && shared_usb.feldhell_pattern.as_slice()[0] == 0 { shared_usb.feldhell_pattern.pop_at(0); } if shared_usb.feldhell_pattern.len() > 0 { let remove_first_element = { let pat = shared_usb.feldhell_pattern.as_mut_slice().first_mut().unwrap(); let shared = unsafe { &mut *crate::SHARED.as_mut_ptr() }; use embedded_hal::digital::v2::OutputPin; if shared.state.allow_feldhell_keying() && (*pat & 0b1) == 0b1 { shared.cw_key_out_n.set_low().unwrap(); } else { shared.cw_key_out_n.set_high().unwrap(); } *pat >>= 1; assert_ne!(*pat, 0); *pat == 0b1 }; if remove_first_element { shared_usb.feldhell_pattern.pop_at(0); } } }