#![feature(abi_avr_interrupt)] #![no_std] #![no_main] 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 core::sync::atomic::{AtomicBool, Ordering}; use embedded_hal::{ blocking::delay::DelayMs, spi::{Mode, Phase, Polarity}, }; use panic_halt as _; mod assets; mod eeprom; mod lcd; mod rotary; mod screen; use rotary::{Direction, Rotary}; use screen::{Screen, Screens}; pub type DefaultClock = MHz8; pub type I2c = atmega_hal::i2c::I2c; #[used] #[link_section = ".eeprom"] static BACKLIGHT: u8 = 0; #[used] #[link_section = ".eeprom"] static CONTRAST: u8 = 8; pub enum Input { Next, Previous, Select, Back, } struct ClockGenerator { screen: Screen, tc1: TC1, exint: EXINT, encoder: Rotary, PB6>, Pin, PB7>>, button: Pin, PC0>, delay: Delay, } impl ClockGenerator { fn new() -> Self { let dp = Peripherals::take().unwrap(); let pins = pins!(dp); // 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.pd5.into_output(), pins.pb0.into_output(), pins.pb1.into_output(), I2c::new( dp.TWI, pins.pc4.into_pull_up_input(), pins.pc5.into_pull_up_input(), 50000, ), ), 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); self.delay.delay_ms(3_u8); } } 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); #[interrupt(atmega328p)] #[allow(non_snake_case)] fn PCINT0() { interrupt::free(|_cs| { UPDATE_ENCODER.store(true, Ordering::SeqCst); }) } #[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() -> ! { let mut cg = ClockGenerator::new(); cg.init(); loop { cg.update(); } }