diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 0a7ee59..1bb4ff1 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -1,3 +1,4 @@ +#![feature(abi_avr_interrupt)] #![no_std] #![no_main] @@ -9,9 +10,9 @@ use atmega_hal::{ spi::{DataOrder, SerialClockRate, Settings, Spi}, Peripherals, }; -use avr_device::interrupt::{self, Mutex}; +use avr_device::interrupt; use avr_eeprom::{eeprom, Eeprom}; -use core::cell::RefCell; +use core::sync::atomic::{AtomicBool, Ordering}; use embedded_hal::{ blocking::delay::DelayMs, spi::{FullDuplex, Mode, Phase, Polarity}, @@ -19,6 +20,9 @@ use embedded_hal::{ use nb::block; use panic_halt as _; +mod rotary; +use rotary::{Direction, Rotary}; + eeprom! { static eeprom CONTRAST: u8 = 8; static eeprom BACKLIGHT: u8 = 1; @@ -345,20 +349,27 @@ impl Lcd { } } - fn screen(&mut self) { - self.clear(); + fn draw(&mut self, screen: &Screens) { + interrupt::free(|_cs| { + self.clear(); - interrupt::free(|cs| match &*SCREEN.borrow(cs).borrow() { - Screens::Splash(splash) => { - splash.draw(self); + match screen { + Screens::Splash(splash) => { + splash.draw(self); + } + Screens::Home(home) => { + home.draw(self); + } } - Screens::Home(home) => { - home.draw(self); - } - }) + }); } } +enum Input { + Next, + Previous, +} + pub trait Draw { fn draw(&self, lcd: &mut Lcd); } @@ -414,6 +425,41 @@ struct HomeScreen { active: HomeSelection, } +impl HomeScreen { + fn input(&self, input: &Input) -> Screens { + match input { + Input::Next => match self.active { + HomeSelection::Ch1 => Screens::Home(HomeScreen { + active: HomeSelection::Ch2, + }), + HomeSelection::Ch2 => Screens::Home(HomeScreen { + active: HomeSelection::Ch3, + }), + HomeSelection::Ch3 => Screens::Home(HomeScreen { + active: HomeSelection::Setup, + }), + HomeSelection::Setup => Screens::Home(HomeScreen { + active: HomeSelection::Ch1, + }), + }, + Input::Previous => match self.active { + HomeSelection::Ch1 => Screens::Home(HomeScreen { + active: HomeSelection::Setup, + }), + HomeSelection::Ch2 => Screens::Home(HomeScreen { + active: HomeSelection::Ch1, + }), + HomeSelection::Ch3 => Screens::Home(HomeScreen { + active: HomeSelection::Ch2, + }), + HomeSelection::Setup => Screens::Home(HomeScreen { + active: HomeSelection::Ch3, + }), + }, + } + } +} + impl Draw for HomeScreen { fn draw(&self, lcd: &mut Lcd) { match &self.active { @@ -450,112 +496,132 @@ enum Screens { Home(HomeScreen), } -// TODO: Investigate if this should also be volatile -static SCREEN: Mutex> = Mutex::new(RefCell::new(Screens::Splash(SplashScreen))); +impl Screens { + fn input(&mut self, input: &Input) { + match self { + Screens::Splash(_) => {} + Screens::Home(home) => *self = home.input(input), + } + } +} + +static UPDATE_ENCODER: AtomicBool = AtomicBool::new(false); + +#[interrupt(atmega328p)] +#[allow(non_snake_case)] +fn PCINT0() { + interrupt::free(|_cs| { + UPDATE_ENCODER.store(true, Ordering::SeqCst); + }) +} #[atmega_hal::entry] fn main() -> ! { - interrupt::free(|cs| { - // Get peripherals, pins, eeprom and delay - let dp = Peripherals::take().unwrap(); - let pins = pins!(dp); - let eeprom = Eeprom::new(dp.EEPROM); - let mut delay = Delay::::new(); + // Get peripherals, pins, eeprom and delay + let dp = Peripherals::take().unwrap(); + let pins = pins!(dp); + let eeprom = Eeprom::new(dp.EEPROM); + let mut delay = Delay::::new(); - // Get contrast and backlight from EEPROM - let contrast = eeprom.read_value(&CONTRAST); - let backlight = eeprom.read_value(&BACKLIGHT); + // Get contrast and backlight from EEPROM + let contrast = eeprom.read_value(&CONTRAST); + let backlight = eeprom.read_value(&BACKLIGHT); - // Init display backlight - let tc0 = dp.TC0; - tc0.tccr0a.write(|w| { - w.wgm0().pwm_fast(); - w.com0b().match_clear(); - w - }); - tc0.tccr0b.write(|w| { - w.wgm02().set_bit(); - w.cs0().prescale_64(); - w - }); - tc0.ocr0a.write(|w| unsafe { w.bits(255) }); - tc0.ocr0b.write(|w| unsafe { w.bits(backlight) }); - pins.pd5.into_output(); + // Init display backlight + let tc0 = dp.TC0; + tc0.tccr0a.write(|w| { + w.wgm0().pwm_fast(); + w.com0b().match_clear(); + w + }); + tc0.tccr0b.write(|w| { + w.wgm02().set_bit(); + w.cs0().prescale_64(); + w + }); + tc0.ocr0a.write(|w| unsafe { w.bits(255) }); + tc0.ocr0b.write(|w| unsafe { w.bits(backlight) }); + pins.pd5.into_output(); - // Init SPI - let (spi, _) = Spi::new( - dp.SPI, - pins.pb5.into_output(), - pins.pb3.into_output(), - pins.pb4.into_pull_up_input(), - pins.pb2.into_output(), - Settings { - data_order: DataOrder::MostSignificantFirst, - clock: SerialClockRate::OscfOver2, - mode: Mode { - polarity: Polarity::IdleLow, - phase: Phase::CaptureOnFirstTransition, - }, + // Init SPI + let (spi, _) = Spi::new( + dp.SPI, + pins.pb5.into_output(), + pins.pb3.into_output(), + pins.pb4.into_pull_up_input(), + pins.pb2.into_output(), + Settings { + data_order: DataOrder::MostSignificantFirst, + clock: SerialClockRate::OscfOver2, + mode: Mode { + polarity: Polarity::IdleLow, + phase: Phase::CaptureOnFirstTransition, }, - ); + }, + ); - // Init LCD - let mut lcd = Lcd { - spi, - cd: pins.pb0.into_output(), - rst: pins.pb1.into_output(), - }; - lcd.init(contrast); + // Init LCD + let mut lcd = Lcd { + spi, + cd: pins.pb0.into_output(), + rst: pins.pb1.into_output(), + }; + lcd.init(contrast); - // Draw splash screen - lcd.screen(); + // Init encoder + let mut encoder = Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input()); + encoder.update(); - // Show splash screen for a moment - delay.delay_ms(2000_u16); + // Enable interrupts for encoder // TODO: and switch + dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0001)); + dp.EXINT.pcmsk0.write(|w| w.pcint().bits(0b1100_0000)); - // Set home screen - let screen = SCREEN.borrow(cs); - *screen.borrow_mut() = Screens::Home(HomeScreen { - active: HomeSelection::Ch1, - }); + // Create screen + let mut screen = Screens::Splash(SplashScreen); - // Draw screen - lcd.screen(); + // Draw splash screen + lcd.draw(&screen); - // Show splash screen for a moment - delay.delay_ms(1000_u16); + // Show splash screen for a moment + delay.delay_ms(2000_u16); - // Demo select CH2 - *screen.borrow_mut() = Screens::Home(HomeScreen { - active: HomeSelection::Ch2, - }); - - // Draw screen - lcd.screen(); - - // Show splash screen for a moment - delay.delay_ms(1000_u16); - - // Demo select CH3 - *screen.borrow_mut() = Screens::Home(HomeScreen { - active: HomeSelection::Ch3, - }); - - // Draw screen - lcd.screen(); - - // Show splash screen for a moment - delay.delay_ms(1000_u16); - - // Demo select SETUP - *screen.borrow_mut() = Screens::Home(HomeScreen { - active: HomeSelection::Setup, - }); - - // Draw screen - lcd.screen(); + // Set home screen + screen = Screens::Home(HomeScreen { + active: HomeSelection::Ch1, }); + // Draw screen + lcd.draw(&screen); + + let mut update_screen = false; + + // Enable interrupts globally + unsafe { interrupt::enable() }; + #[allow(clippy::empty_loop)] - loop {} + loop { + if UPDATE_ENCODER.load(Ordering::SeqCst) { + interrupt::free(|_cs| { + delay.delay_ms(3_u8); + + match encoder.update() { + Direction::Clockwise => { + screen.input(&Input::Next); + update_screen = true; + } + Direction::CounterClockwise => { + screen.input(&Input::Previous); + update_screen = true; + } + Direction::None => {} + } + UPDATE_ENCODER.store(false, Ordering::SeqCst); + }) + } + + if update_screen { + lcd.draw(&screen); + update_screen = false; + } + } } diff --git a/firmware/rust/src/rotary.rs b/firmware/rust/src/rotary.rs new file mode 100644 index 0000000..21919dc --- /dev/null +++ b/firmware/rust/src/rotary.rs @@ -0,0 +1,51 @@ +use embedded_hal::digital::v2::InputPin; + +pub struct Rotary { + pin_a: A, + pin_b: B, + state: u8, +} + +pub enum Direction { + Clockwise, + CounterClockwise, + None, +} + +impl Rotary +where + A: InputPin, + B: InputPin, +{ + pub fn new(pin_a: A, pin_b: B) -> Self { + Self { + pin_a, + pin_b, + state: 0, + } + } + + pub fn update(&mut self) -> Direction { + let a = self.pin_a.is_low(); + let b = self.pin_b.is_low(); + + let new_state = match (a, b) { + (Ok(true), Ok(true)) => 0b00, + (Ok(true), Ok(false)) => 0b01, + (Ok(false), Ok(true)) => 0b10, + (Ok(false), Ok(false)) => 0b11, + _ => return Direction::None, + }; + + let direction = match (self.state, new_state) { + (0b10, 0b11) => Direction::Clockwise, + (0b01, 0b00) => Direction::Clockwise, + (0b01, 0b11) => Direction::CounterClockwise, + (0b10, 0b00) => Direction::CounterClockwise, + _ => Direction::None, + }; + + self.state = new_state; + direction + } +}