fw-rust: Handle screens differently, use encoder
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
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:
parent
4a33986e8d
commit
2ec8d1aeb9
2 changed files with 219 additions and 102 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
51
firmware/rust/src/rotary.rs
Normal file
51
firmware/rust/src/rotary.rs
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue