clock_generator/firmware/rust/src/main.rs
finga 7d8f5f6870
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fw-rust: Create a print u8 function for setup
Create `print_u8()` helper function to print `u8` primitives to
lcd. Refactor contrast and backlight variables to also keep them in a
global `AtomicU8`.
2022-03-21 12:46:18 +01:00

213 lines
5.6 KiB
Rust

#![feature(abi_avr_interrupt)]
#![no_std]
#![no_main]
use atmega_hal::{
clock::MHz8,
delay::Delay,
pins,
spi::{DataOrder, SerialClockRate, Settings, Spi},
Peripherals,
};
use avr_device::interrupt;
use avr_eeprom::{eeprom, Eeprom};
use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use embedded_hal::{
blocking::delay::DelayMs,
spi::{Mode, Phase, Polarity},
};
use panic_halt as _;
mod assets;
mod lcd;
mod rotary;
mod screen;
use lcd::Lcd;
use rotary::{Direction, Rotary};
use screen::Screens;
eeprom! {
static eeprom EEPROM_CONTRAST: u8 = 8;
static eeprom EEPROM_BACKLIGHT: u8 = 1;
}
pub enum Input {
Next,
Previous,
Select,
Back,
}
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)]
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() -> ! {
// 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::<MHz8>::new();
// 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;
}
}
}