clock_generator/firmware/src/main.rs
finga 698965fcf7
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
firmware: Remove the c attempt of the firmware
2023-06-07 22:17:01 +02:00

208 lines
5.5 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 lcd;
mod rotary;
mod screen;
use rotary::{Direction, Rotary};
use screen::{Screen, Screens};
pub type DefaultClock = MHz8;
pub type I2c = atmega_hal::i2c::I2c<DefaultClock>;
#[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<DefaultClock>,
}
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,
dp.EEPROM,
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::<DefaultClock>::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| 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| 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();
}
}