use atmega_hal::{ pac::TC0, port::{mode::Output, Pin, PB0, PB1, PD5}, Spi, }; use si5351::{ClockOutput, Si5351, Si5351Device, PLL}; mod channel; mod home; mod setup; mod splash; use crate::{ assets::{OFF, ON, PLL_A, PLL_B}, eeprom, lcd::Lcd, I2c, Input, BACKLIGHT, }; pub use channel::Channel; pub use home::Home; pub use setup::Setup; pub use splash::Splash; // TODO: Only update changes instead of whole screen pub enum Event { Screen(Screens), Backlight(u8), Contrast(u8), Channel(ClockChannel), None, } #[derive(Clone, Copy)] pub struct ClockChannel { output: ClockOutput, freq: u32, enabled: bool, pll: PLL, } impl ClockChannel { fn print(&self, lcd: &mut Lcd, page: u8) { lcd.print_freq(25, page, self.freq); lcd.print_icon(91, page, if self.enabled { &ON } else { &OFF }); lcd.print_icon( 94, page + 1, match self.pll { PLL::A => &PLL_A, PLL::B => &PLL_B, }, ); } fn update(&self, si5351: &mut Si5351Device) { si5351 .set_frequency(self.pll, self.output, self.freq) .unwrap(); si5351.set_clock_enabled(self.output, self.enabled); si5351.flush_output_enabled().unwrap(); } } pub enum Screens { Splash(Splash), Home(Home), Setup(Setup), Channel(Channel), } impl Screens { pub fn input(&mut self, input: &Input, channels: [ClockChannel; 3]) -> Event { match self { Screens::Splash(_) => Event::None, Screens::Home(home) => home.input(input, channels), Screens::Setup(setup) => setup.input(input), Screens::Channel(channel) => channel.input(input), } } } pub struct Screen { lcd: Lcd, tc0: TC0, pwm: Pin, screen: Screens, si5351: Si5351Device, channels: [ClockChannel; 3], } impl Screen { pub fn new( tc0: TC0, spi: Spi, pwm: Pin, cd: Pin, rst: Pin, i2c: I2c, ) -> Self { Self { lcd: Lcd::new(spi, cd, rst), tc0, pwm, screen: Screens::Splash(Splash), si5351: Si5351Device::new_adafruit_module(i2c), channels: [ ClockChannel { output: ClockOutput::Clk0, freq: 1_000_000, enabled: false, pll: PLL::A, }, ClockChannel { output: ClockOutput::Clk1, freq: 1_000_000, enabled: false, pll: PLL::A, }, ClockChannel { output: ClockOutput::Clk2, freq: 1_000_000, enabled: false, pll: PLL::A, }, ], } } pub fn init(&mut self) { // Init display backlight self.tc0.ocr0a.write(|w| unsafe { w.bits(255) }); self.tc0.tccr0a.write(|w| { w.wgm0().pwm_fast(); w.com0b().match_clear() }); self.set_backlight(nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap()); // Init lcd display self.lcd.init(); self.draw(); // Init Si5351 self.si5351.init_adafruit_module().unwrap(); } fn set_backlight(&mut self, backlight: u8) { match backlight { 0 => { self.tc0.tccr0b.write(|w| w.cs0().no_clock()); self.pwm.set_low(); } 1..=5 => { self.tc0.tccr0b.write(|w| { w.wgm02().set_bit(); w.cs0().prescale_256() }); self.tc0.ocr0b.write(|w| unsafe { w.bits(backlight - 1) }); } _ => { self.tc0.tccr0b.write(|w| { w.wgm02().set_bit(); w.cs0().prescale_64() }); self.tc0.ocr0b.write(|w| unsafe { w.bits(backlight - 6) }); } } } 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, self.channels), Screens::Setup(setup) => setup.draw(&mut self.lcd), Screens::Channel(channel) => channel.draw(&mut self.lcd), } } pub fn input(&mut self, input: &Input) { match self.screen.input(input, self.channels) { Event::Screen(screen) => self.screen = screen, Event::Backlight(backlight) => self.set_backlight(backlight), Event::Contrast(contrast) => self.lcd.set_contrast(contrast), Event::Channel(channel) => { match channel.output { ClockOutput::Clk0 => self.channels[0] = channel, ClockOutput::Clk1 => self.channels[1] = channel, ClockOutput::Clk2 => self.channels[2] = channel, _ => unimplemented!(), } channel.update(&mut self.si5351); } Event::None => {} } self.draw(); } pub fn change(&mut self, screen: Screens) { self.screen = screen; self.draw(); } }