diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index ddea84b..dcbae03 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -133,6 +133,21 @@ const SYM_9: [&[u8]; 2] = [ &[0x1C, 0x3D, 0x31, 0x3F, 0x1F], ]; +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_COLON: [&[u8]; 2] = [&[0x30, 0x30], &[0x0C, 0x0C]]; + +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_A: [&[u8]; 2] = [ + &[0xC0, 0xF0, 0x3C, 0x3C, 0xF0, 0xC0], + &[0x3F, 0x3F, 0x06, 0x06, 0x3F, 0x3F], +]; + +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_B: [&[u8]; 2] = [ + &[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0x78], + &[0x3F, 0x3F, 0x31, 0x31, 0x3F, 0x1E], +]; + // TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized const SYM_C: [&[u8]; 2] = [ &[0xF8, 0xFC, 0x0C, 0x1C, 0x18], @@ -145,18 +160,57 @@ const SYM_E: [&[u8]; 2] = [ &[0x3F, 0x3F, 0x31, 0x31, 0x30, 0x30], ]; +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_G: [&[u8]; 2] = [ + &[0xF8, 0xFC, 0x0C, 0x0C, 0x3C, 0x38], + &[0x1F, 0x3F, 0x30, 0x33, 0x3F, 0x1F], +]; + // TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized const SYM_H: [&[u8]; 2] = [ &[0xFC, 0xFC, 0x80, 0x80, 0xFC, 0xFC], &[0x3F, 0x3F, 0x01, 0x01, 0x3F, 0x3F], ]; +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_I: [&[u8]; 2] = [&[0x0C, 0xFC, 0xFC, 0x0C], &[0x30, 0x3F, 0x3F, 0x30]]; + +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_K: [&[u8]; 2] = [ + &[0xFC, 0xFC, 0xC0, 0xF0, 0x7C, 0x1C], + &[0x3F, 0x3F, 0x03, 0x0F, 0x3E, 0x38], +]; + +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_L: [&[u8]; 2] = [ + &[0xFC, 0xFC, 0x00, 0x00, 0x00], + &[0x3F, 0x3F, 0x30, 0x30, 0x30], +]; + +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_N: [&[u8]; 2] = [ + &[0xFC, 0xFC, 0xF0, 0xC0, 0x00, 0xFC, 0xFC], + &[0x3F, 0x3F, 0x00, 0x03, 0x0F, 0x3F, 0x3F], +]; + +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_O: [&[u8]; 2] = [ + &[0xF8, 0xFC, 0x0C, 0x0C, 0xFC, 0xF8], + &[0x1F, 0x3F, 0x30, 0x30, 0x3F, 0x1F], +]; + // TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized const SYM_P: [&[u8]; 2] = [ &[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0xF8], &[0x3F, 0x3F, 0x01, 0x01, 0x01, 0x00], ]; +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +const SYM_R: [&[u8]; 2] = [ + &[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0xF8], + &[0x3F, 0x3F, 0x01, 0x03, 0x3F, 0x3E], +]; + // TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized const SYM_S: [&[u8]; 2] = [ &[0xF8, 0xFC, 0x8C, 0x8C, 0x9C, 0x18], @@ -193,31 +247,31 @@ const SYMBOL_TABLE: [&[&[u8]; 2]; 48] = [ &SYM_7, // '7' &SYM_8, // '8' &SYM_9, // '9' - &SYM_INVALID, // ':' + &SYM_COLON, // ':' &SYM_INVALID, // ';' &SYM_INVALID, // '<' &SYM_INVALID, // '=' &SYM_INVALID, // '>' &SYM_INVALID, // '?' &SYM_INVALID, // '@' - &SYM_INVALID, // 'A' - &SYM_INVALID, // 'B' + &SYM_A, // 'A' + &SYM_B, // 'B' &SYM_C, // 'C' &SYM_INVALID, // 'D' &SYM_E, // 'E' &SYM_INVALID, // 'F' - &SYM_INVALID, // 'G' + &SYM_G, // 'G' &SYM_H, // 'H' - &SYM_INVALID, // 'I' + &SYM_I, // 'I' &SYM_INVALID, // 'J' - &SYM_INVALID, // 'K' - &SYM_INVALID, // 'L' + &SYM_K, // 'K' + &SYM_L, // 'L' &SYM_INVALID, // 'M' - &SYM_INVALID, // 'N' - &SYM_INVALID, // 'O' + &SYM_N, // 'N' + &SYM_O, // 'O' &SYM_P, // 'P' &SYM_INVALID, // 'Q' - &SYM_INVALID, // 'R' + &SYM_R, // 'R' &SYM_S, // 'S' &SYM_T, // 'T' &SYM_U, // 'U' @@ -297,6 +351,33 @@ impl Lcd { } } + fn fill(&mut self, segment: u8, page: u8, width: u8, data: u8) { + assert!(segment + width <= 102); + let mut delay = Delay::::new(); + + self.move_cursor(segment, page); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + self.cd.set_high(); + + for _ in 0..width { + block!(self.spi.send(data)).unwrap(); + } + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + self.cd.set_low(); + } + + fn fill_area(&mut self, segment: u8, page: u8, width: u8, height: u8, data: u8) { + assert!(page + height <= 8); + + for i in 0..height { + self.fill(segment, page + i, width, data); + } + } + fn print(&mut self, segment: u8, page: u8, string: &str) { let mut delay = Delay::::new(); @@ -360,6 +441,9 @@ impl Lcd { Screens::Home(home) => { home.draw(self); } + Screens::Setup(setup) => { + setup.draw(self); + } } }); } @@ -368,6 +452,8 @@ impl Lcd { enum Input { Next, Previous, + Select, + Back, } pub trait Draw { @@ -438,18 +524,26 @@ impl HomeScreen { HomeSelection::Ch1 => match input { Input::Next => HomeSelection::Ch2, Input::Previous => HomeSelection::Setup, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => HomeSelection::Ch1, }, HomeSelection::Ch2 => match input { Input::Next => HomeSelection::Ch3, Input::Previous => HomeSelection::Ch1, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => HomeSelection::Ch2, }, HomeSelection::Ch3 => match input { Input::Next => HomeSelection::Setup, Input::Previous => HomeSelection::Ch2, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => HomeSelection::Ch3, }, HomeSelection::Setup => match input { Input::Next => HomeSelection::Ch1, Input::Previous => HomeSelection::Ch3, + Input::Select => return Screens::Setup(SetupScreen::new()), + Input::Back => HomeSelection::Setup, }, }, }) @@ -487,9 +581,84 @@ impl Draw for HomeScreen { } } +enum SetupSelection { + Contrast, + Backlight, + Back, +} + +struct SetupScreen { + active: SetupSelection, +} + +impl SetupScreen { + fn new() -> Self { + Self { + active: SetupSelection::Contrast, + } + } + + fn input(&self, input: &Input) -> Screens { + Screens::Setup(Self { + active: match self.active { + SetupSelection::Contrast => match input { + Input::Next => SetupSelection::Backlight, + Input::Previous => SetupSelection::Back, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => return Screens::Home(HomeScreen::new()), + }, + SetupSelection::Backlight => match input { + Input::Next => SetupSelection::Back, + Input::Previous => SetupSelection::Contrast, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => return Screens::Home(HomeScreen::new()), + }, + SetupSelection::Back => match input { + Input::Next => SetupSelection::Contrast, + Input::Previous => SetupSelection::Backlight, + Input::Select => return Screens::Home(HomeScreen::new()), + Input::Back => return Screens::Home(HomeScreen::new()), + }, + }, + }) + } +} + +impl Draw for SetupScreen { + fn draw(&self, lcd: &mut Lcd) { + match &self.active { + SetupSelection::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(0, 4, "BACKLIGHT:"); + lcd.print(36, 6, "BACK"); + } + SetupSelection::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_inverted(0, 4, "BACKLIGHT:"); + lcd.print(36, 6, "BACK"); + } + SetupSelection::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(0, 4, "BACKLIGHT:"); + lcd.print_inverted(36, 6, "BACK"); + } + } + } +} + enum Screens { Splash(SplashScreen), Home(HomeScreen), + Setup(SetupScreen), } impl Screens { @@ -497,11 +666,14 @@ impl Screens { match self { Screens::Splash(_) => {} Screens::Home(home) => *self = home.input(input), + Screens::Setup(setup) => *self = setup.input(input), } } } static UPDATE_ENCODER: AtomicBool = AtomicBool::new(false); +static UPDATE_BUTTON: AtomicBool = AtomicBool::new(false); +static UPDATE_TIMER: AtomicBool = AtomicBool::new(false); #[interrupt(atmega328p)] #[allow(non_snake_case)] @@ -511,6 +683,22 @@ fn PCINT0() { }) } +#[interrupt(atmega328p)] +#[allow(non_snake_case)] +fn PCINT1() { + interrupt::free(|_cs| { + UPDATE_BUTTON.store(true, Ordering::SeqCst); + }) +} + +#[interrupt(atmega328p)] +#[allow(non_snake_case)] +fn TIMER1_COMPA() { + interrupt::free(|_cs| { + UPDATE_TIMER.store(true, Ordering::SeqCst); + }) +} + #[atmega_hal::entry] fn main() -> ! { // Get peripherals, pins, eeprom and delay @@ -568,9 +756,18 @@ fn main() -> ! { let mut encoder = Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input()); encoder.update(); - // Enable interrupts for encoder // TODO: and switch - dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0001)); + // 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(SplashScreen); @@ -613,6 +810,33 @@ fn main() -> ! { }) } + 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;