From fdd1f4636d2d5dcbaca356a6e4b40f08a42a693a Mon Sep 17 00:00:00 2001 From: finga Date: Wed, 30 Mar 2022 19:29:32 +0200 Subject: [PATCH] fw-rust: Refactor everything Remove avr-eeprom dependency for now and introduce events. --- firmware/rust/Cargo.lock | 10 -- firmware/rust/Cargo.toml | 1 - firmware/rust/src/eeprom.rs | 48 +++++ firmware/rust/src/lcd.rs | 70 +++++--- firmware/rust/src/main.rs | 273 ++++++++++++++--------------- firmware/rust/src/screen/home.rs | 60 +++---- firmware/rust/src/screen/mod.rs | 90 +++++++++- firmware/rust/src/screen/setup.rs | 166 +++++++++++++----- firmware/rust/src/screen/splash.rs | 4 +- 9 files changed, 459 insertions(+), 263 deletions(-) create mode 100644 firmware/rust/src/eeprom.rs diff --git a/firmware/rust/Cargo.lock b/firmware/rust/Cargo.lock index 04ba3b3..c856d14 100644 --- a/firmware/rust/Cargo.lock +++ b/firmware/rust/Cargo.lock @@ -32,15 +32,6 @@ dependencies = [ "syn", ] -[[package]] -name = "avr-eeprom" -version = "0.1.0-dev" -source = "git+https://git.onders.org/finga/avr-eeprom-rs.git?branch=main#96397b5fbbaaa8531842db301127c1346f6e05f9" -dependencies = [ - "avr-device", - "nb 1.0.0", -] - [[package]] name = "avr-hal-generic" version = "0.1.0" @@ -76,7 +67,6 @@ version = "0.1.0-dev" dependencies = [ "atmega-hal", "avr-device", - "avr-eeprom", "embedded-hal", "nb 1.0.0", "panic-halt", diff --git a/firmware/rust/Cargo.toml b/firmware/rust/Cargo.toml index 9a1d6e9..aee0597 100644 --- a/firmware/rust/Cargo.toml +++ b/firmware/rust/Cargo.toml @@ -15,7 +15,6 @@ panic-halt = "0.2" nb = "1.0" embedded-hal = "0.2" avr-device = { version = "0.3", features = ["atmega328p"] } -avr-eeprom = { git = "https://git.onders.org/finga/avr-eeprom-rs.git", branch = "main" } [dependencies.atmega-hal] git = "https://github.com/rahix/avr-hal" diff --git a/firmware/rust/src/eeprom.rs b/firmware/rust/src/eeprom.rs new file mode 100644 index 0000000..dad5600 --- /dev/null +++ b/firmware/rust/src/eeprom.rs @@ -0,0 +1,48 @@ +use atmega_hal::Peripherals; +use avr_device::interrupt; +use core::convert::Infallible; +use nb::Error::WouldBlock; + +pub fn read_byte(p_addr: *const u8) -> nb::Result { + let dp = unsafe { Peripherals::steal() }; + let eeprom = dp.EEPROM; + + // Wait for completion of previous access + if eeprom.eecr.read().eepe().bit_is_set() { + return Err(WouldBlock); + } + + interrupt::free(|_cs| { + // Write address into EEPROM address register + eeprom.eear.write(|w| unsafe { w.bits(p_addr as u16) }); + // Start to read from EEPROM by setting EERE + eeprom.eecr.write(|w| w.eere().set_bit()); + }); + + // Return data from EEPROM data register + Ok(eeprom.eedr.read().bits()) +} + +pub fn write_byte(p_addr: *const u8, data: u8) -> nb::Result<(), Infallible> { + let dp = unsafe { Peripherals::steal() }; + let eeprom = dp.EEPROM; + + // Wait for completion of previous access + if eeprom.eecr.read().eepe().bit_is_set() { + return Err(WouldBlock); + } + + interrupt::free(|_cs| { + // Write address into EEPROM address register + eeprom.eear.write(|w| unsafe { w.bits(p_addr as u16) }); + // Write data into EEPROM data register + eeprom.eedr.write(|w| unsafe { w.bits(data) }); + // Enable writing to the EEPROM by setting EEMPE + eeprom.eecr.write(|w| w.eempe().set_bit()); + // Start to write to EEPROM by setting EEPE + eeprom.eecr.write(|w| w.eepe().set_bit()); + }); + + // Return data from EEPROM data register + Ok(()) +} diff --git a/firmware/rust/src/lcd.rs b/firmware/rust/src/lcd.rs index 49adb06..53dee58 100644 --- a/firmware/rust/src/lcd.rs +++ b/firmware/rust/src/lcd.rs @@ -4,15 +4,11 @@ use atmega_hal::{ port::{mode::Output, Pin, PB0, PB1}, Spi, }; -use avr_device::interrupt; use core::convert::TryInto; use embedded_hal::{blocking::delay::DelayMs, spi::FullDuplex}; use nb::block; -use crate::{ - assets::SYMBOL_TABLE, - screen::{Screen, Screens}, -}; +use crate::{assets::SYMBOL_TABLE, eeprom, CONTRAST}; // TODO: Make `cd` and `rst` pins generic pins pub struct Lcd { @@ -26,7 +22,7 @@ impl Lcd { Self { spi, cd, rst } } - pub fn init(&mut self, contrast: u8) { + pub fn init(&mut self) { let mut delay = Delay::::new(); // TODO: Test if delay is really needed @@ -40,13 +36,13 @@ impl Lcd { 0xA1, // (13) Set SEG direction: SEG reverse 0xC0, // (14) Set COM direction: Normal COM0 - COM63 // 0xA4, // (10) Set All Pixel On: Disable -> Set All Pixel to ON - 0xA6, // (11) Set Inverse Display: Display inverse off - 0xA2, // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) - 0x2F, // (5) Set Power Control: Booster, Regulator and Follower on - 0x27, // (8) Set VLCD Resistor Ratio: Set Contrast - 0xEE, // (18) Reset Cursor Update Mode - 0x81, // (9) Set Electronic Volume: Set Contrast - contrast, // (9) Set Electronic Volume: Set Contrast + 0xA6, // (11) Set Inverse Display: Display inverse off + 0xA2, // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) + 0x2F, // (5) Set Power Control: Booster, Regulator and Follower on + 0x27, // (8) Set VLCD Resistor Ratio: Set Contrast + 0xEE, // (18) Reset Cursor Update Mode + 0x81, // (9) Set Electronic Volume: Set Contrast + nb::block!(eeprom::read_byte(&CONTRAST)).unwrap(), // (9) Set Electronic Volume: Set Contrast // 0xFA, // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C // 0x90, // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C 0xAF, // (12) Set Display Enable: Display on @@ -60,6 +56,13 @@ impl Lcd { delay.delay_ms(1_u8); } + pub fn set_contrast(&mut self, contrast: u8) { + assert!(contrast <= 63); + + block!(self.spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast + block!(self.spi.send(contrast)).unwrap(); // (9) Set Electronic Volume: Set Contrast + } + pub fn move_cursor(&mut self, segment: u8, page: u8) { assert!(segment < 102); assert!(page < 8); @@ -181,21 +184,36 @@ impl Lcd { } } - pub fn draw(&mut self, screen: &Screens) { - interrupt::free(|_cs| { - self.fill_area(0, 0, 102, 8, 0x00); + pub fn print_u8_inverted(&mut self, segment: u8, page: u8, digits: u8, data: u8) { + assert!(digits <= 3); + let mut delay = Delay::::new(); - match screen { - Screens::Splash(splash) => { - splash.draw(self); - } - Screens::Home(home) => { - home.draw(self); - } - Screens::Setup(setup) => { - setup.draw(self); + let mut array = [0usize; 3]; + for (i, item) in array.iter_mut().enumerate() { + *item = ((data / 10_u8.pow(i.try_into().unwrap())) % 10).into(); + } + array.reverse(); + + for i in 0..2 { + self.move_cursor(segment, page + i); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + self.cd.set_high(); + + block!(self.spi.send(0xFF)).unwrap(); + block!(self.spi.send(0xFF)).unwrap(); + for j in 3 - digits..3 { + for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { + block!(self.spi.send(!*segment)).unwrap(); } + block!(self.spi.send(0xFF)).unwrap(); } - }); + block!(self.spi.send(0xFF)).unwrap(); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + self.cd.set_low(); + } } } diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index a5ac1fc..9e9eb49 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -5,13 +5,14 @@ use atmega_hal::{ clock::MHz8, delay::Delay, + pac::{EXINT, TC1}, pins, + port::{self, mode::PullUp, Pin, PB6, PB7, PC0}, spi::{DataOrder, SerialClockRate, Settings, Spi}, Peripherals, }; use avr_device::interrupt; -use avr_eeprom::{eeprom, Eeprom}; -use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; use embedded_hal::{ blocking::delay::DelayMs, spi::{Mode, Phase, Polarity}, @@ -19,18 +20,21 @@ use embedded_hal::{ use panic_halt as _; mod assets; +mod eeprom; mod lcd; mod rotary; mod screen; -use lcd::Lcd; use rotary::{Direction, Rotary}; -use screen::Screens; +use screen::{Screen, Screens}; -eeprom! { - static eeprom EEPROM_CONTRAST: u8 = 8; - static eeprom EEPROM_BACKLIGHT: u8 = 1; -} +#[used] +#[link_section = ".eeprom"] +static BACKLIGHT: u8 = 0; + +#[used] +#[link_section = ".eeprom"] +static CONTRAST: u8 = 8; pub enum Input { Next, @@ -39,11 +43,122 @@ pub enum Input { Back, } +struct ClockGenerator { + screen: Screen, + tc1: TC1, + exint: EXINT, + encoder: Rotary, PB6>, Pin, PB7>>, + button: Pin, PC0>, + delay: Delay, +} + +impl ClockGenerator { + fn new() -> Self { + // Get peripherals, pins and eeprom + let dp = Peripherals::take().unwrap(); + let pins = pins!(dp); + + // Set display PWM backlight pin as output + 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, + }, + }, + ); + + Self { + screen: Screen::new(dp.TC0, spi, pins.pb0.into_output(), pins.pb1.into_output()), + tc1: dp.TC1, + exint: dp.EXINT, + delay: Delay::::new(), + encoder: Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input()), + button: pins.pc0.into_pull_up_input(), + } + } + + fn init(&mut self) { + // Init Timer/Counter1 + self.tc1.ocr1a.write(|w| unsafe { w.bits(65535) }); + + // Enable interrupts for encoder, button and timer + self.exint.pcicr.write(|w| w.pcie().bits(0b0000_0011)); + self.exint.pcmsk0.write(|w| w.pcint().bits(0b1100_0000)); + self.exint.pcmsk1.write(|w| w.pcint().bits(0b0000_0001)); + self.tc1.timsk1.write(|w| w.ocie1a().set_bit()); + + // Init screen + self.screen.init(); + + // Show splash screen for a moment + self.delay.delay_ms(2000_u16); + + // Set home screen + self.screen.change(Screens::Home(screen::Home::new())); + + // Enable interrupts globally + unsafe { interrupt::enable() }; + } + + fn update(&mut self) { + if UPDATE_ENCODER.load(Ordering::SeqCst) { + interrupt::free(|_cs| { + self.delay.delay_ms(3_u8); + + match self.encoder.update() { + Direction::Clockwise => { + self.screen.input(&Input::Next); + } + Direction::CounterClockwise => { + self.screen.input(&Input::Previous); + } + Direction::None => {} + } + UPDATE_ENCODER.store(false, Ordering::SeqCst); + }) + } + + if UPDATE_TIMER.load(Ordering::SeqCst) { + self.tc1.tccr1b.write(|w| w.cs1().no_clock()); + self.screen.input(&Input::Back); + UPDATE_TIMER.store(false, Ordering::SeqCst); + } + + if UPDATE_BUTTON.load(Ordering::SeqCst) { + self.delay.delay_ms(2_u8); + + if self.button.is_low() { + // Press + if self.tc1.tccr1b.read().cs1().is_no_clock() { + self.tc1.tcnt1.write(|w| unsafe { w.bits(0) }); + self.tc1.tccr1b.write(|w| w.cs1().prescale_64()); + } + } else { + // Release + if self.tc1.tccr1b.read().cs1().is_prescale_64() { + self.tc1.tccr1b.write(|w| w.cs1().no_clock()); + self.screen.input(&Input::Select); + } + } + UPDATE_BUTTON.store(false, Ordering::SeqCst); + } + } +} + static UPDATE_ENCODER: AtomicBool = AtomicBool::new(false); static UPDATE_BUTTON: AtomicBool = AtomicBool::new(false); static UPDATE_TIMER: AtomicBool = AtomicBool::new(false); -static CONTRAST: AtomicU8 = AtomicU8::new(0); -static BACKLIGHT: AtomicU8 = AtomicU8::new(0); #[interrupt(atmega328p)] #[allow(non_snake_case)] @@ -71,142 +186,10 @@ fn TIMER1_COMPA() { #[atmega_hal::entry] fn main() -> ! { - // 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(); + let mut cg = ClockGenerator::new(); + cg.init(); - // Get contrast and backlight from EEPROM - CONTRAST.store(eeprom.read_value(&EEPROM_CONTRAST), Ordering::SeqCst); - BACKLIGHT.store(eeprom.read_value(&EEPROM_BACKLIGHT), Ordering::SeqCst); - - // 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.load(Ordering::SeqCst)) }); - 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 LCD - let mut lcd = Lcd::new(spi, pins.pb0.into_output(), pins.pb1.into_output()); - lcd.init(CONTRAST.load(Ordering::SeqCst)); - - // Init encoder - let mut encoder = Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input()); - encoder.update(); - - // Init button - let button = pins.pc0.into_pull_up_input(); - - // Init Timer/Counter1 - let tc1 = dp.TC1; - tc1.ocr1a.write(|w| unsafe { w.bits(65535) }); - - // Enable interrupts for encoder, button and timer - dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0011)); - dp.EXINT.pcmsk0.write(|w| w.pcint().bits(0b1100_0000)); - dp.EXINT.pcmsk1.write(|w| w.pcint().bits(0b0000_0001)); - tc1.timsk1.write(|w| w.ocie1a().set_bit()); - - // Create screen - let mut screen = Screens::Splash(screen::Splash); - - // Draw splash screen - lcd.draw(&screen); - - // Show splash screen for a moment - delay.delay_ms(2000_u16); - - // Set home screen - screen = Screens::Home(screen::Home::new()); - - // Draw screen - lcd.draw(&screen); - - let mut update_screen = false; - - // Enable interrupts globally - unsafe { interrupt::enable() }; - - #[allow(clippy::empty_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_TIMER.load(Ordering::SeqCst) { - tc1.tccr1b.write(|w| w.cs1().no_clock()); - screen.input(&Input::Back); - update_screen = true; - UPDATE_TIMER.store(false, Ordering::SeqCst); - } - - if UPDATE_BUTTON.load(Ordering::SeqCst) { - delay.delay_ms(2_u8); - - if button.is_low() { - // Press - if tc1.tccr1b.read().cs1().is_no_clock() { - tc1.tcnt1.write(|w| unsafe { w.bits(0) }); - tc1.tccr1b.write(|w| w.cs1().prescale_64()); - } - } else { - // Release - if tc1.tccr1b.read().cs1().is_prescale_64() { - tc1.tccr1b.write(|w| w.cs1().no_clock()); - screen.input(&Input::Select); - update_screen = true; - } - } - UPDATE_BUTTON.store(false, Ordering::SeqCst); - } - - if update_screen { - lcd.draw(&screen); - update_screen = false; - } + cg.update(); } } diff --git a/firmware/rust/src/screen/home.rs b/firmware/rust/src/screen/home.rs index 749d42f..3fe84ba 100644 --- a/firmware/rust/src/screen/home.rs +++ b/firmware/rust/src/screen/home.rs @@ -1,4 +1,4 @@ -use super::{Screen, Screens, Setup, Splash}; +use super::{Draw, Event, Screens, Setup, Splash}; use crate::{lcd::Lcd, Input}; enum Selection { @@ -19,39 +19,39 @@ impl Home { } } - pub fn input(&self, input: &Input) -> Screens { - Screens::Home(Self { - active: match self.active { - Selection::Ch1 => match input { - Input::Next => Selection::Ch2, - Input::Previous => Selection::Setup, - Input::Select => return Screens::Splash(Splash), - Input::Back => Selection::Ch1, - }, - Selection::Ch2 => match input { - Input::Next => Selection::Ch3, - Input::Previous => Selection::Ch1, - Input::Select => return Screens::Splash(Splash), - Input::Back => Selection::Ch2, - }, - Selection::Ch3 => match input { - Input::Next => Selection::Setup, - Input::Previous => Selection::Ch2, - Input::Select => return Screens::Splash(Splash), - Input::Back => Selection::Ch3, - }, - Selection::Setup => match input { - Input::Next => Selection::Ch1, - Input::Previous => Selection::Ch3, - Input::Select => return Screens::Setup(Setup::new()), - Input::Back => Selection::Setup, - }, + pub fn input(&mut self, input: &Input) -> Event { + self.active = match self.active { + Selection::Ch1 => match input { + Input::Next => Selection::Ch2, + Input::Previous => Selection::Setup, + Input::Select => return Event::Screen(Screens::Splash(Splash)), + Input::Back => Selection::Ch1, }, - }) + Selection::Ch2 => match input { + Input::Next => Selection::Ch3, + Input::Previous => Selection::Ch1, + Input::Select => return Event::Screen(Screens::Splash(Splash)), + Input::Back => Selection::Ch2, + }, + Selection::Ch3 => match input { + Input::Next => Selection::Setup, + Input::Previous => Selection::Ch2, + Input::Select => return Event::Screen(Screens::Splash(Splash)), + Input::Back => Selection::Ch3, + }, + Selection::Setup => match input { + Input::Next => Selection::Ch1, + Input::Previous => Selection::Ch3, + Input::Select => return Event::Screen(Screens::Setup(Setup::new())), + Input::Back => Selection::Setup, + }, + }; + + Event::None } } -impl Screen for Home { +impl Draw for Home { fn draw(&self, lcd: &mut Lcd) { match &self.active { Selection::Ch1 => { diff --git a/firmware/rust/src/screen/mod.rs b/firmware/rust/src/screen/mod.rs index 09d46be..fabedcc 100644 --- a/firmware/rust/src/screen/mod.rs +++ b/firmware/rust/src/screen/mod.rs @@ -1,15 +1,28 @@ +use atmega_hal::{ + pac::TC0, + port::{mode::Output, Pin, PB0, PB1}, + Spi, +}; + mod home; mod setup; mod splash; -use crate::{lcd::Lcd, Input}; +use crate::{eeprom, lcd::Lcd, Input, BACKLIGHT}; pub use home::Home; pub use setup::Setup; pub use splash::Splash; // TODO: Only update changes instead of whole screen -pub trait Screen { +pub enum Event { + Screen(Screens), + Backlight(u8), + Contrast(u8), + None, +} + +pub trait Draw { fn draw(&self, lcd: &mut Lcd); } @@ -20,11 +33,76 @@ pub enum Screens { } impl Screens { - pub fn input(&mut self, input: &Input) { + pub fn input(&mut self, input: &Input) -> Event { match self { - Screens::Splash(_) => {} - Screens::Home(home) => *self = home.input(input), - Screens::Setup(setup) => *self = setup.input(input), + Screens::Splash(_) => Event::None, + Screens::Home(home) => home.input(input), + Screens::Setup(setup) => setup.input(input), } } } + +pub struct Screen { + lcd: Lcd, + tc0: TC0, + screen: Screens, +} + +impl Screen { + pub fn new(tc0: TC0, spi: Spi, cd: Pin, rst: Pin) -> Self { + Self { + lcd: Lcd::new(spi, cd, rst), + tc0, + screen: Screens::Splash(Splash), + } + } + + pub fn init(&mut self) { + // Init display backlight + self.tc0.tccr0a.write(|w| { + w.wgm0().pwm_fast(); + w.com0b().match_clear(); + w + }); + self.tc0.tccr0b.write(|w| { + w.wgm02().set_bit(); + w.cs0().prescale_64(); + w + }); + self.tc0.ocr0a.write(|w| unsafe { w.bits(255) }); + self.tc0 + .ocr0b + .write(|w| unsafe { w.bits(nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap()) }); + + // Init lcd display + self.lcd.init(); + + self.draw(); + } + + pub fn draw(&mut self) { + self.lcd.fill_area(0, 0, 102, 8, 0x00); + + match &self.screen { + Screens::Splash(splash) => splash.draw(&mut self.lcd), + Screens::Home(home) => home.draw(&mut self.lcd), + Screens::Setup(setup) => setup.draw(&mut self.lcd), + } + } + + pub fn input(&mut self, input: &Input) { + match self.screen.input(input) { + Event::Screen(screen) => self.screen = screen, + Event::Backlight(backlight) => self.tc0.ocr0b.write(|w| unsafe { w.bits(backlight) }), + Event::Contrast(contrast) => self.lcd.set_contrast(contrast), + Event::None => {} + } + + self.draw(); + } + + pub fn change(&mut self, screen: Screens) { + self.screen = screen; + self.draw(); + } +} diff --git a/firmware/rust/src/screen/setup.rs b/firmware/rust/src/screen/setup.rs index a3a61bc..dd38809 100644 --- a/firmware/rust/src/screen/setup.rs +++ b/firmware/rust/src/screen/setup.rs @@ -1,84 +1,164 @@ -use super::{Home, Screen, Screens, Splash}; -use crate::{lcd::Lcd, Input, BACKLIGHT, CONTRAST}; -use core::sync::atomic::Ordering; +use super::{Draw, Event, Home, Screens}; +use crate::{eeprom, lcd::Lcd, Input, BACKLIGHT, CONTRAST}; enum Selection { - Contrast, Backlight, + BacklightEdit, + Contrast, + ContrastEdit, Back, } pub struct Setup { active: Selection, + backlight: u8, + contrast: u8, } impl Setup { pub fn new() -> Self { Self { - active: Selection::Contrast, + active: Selection::Backlight, + backlight: nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap(), + contrast: nb::block!(eeprom::read_byte(&CONTRAST)).unwrap(), } } - pub fn input(&self, input: &Input) -> Screens { - Screens::Setup(Self { - active: match self.active { - Selection::Contrast => match input { - Input::Next => Selection::Backlight, - Input::Previous => Selection::Back, - Input::Select => return Screens::Splash(Splash), - Input::Back => return Screens::Home(Home::new()), - }, - Selection::Backlight => match input { - Input::Next => Selection::Back, - Input::Previous => Selection::Contrast, - Input::Select => return Screens::Splash(Splash), - Input::Back => return Screens::Home(Home::new()), - }, - Selection::Back => match input { - Input::Next => Selection::Contrast, - Input::Previous => Selection::Backlight, - Input::Select => return Screens::Home(Home::new()), - Input::Back => return Screens::Home(Home::new()), - }, + pub fn input(&mut self, input: &Input) -> Event { + self.active = match self.active { + Selection::Backlight => match input { + Input::Next => Selection::Contrast, + Input::Previous => Selection::Back, + Input::Select => Selection::BacklightEdit, + Input::Back => return Event::Screen(Screens::Home(Home::new())), }, - }) + Selection::BacklightEdit => match input { + Input::Next => { + self.backlight = if self.backlight == u8::MAX { + u8::MIN + } else { + self.backlight + 1 + }; + + return Event::Backlight(self.backlight); + } + Input::Previous => { + self.backlight = if self.backlight == u8::MIN { + u8::MAX + } else { + self.backlight - 1 + }; + + return Event::Backlight(self.backlight); + } + Input::Select => { + nb::block!(eeprom::write_byte(&BACKLIGHT, self.backlight)).unwrap(); + Selection::Backlight + } + Input::Back => { + self.active = Selection::Backlight; + self.backlight = nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap(); + return Event::Backlight(self.backlight); + } + }, + Selection::Contrast => match input { + Input::Next => Selection::Back, + Input::Previous => Selection::Backlight, + Input::Select => Selection::ContrastEdit, + Input::Back => return Event::Screen(Screens::Home(Home::new())), + }, + Selection::ContrastEdit => match input { + Input::Next => { + self.contrast = if self.contrast >= 63 { + u8::MIN + } else { + self.contrast + 1 + }; + + return Event::Contrast(self.contrast); + } + Input::Previous => { + self.contrast = if self.contrast == u8::MIN { + 63 + } else { + self.contrast - 1 + }; + + return Event::Contrast(self.contrast); + } + Input::Select => { + nb::block!(eeprom::write_byte(&CONTRAST, self.contrast)).unwrap(); + Selection::Contrast + } + Input::Back => { + self.active = Selection::Contrast; + self.contrast = nb::block!(eeprom::read_byte(&CONTRAST)).unwrap(); + return Event::Contrast(self.contrast); + } + }, + Selection::Back => match input { + Input::Next => Selection::Backlight, + Input::Previous => Selection::Contrast, + Input::Select => return Event::Screen(Screens::Home(Home::new())), + Input::Back => return Event::Screen(Screens::Home(Home::new())), + }, + }; + + Event::None } } -impl Screen for Setup { +impl Draw for Setup { fn draw(&self, lcd: &mut Lcd) { - let contrast = CONTRAST.load(Ordering::SeqCst); - let backlight = BACKLIGHT.load(Ordering::SeqCst); - match &self.active { Selection::Contrast => { lcd.fill_area(0, 0, 33, 2, 0xFF); lcd.print_inverted(33, 0, "SETUP"); lcd.fill_area(69, 0, 33, 2, 0xFF); - lcd.print_inverted(0, 2, "CONTRAST:"); - lcd.print_u8(89, 2, 2, contrast); - lcd.print(0, 4, "BACKLIGHT:"); - lcd.print_u8(83, 2, 3, backlight); + lcd.print(0, 2, "BACKLIGHT:"); + lcd.print_u8(81, 2, 3, self.backlight); + lcd.print_inverted(0, 4, "CONTRAST:"); + lcd.print_u8(87, 4, 2, self.contrast); + lcd.print(36, 6, "BACK"); + } + Selection::ContrastEdit => { + lcd.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + lcd.print(0, 2, "BACKLIGHT:"); + lcd.print_u8(81, 2, 3, self.backlight); + lcd.print(0, 4, "CONTRAST:"); + lcd.print_u8_inverted(87, 4, 2, self.contrast); lcd.print(36, 6, "BACK"); } Selection::Backlight => { lcd.fill_area(0, 0, 33, 2, 0xFF); lcd.print_inverted(33, 0, "SETUP"); lcd.fill_area(69, 0, 33, 2, 0xFF); - lcd.print(0, 2, "CONTRAST:"); - lcd.print_u8(89, 2, 2, contrast); - lcd.print_inverted(0, 4, "BACKLIGHT:"); - lcd.print_u8(83, 2, 3, backlight); + lcd.print_inverted(0, 2, "BACKLIGHT:"); + lcd.print_u8(81, 2, 3, self.backlight); + lcd.print(0, 4, "CONTRAST:"); + lcd.print_u8(87, 4, 2, self.contrast); + lcd.print(36, 6, "BACK"); + } + Selection::BacklightEdit => { + lcd.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + lcd.print(0, 2, "BACKLIGHT:"); + lcd.print_u8_inverted(81, 2, 3, self.backlight); + lcd.print(0, 4, "CONTRAST:"); + lcd.print_u8(87, 4, 2, self.contrast); lcd.print(36, 6, "BACK"); } Selection::Back => { lcd.fill_area(0, 0, 33, 2, 0xFF); lcd.print_inverted(33, 0, "SETUP"); lcd.fill_area(69, 0, 33, 2, 0xFF); - lcd.print(0, 2, "CONTRAST:"); - lcd.print_u8(89, 2, 2, contrast); - lcd.print(0, 4, "BACKLIGHT:"); - lcd.print_u8(83, 2, 3, backlight); + lcd.print(0, 2, "BACKLIGHT:"); + lcd.print_u8(81, 2, 3, self.backlight); + lcd.print(0, 4, "CONTRAST:"); + lcd.print_u8(87, 4, 2, self.contrast); lcd.print_inverted(36, 6, "BACK"); } } diff --git a/firmware/rust/src/screen/splash.rs b/firmware/rust/src/screen/splash.rs index 219f314..fcbbafa 100644 --- a/firmware/rust/src/screen/splash.rs +++ b/firmware/rust/src/screen/splash.rs @@ -1,4 +1,4 @@ -use super::Screen; +use super::Draw; use crate::{ assets::{ONDERS_ORG, SACRED_CHAO}, lcd::Lcd, @@ -9,7 +9,7 @@ use nb::block; pub struct Splash; -impl Screen for Splash { +impl Draw for Splash { fn draw(&self, lcd: &mut Lcd) { let mut delay = Delay::::new();