aboutsummaryrefslogtreecommitdiffstats
path: root/sw/eval-clock-cw-tx/src/usb.rs
diff options
context:
space:
mode:
Diffstat (limited to 'sw/eval-clock-cw-tx/src/usb.rs')
-rw-r--r--sw/eval-clock-cw-tx/src/usb.rs318
1 files changed, 292 insertions, 26 deletions
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<CountDownTimer<pac::TIM4>> = MaybeUninit::uninit();
+
+static mut SHARED_USB: MaybeUninit<USBSharedData> = MaybeUninit::uninit();
static mut USB_BUS: Option<UsbBusAllocator<UsbBusType>> = None;
static mut USB_SERIAL: Option<usbd_serial::SerialPort<UsbBusType>> = None;
static mut USB_DEVICE: Option<UsbDevice<UsbBusType>> = 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<pac::TIM4>,
+ usb: pac::USB,
pin_dm: gpio::gpioa::PA11<gpio::Input<gpio::Floating>>,
- pin_dp: gpio::gpioa::PA12<gpio::Input<gpio::Floating>>) -> Self {
+ pin_dp: gpio::gpioa::PA12<gpio::Input<gpio::Floating>>) -> 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);
+ }
+ }
+}