fw-rust: Handle screens differently, use encoder
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

Handle the screens differently and implement rotary encoder support.
This commit is contained in:
finga 2022-03-14 22:51:35 +01:00
parent 4a33986e8d
commit 2ec8d1aeb9
2 changed files with 219 additions and 102 deletions

View file

@ -1,3 +1,4 @@
#![feature(abi_avr_interrupt)]
#![no_std]
#![no_main]
@ -9,9 +10,9 @@ use atmega_hal::{
spi::{DataOrder, SerialClockRate, Settings, Spi},
Peripherals,
};
use avr_device::interrupt::{self, Mutex};
use avr_device::interrupt;
use avr_eeprom::{eeprom, Eeprom};
use core::cell::RefCell;
use core::sync::atomic::{AtomicBool, Ordering};
use embedded_hal::{
blocking::delay::DelayMs,
spi::{FullDuplex, Mode, Phase, Polarity},
@ -19,6 +20,9 @@ use embedded_hal::{
use nb::block;
use panic_halt as _;
mod rotary;
use rotary::{Direction, Rotary};
eeprom! {
static eeprom CONTRAST: u8 = 8;
static eeprom BACKLIGHT: u8 = 1;
@ -345,20 +349,27 @@ impl Lcd {
}
}
fn screen(&mut self) {
self.clear();
fn draw(&mut self, screen: &Screens) {
interrupt::free(|_cs| {
self.clear();
interrupt::free(|cs| match &*SCREEN.borrow(cs).borrow() {
Screens::Splash(splash) => {
splash.draw(self);
match screen {
Screens::Splash(splash) => {
splash.draw(self);
}
Screens::Home(home) => {
home.draw(self);
}
}
Screens::Home(home) => {
home.draw(self);
}
})
});
}
}
enum Input {
Next,
Previous,
}
pub trait Draw {
fn draw(&self, lcd: &mut Lcd);
}
@ -414,6 +425,41 @@ struct HomeScreen {
active: HomeSelection,
}
impl HomeScreen {
fn input(&self, input: &Input) -> Screens {
match input {
Input::Next => match self.active {
HomeSelection::Ch1 => Screens::Home(HomeScreen {
active: HomeSelection::Ch2,
}),
HomeSelection::Ch2 => Screens::Home(HomeScreen {
active: HomeSelection::Ch3,
}),
HomeSelection::Ch3 => Screens::Home(HomeScreen {
active: HomeSelection::Setup,
}),
HomeSelection::Setup => Screens::Home(HomeScreen {
active: HomeSelection::Ch1,
}),
},
Input::Previous => match self.active {
HomeSelection::Ch1 => Screens::Home(HomeScreen {
active: HomeSelection::Setup,
}),
HomeSelection::Ch2 => Screens::Home(HomeScreen {
active: HomeSelection::Ch1,
}),
HomeSelection::Ch3 => Screens::Home(HomeScreen {
active: HomeSelection::Ch2,
}),
HomeSelection::Setup => Screens::Home(HomeScreen {
active: HomeSelection::Ch3,
}),
},
}
}
}
impl Draw for HomeScreen {
fn draw(&self, lcd: &mut Lcd) {
match &self.active {
@ -450,112 +496,132 @@ enum Screens {
Home(HomeScreen),
}
// TODO: Investigate if this should also be volatile
static SCREEN: Mutex<RefCell<Screens>> = Mutex::new(RefCell::new(Screens::Splash(SplashScreen)));
impl Screens {
fn input(&mut self, input: &Input) {
match self {
Screens::Splash(_) => {}
Screens::Home(home) => *self = home.input(input),
}
}
}
static UPDATE_ENCODER: AtomicBool = AtomicBool::new(false);
#[interrupt(atmega328p)]
#[allow(non_snake_case)]
fn PCINT0() {
interrupt::free(|_cs| {
UPDATE_ENCODER.store(true, Ordering::SeqCst);
})
}
#[atmega_hal::entry]
fn main() -> ! {
interrupt::free(|cs| {
// 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 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
let contrast = eeprom.read_value(&CONTRAST);
let backlight = eeprom.read_value(&BACKLIGHT);
// Get contrast and backlight from EEPROM
let contrast = eeprom.read_value(&CONTRAST);
let backlight = eeprom.read_value(&BACKLIGHT);
// 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) });
pins.pd5.into_output();
// 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) });
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 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 {
spi,
cd: pins.pb0.into_output(),
rst: pins.pb1.into_output(),
};
lcd.init(contrast);
// Init LCD
let mut lcd = Lcd {
spi,
cd: pins.pb0.into_output(),
rst: pins.pb1.into_output(),
};
lcd.init(contrast);
// Draw splash screen
lcd.screen();
// Init encoder
let mut encoder = Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input());
encoder.update();
// Show splash screen for a moment
delay.delay_ms(2000_u16);
// Enable interrupts for encoder // TODO: and switch
dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0001));
dp.EXINT.pcmsk0.write(|w| w.pcint().bits(0b1100_0000));
// Set home screen
let screen = SCREEN.borrow(cs);
*screen.borrow_mut() = Screens::Home(HomeScreen {
active: HomeSelection::Ch1,
});
// Create screen
let mut screen = Screens::Splash(SplashScreen);
// Draw screen
lcd.screen();
// Draw splash screen
lcd.draw(&screen);
// Show splash screen for a moment
delay.delay_ms(1000_u16);
// Show splash screen for a moment
delay.delay_ms(2000_u16);
// Demo select CH2
*screen.borrow_mut() = Screens::Home(HomeScreen {
active: HomeSelection::Ch2,
});
// Draw screen
lcd.screen();
// Show splash screen for a moment
delay.delay_ms(1000_u16);
// Demo select CH3
*screen.borrow_mut() = Screens::Home(HomeScreen {
active: HomeSelection::Ch3,
});
// Draw screen
lcd.screen();
// Show splash screen for a moment
delay.delay_ms(1000_u16);
// Demo select SETUP
*screen.borrow_mut() = Screens::Home(HomeScreen {
active: HomeSelection::Setup,
});
// Draw screen
lcd.screen();
// Set home screen
screen = Screens::Home(HomeScreen {
active: HomeSelection::Ch1,
});
// Draw screen
lcd.draw(&screen);
let mut update_screen = false;
// Enable interrupts globally
unsafe { interrupt::enable() };
#[allow(clippy::empty_loop)]
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_screen {
lcd.draw(&screen);
update_screen = false;
}
}
}

View file

@ -0,0 +1,51 @@
use embedded_hal::digital::v2::InputPin;
pub struct Rotary<A, B> {
pin_a: A,
pin_b: B,
state: u8,
}
pub enum Direction {
Clockwise,
CounterClockwise,
None,
}
impl<A, B> Rotary<A, B>
where
A: InputPin,
B: InputPin,
{
pub fn new(pin_a: A, pin_b: B) -> Self {
Self {
pin_a,
pin_b,
state: 0,
}
}
pub fn update(&mut self) -> Direction {
let a = self.pin_a.is_low();
let b = self.pin_b.is_low();
let new_state = match (a, b) {
(Ok(true), Ok(true)) => 0b00,
(Ok(true), Ok(false)) => 0b01,
(Ok(false), Ok(true)) => 0b10,
(Ok(false), Ok(false)) => 0b11,
_ => return Direction::None,
};
let direction = match (self.state, new_state) {
(0b10, 0b11) => Direction::Clockwise,
(0b01, 0b00) => Direction::Clockwise,
(0b01, 0b11) => Direction::CounterClockwise,
(0b10, 0b00) => Direction::CounterClockwise,
_ => Direction::None,
};
self.state = new_state;
direction
}
}