#![no_std] #![no_main] #![feature(abi_avr_interrupt)] use core::char; use core::cell; use arduino_uno::{delay_ms, hal::{clock::MHz16, port::{mode::{Floating, Input, Output}, portb::PB5, portd::{PD0, PD1}}, usart::Usart}, pac::USART0, prelude::*}; use panic_halt as _; const SHORT: u16 = 100; const LONG: u16 = SHORT * 3; const SPACE: u16 = SHORT * 7; const PRESCALER: u32 = 1024; const TIMER_COUNTS: u32 = 125; const MILLIS_INCREMENT: u32 = PRESCALER * TIMER_COUNTS / 16000; static MILLIS_COUNTER: avr_device::interrupt::Mutex> = avr_device::interrupt::Mutex::new(cell::Cell::new(0)); type Serial = Usart>, PD1, MHz16>; fn millis_init(tc0: arduino_uno::pac::TC0) { // Configure the timer for the above interval (in CTC mode) // and enable its interrupt. tc0.tccr0a.write(|w| w.wgm0().ctc()); tc0.ocr0a.write(|w| unsafe { w.bits(TIMER_COUNTS as u8) }); tc0.tccr0b.write(|w| match PRESCALER { 8 => w.cs0().prescale_8(), 64 => w.cs0().prescale_64(), 256 => w.cs0().prescale_256(), 1024 => w.cs0().prescale_1024(), _ => panic!(), }); tc0.timsk0.write(|w| w.ocie0a().set_bit()); // Reset the global millisecond counter avr_device::interrupt::free(|cs| { MILLIS_COUNTER.borrow(cs).set(0); }); } #[avr_device::interrupt(atmega328p)] fn TIMER0_COMPA() { avr_device::interrupt::free(|cs| { let counter_cell = MILLIS_COUNTER.borrow(cs); let counter = counter_cell.get(); counter_cell.set(counter + MILLIS_INCREMENT); }) } fn millis() -> u32 { avr_device::interrupt::free(|cs| MILLIS_COUNTER.borrow(cs).get()) } struct RingBuf { data: [LedInstruction; N], read_idx: usize, write_idx: usize, } impl RingBuf { pub fn new(initial: LedInstruction) -> Self { Self { data: [initial; N], read_idx: 0, write_idx: 0, } } pub fn _peek(&self) -> Option<&LedInstruction> { if self.write_idx != self.read_idx { Some(&self.data[self.read_idx]) } else { None } } pub fn peek_mut(&mut self) -> Option<&mut LedInstruction> { if self.write_idx != self.read_idx { Some(&mut self.data[self.read_idx]) } else { None } } pub fn push(&mut self, value: LedInstruction) { if self.write_idx + 1 != self.read_idx { self.data[self.write_idx] = value; self.write_idx += 1; if self.write_idx == N { self.write_idx = 0; } } } pub fn pop(&mut self) -> Option { if self.write_idx != self.read_idx { let res = Some(self.data[self.read_idx]); self.read_idx += 1; if self.read_idx == N { self.read_idx = 0; } res } else { None } } pub fn update(&mut self, millis: u16, led: &mut PB5, serial: &mut Serial) { if let Some(cur) = self.peek_mut() { if cur.state { led.set_high().void_unwrap() } else { led.set_low().void_unwrap(); } if cur.time > millis { cur.time -= millis; } else { let time = cur.time; self.pop().unwrap(); self.update(millis - time, led, serial); } } } } #[derive(Clone, Copy)] struct LedInstruction { state: bool, time: u16, } impl LedInstruction { pub fn new(state: bool, time: u16) -> Self { Self { state, time, } } } macro_rules! push_morse { ( $buf:expr, $( $delay:expr ),* ) => { { $( $buf.push(LedInstruction::new(true, $delay)); $buf.push(LedInstruction::new(false, SHORT)); )* $buf.push(LedInstruction::new(false, LONG - SHORT)); } } } fn push_morse(buf: &mut RingBuf, c: u8) { let c = char::from_u32(c as u32); if let Some(c) = c { match c { 'a' => push_morse!(buf, SHORT, LONG), 'b' => push_morse!(buf, LONG, SHORT, SHORT, SHORT), 'c' => push_morse!(buf, LONG, SHORT, LONG, SHORT), 'd' => push_morse!(buf, LONG, SHORT, SHORT), 'e' => push_morse!(buf, SHORT), 'f' => push_morse!(buf, SHORT, SHORT, LONG, SHORT), 'g' => push_morse!(buf, LONG, LONG, SHORT), 'h' => push_morse!(buf, SHORT, SHORT, SHORT, SHORT), 'i' => push_morse!(buf, SHORT, SHORT), 'j' => push_morse!(buf, SHORT, LONG, LONG, LONG), 'k' => push_morse!(buf, LONG, SHORT, LONG), 'l' => push_morse!(buf, SHORT, LONG, SHORT, SHORT), 'm' => push_morse!(buf, LONG, LONG), 'n' => push_morse!(buf, LONG, SHORT), 'o' => push_morse!(buf, LONG, LONG, LONG), 'p' => push_morse!(buf, SHORT, LONG, LONG, SHORT), 'q' => push_morse!(buf, LONG, LONG, SHORT, LONG), 'r' => push_morse!(buf, SHORT, LONG, SHORT), 's' => push_morse!(buf, SHORT, SHORT, SHORT), 't' => push_morse!(buf, LONG), 'u' => push_morse!(buf, SHORT, SHORT, LONG), 'v' => push_morse!(buf, SHORT, SHORT, SHORT, LONG), 'w' => push_morse!(buf, SHORT, LONG, LONG), 'x' => push_morse!(buf, LONG, SHORT, SHORT, LONG), 'y' => push_morse!(buf, LONG, SHORT, LONG, LONG), 'z' => push_morse!(buf, LONG, LONG, SHORT, SHORT), '1' => push_morse!(buf, SHORT, LONG, LONG, LONG, LONG), '2' => push_morse!(buf, SHORT, SHORT, LONG, LONG, LONG), '3' => push_morse!(buf, SHORT, SHORT, SHORT, LONG, LONG), '4' => push_morse!(buf, SHORT, SHORT, SHORT, SHORT, LONG), '5' => push_morse!(buf, SHORT, SHORT, SHORT, SHORT, SHORT), '6' => push_morse!(buf, LONG, SHORT, SHORT, SHORT, SHORT), '7' => push_morse!(buf, LONG, LONG, SHORT, SHORT, SHORT), '8' => push_morse!(buf, LONG, LONG, LONG, SHORT, SHORT), '9' => push_morse!(buf, LONG, LONG, LONG, LONG, SHORT), '0' => push_morse!(buf, LONG, LONG, LONG, LONG, LONG), ' ' => buf.push(LedInstruction::new(false, SPACE)), _ => {}, } } } #[arduino_uno::entry] fn main() -> ! { let dp = arduino_uno::Peripherals::take().unwrap(); let mut pins = arduino_uno::Pins::new(dp.PORTB, dp.PORTC, dp.PORTD); let mut led = pins.d13.into_output(&mut pins.ddr); led.set_low().void_unwrap(); let mut serial = arduino_uno::Serial::new( dp.USART0, pins.d0, pins.d1.into_output(&mut pins.ddr), 57600.into_baudrate(), ); let mut buf: RingBuf<100> = RingBuf::new(LedInstruction::new(false, 0)); millis_init(dp.TC0); // Enable interrupts globally unsafe { avr_device::interrupt::enable() }; let mut last_millis = millis(); loop { // let b = nb::block!(serial.read()).void_unwrap(); match serial.read() { Ok(b) => push_morse(&mut buf, b), Err(_) => {}, } let new_millis = millis(); buf.update((new_millis - last_millis) as u16, &mut led, &mut serial); last_millis = new_millis; delay_ms(100); } }