From 03ad81d7afaea119df607b6e65f19bd5362fab39 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Thu, 15 Apr 2021 13:07:56 +0200 Subject: Get FELDHELL to work --- sw/eval-clock-cw-tx/src/usb.rs | 318 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 292 insertions(+), 26 deletions(-) (limited to 'sw/eval-clock-cw-tx/src/usb.rs') diff --git a/sw/eval-clock-cw-tx/src/usb.rs b/sw/eval-clock-cw-tx/src/usb.rs index e803c91..467c148 100644 --- a/sw/eval-clock-cw-tx/src/usb.rs +++ b/sw/eval-clock-cw-tx/src/usb.rs @@ -1,23 +1,96 @@ +use core::mem::MaybeUninit; use core::fmt::Write; -use stm32f1xx_hal::gpio; -use stm32f1xx_hal::pac; -use stm32f1xx_hal::pac::{interrupt, Interrupt}; +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; -pub struct USB { + +/* 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, } -impl USB { +// Shared between application and interrupt context +struct USBSharedData { + incoming_buf: ArrayVec::<[u8; MESSAGE_LEN]>, + message : ArrayString::<[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( - usb: stm32f1xx_hal::pac::USB, + timer4: CountDownTimer, + usb: pac::USB, pin_dm: gpio::gpioa::PA11>, - pin_dp: gpio::gpioa::PA12>) -> Self { + pin_dp: gpio::gpioa::PA12>) -> USBData { let peripheral = Peripheral { usb, @@ -25,32 +98,161 @@ impl USB { pin_dp, }; - unsafe { - let bus = UsbBus::new(peripheral); - - USB_BUS = Some(bus); + { + 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_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x1d50, 0x5120)) // Openmoko Neo1973 serial + 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); - USB_DEVICE = Some(usb_dev); + let shared_usb = &mut *SHARED_USB.as_mut_ptr(); + *shared_usb = USBSharedData { + incoming_buf : ArrayVec::new(), + message : ArrayString::new(), + feldhell_pattern : ArrayVec::new(), + } } - USB{} + USBData { + font : Font::Narrow, + frequency : 10121000, + transmit : false, + current_str : ArrayVec::new(), + send_ix : 0, + } } - pub fn enable_interrupts(&self) { - unsafe { - pac::NVIC::unmask(Interrupt::USB_HP_CAN_TX); - pac::NVIC::unmask(Interrupt::USB_LP_CAN_RX0); + pub fn handle(&mut self) { + let in_message = cortex_m::interrupt::free(|_cs| { + let shared_usb = unsafe { &mut *SHARED_USB.as_mut_ptr() }; + + if shared_usb.message.len() > 0 { + let mut outgoing = ArrayString::<[u8; MESSAGE_LEN]>::new(); + + if let Err(_) = write!(outgoing, "{}", &shared_usb.message) { + outgoing.clear(); + write!(outgoing, "EXT FAIL\n").ok(); + } + let serial = unsafe { USB_SERIAL.as_mut().unwrap() }; + serial.write(outgoing.as_bytes()).ok(); + + let in_message = shared_usb.message.clone(); + shared_usb.message.clear(); + Some(in_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.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() == "clear\n" { + self.current_str.clear(); + outgoing.try_extend_from_slice(b"clear 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)] @@ -73,18 +275,82 @@ fn usb_interrupt() { return; } - let mut buf = [0u8; 8]; - let mut string = arrayvec::ArrayString::<[_; 16]>::new(); + let mut buf = [0u8; 64]; + + let shared_usb = unsafe { &mut *SHARED_USB.as_mut_ptr() }; match serial.read(&mut buf) { Ok(count) if count > 0 => { - // Echo back in upper case - for c in buf[0..count].iter_mut() { - write!(string, "{:02x}", *c).ok(); + if count > 64 { + panic!("illegal count"); + } + shared_usb.incoming_buf.try_extend_from_slice(&buf[0..count]).ok(); + + let mut split_at = None; + for (i, c) in shared_usb.incoming_buf.iter().enumerate() { + if *c == b'\n' { + split_at = Some(i); + break; + } } - serial.write(string.as_bytes()).ok(); - string.clear(); + + match split_at { + Some(i) if i+1 == shared_usb.incoming_buf.len() => { + write!(&mut shared_usb.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 shared_usb.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(); + } + _ => { } + }; + } _ => {} } } + +#[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); + } + } +} -- cgit v1.2.3