All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
When pressing the button until the timer1 creates an interrupt in order to create a "back" event, there were some debouncing problems.
207 lines
5.4 KiB
Rust
207 lines
5.4 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};
|
|
|
|
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<MHz8>,
|
|
}
|
|
|
|
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::<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);
|
|
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();
|
|
}
|
|
}
|