clock_generator/firmware/rust/src/main.rs
finga fdd1f4636d
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fw-rust: Refactor everything
Remove avr-eeprom dependency for now and introduce events.
2022-03-30 22:59:24 +02:00

196 lines
5.1 KiB
Rust

#![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};
#[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<Pin<port::mode::Input<PullUp>, PB6>, Pin<port::mode::Input<PullUp>, PB7>>,
button: Pin<port::mode::Input<PullUp>, PC0>,
delay: Delay<MHz8>,
}
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::<MHz8>::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);
#[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();
}
}