fw-rust: Add setup screen and handle button input
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

To create a setup screen more symbols are created and added to the
symbol table. Helper functions are added to draw homogenous areas onto
the display. Add two new fields `Click` and `Back` to the `Input`
enum. Adapt functions already handling input to those new kinds of
input. Add the setup screen construct. Add global atomics to handle
the button and the timer for handling the back input when the button
is pressed until the timer one overflows. Button press and the timer
are handled via interrupts.
This commit is contained in:
finga 2022-03-16 22:17:23 +01:00
parent 0003717408
commit 91e120b258

View file

@ -133,6 +133,21 @@ const SYM_9: [&[u8]; 2] = [
&[0x1C, 0x3D, 0x31, 0x3F, 0x1F],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_COLON: [&[u8]; 2] = [&[0x30, 0x30], &[0x0C, 0x0C]];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_A: [&[u8]; 2] = [
&[0xC0, 0xF0, 0x3C, 0x3C, 0xF0, 0xC0],
&[0x3F, 0x3F, 0x06, 0x06, 0x3F, 0x3F],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_B: [&[u8]; 2] = [
&[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0x78],
&[0x3F, 0x3F, 0x31, 0x31, 0x3F, 0x1E],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_C: [&[u8]; 2] = [
&[0xF8, 0xFC, 0x0C, 0x1C, 0x18],
@ -145,18 +160,57 @@ const SYM_E: [&[u8]; 2] = [
&[0x3F, 0x3F, 0x31, 0x31, 0x30, 0x30],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_G: [&[u8]; 2] = [
&[0xF8, 0xFC, 0x0C, 0x0C, 0x3C, 0x38],
&[0x1F, 0x3F, 0x30, 0x33, 0x3F, 0x1F],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_H: [&[u8]; 2] = [
&[0xFC, 0xFC, 0x80, 0x80, 0xFC, 0xFC],
&[0x3F, 0x3F, 0x01, 0x01, 0x3F, 0x3F],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_I: [&[u8]; 2] = [&[0x0C, 0xFC, 0xFC, 0x0C], &[0x30, 0x3F, 0x3F, 0x30]];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_K: [&[u8]; 2] = [
&[0xFC, 0xFC, 0xC0, 0xF0, 0x7C, 0x1C],
&[0x3F, 0x3F, 0x03, 0x0F, 0x3E, 0x38],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_L: [&[u8]; 2] = [
&[0xFC, 0xFC, 0x00, 0x00, 0x00],
&[0x3F, 0x3F, 0x30, 0x30, 0x30],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_N: [&[u8]; 2] = [
&[0xFC, 0xFC, 0xF0, 0xC0, 0x00, 0xFC, 0xFC],
&[0x3F, 0x3F, 0x00, 0x03, 0x0F, 0x3F, 0x3F],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_O: [&[u8]; 2] = [
&[0xF8, 0xFC, 0x0C, 0x0C, 0xFC, 0xF8],
&[0x1F, 0x3F, 0x30, 0x30, 0x3F, 0x1F],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_P: [&[u8]; 2] = [
&[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0xF8],
&[0x3F, 0x3F, 0x01, 0x01, 0x01, 0x00],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_R: [&[u8]; 2] = [
&[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0xF8],
&[0x3F, 0x3F, 0x01, 0x03, 0x3F, 0x3E],
];
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
const SYM_S: [&[u8]; 2] = [
&[0xF8, 0xFC, 0x8C, 0x8C, 0x9C, 0x18],
@ -193,31 +247,31 @@ const SYMBOL_TABLE: [&[&[u8]; 2]; 48] = [
&SYM_7, // '7'
&SYM_8, // '8'
&SYM_9, // '9'
&SYM_INVALID, // ':'
&SYM_COLON, // ':'
&SYM_INVALID, // ';'
&SYM_INVALID, // '<'
&SYM_INVALID, // '='
&SYM_INVALID, // '>'
&SYM_INVALID, // '?'
&SYM_INVALID, // '@'
&SYM_INVALID, // 'A'
&SYM_INVALID, // 'B'
&SYM_A, // 'A'
&SYM_B, // 'B'
&SYM_C, // 'C'
&SYM_INVALID, // 'D'
&SYM_E, // 'E'
&SYM_INVALID, // 'F'
&SYM_INVALID, // 'G'
&SYM_G, // 'G'
&SYM_H, // 'H'
&SYM_INVALID, // 'I'
&SYM_I, // 'I'
&SYM_INVALID, // 'J'
&SYM_INVALID, // 'K'
&SYM_INVALID, // 'L'
&SYM_K, // 'K'
&SYM_L, // 'L'
&SYM_INVALID, // 'M'
&SYM_INVALID, // 'N'
&SYM_INVALID, // 'O'
&SYM_N, // 'N'
&SYM_O, // 'O'
&SYM_P, // 'P'
&SYM_INVALID, // 'Q'
&SYM_INVALID, // 'R'
&SYM_R, // 'R'
&SYM_S, // 'S'
&SYM_T, // 'T'
&SYM_U, // 'U'
@ -297,6 +351,33 @@ impl Lcd {
}
}
fn fill(&mut self, segment: u8, page: u8, width: u8, data: u8) {
assert!(segment + width <= 102);
let mut delay = Delay::<MHz8>::new();
self.move_cursor(segment, page);
// TODO: This delay fixes issues, try find a better solution
delay.delay_ms(1_u8);
self.cd.set_high();
for _ in 0..width {
block!(self.spi.send(data)).unwrap();
}
// TODO: This delay fixes issues, try find a better solution
delay.delay_ms(1_u8);
self.cd.set_low();
}
fn fill_area(&mut self, segment: u8, page: u8, width: u8, height: u8, data: u8) {
assert!(page + height <= 8);
for i in 0..height {
self.fill(segment, page + i, width, data);
}
}
fn print(&mut self, segment: u8, page: u8, string: &str) {
let mut delay = Delay::<MHz8>::new();
@ -360,6 +441,9 @@ impl Lcd {
Screens::Home(home) => {
home.draw(self);
}
Screens::Setup(setup) => {
setup.draw(self);
}
}
});
}
@ -368,6 +452,8 @@ impl Lcd {
enum Input {
Next,
Previous,
Select,
Back,
}
pub trait Draw {
@ -438,18 +524,26 @@ impl HomeScreen {
HomeSelection::Ch1 => match input {
Input::Next => HomeSelection::Ch2,
Input::Previous => HomeSelection::Setup,
Input::Select => return Screens::Splash(SplashScreen),
Input::Back => HomeSelection::Ch1,
},
HomeSelection::Ch2 => match input {
Input::Next => HomeSelection::Ch3,
Input::Previous => HomeSelection::Ch1,
Input::Select => return Screens::Splash(SplashScreen),
Input::Back => HomeSelection::Ch2,
},
HomeSelection::Ch3 => match input {
Input::Next => HomeSelection::Setup,
Input::Previous => HomeSelection::Ch2,
Input::Select => return Screens::Splash(SplashScreen),
Input::Back => HomeSelection::Ch3,
},
HomeSelection::Setup => match input {
Input::Next => HomeSelection::Ch1,
Input::Previous => HomeSelection::Ch3,
Input::Select => return Screens::Setup(SetupScreen::new()),
Input::Back => HomeSelection::Setup,
},
},
})
@ -487,9 +581,84 @@ impl Draw for HomeScreen {
}
}
enum SetupSelection {
Contrast,
Backlight,
Back,
}
struct SetupScreen {
active: SetupSelection,
}
impl SetupScreen {
fn new() -> Self {
Self {
active: SetupSelection::Contrast,
}
}
fn input(&self, input: &Input) -> Screens {
Screens::Setup(Self {
active: match self.active {
SetupSelection::Contrast => match input {
Input::Next => SetupSelection::Backlight,
Input::Previous => SetupSelection::Back,
Input::Select => return Screens::Splash(SplashScreen),
Input::Back => return Screens::Home(HomeScreen::new()),
},
SetupSelection::Backlight => match input {
Input::Next => SetupSelection::Back,
Input::Previous => SetupSelection::Contrast,
Input::Select => return Screens::Splash(SplashScreen),
Input::Back => return Screens::Home(HomeScreen::new()),
},
SetupSelection::Back => match input {
Input::Next => SetupSelection::Contrast,
Input::Previous => SetupSelection::Backlight,
Input::Select => return Screens::Home(HomeScreen::new()),
Input::Back => return Screens::Home(HomeScreen::new()),
},
},
})
}
}
impl Draw for SetupScreen {
fn draw(&self, lcd: &mut Lcd) {
match &self.active {
SetupSelection::Contrast => {
lcd.fill_area(0, 0, 33, 2, 0xFF);
lcd.print_inverted(33, 0, "SETUP");
lcd.fill_area(69, 0, 33, 2, 0xFF);
lcd.print_inverted(0, 2, "CONTRAST:");
lcd.print(0, 4, "BACKLIGHT:");
lcd.print(36, 6, "BACK");
}
SetupSelection::Backlight => {
lcd.fill_area(0, 0, 33, 2, 0xFF);
lcd.print_inverted(33, 0, "SETUP");
lcd.fill_area(69, 0, 33, 2, 0xFF);
lcd.print(0, 2, "CONTRAST:");
lcd.print_inverted(0, 4, "BACKLIGHT:");
lcd.print(36, 6, "BACK");
}
SetupSelection::Back => {
lcd.fill_area(0, 0, 33, 2, 0xFF);
lcd.print_inverted(33, 0, "SETUP");
lcd.fill_area(69, 0, 33, 2, 0xFF);
lcd.print(0, 2, "CONTRAST:");
lcd.print(0, 4, "BACKLIGHT:");
lcd.print_inverted(36, 6, "BACK");
}
}
}
}
enum Screens {
Splash(SplashScreen),
Home(HomeScreen),
Setup(SetupScreen),
}
impl Screens {
@ -497,11 +666,14 @@ impl Screens {
match self {
Screens::Splash(_) => {}
Screens::Home(home) => *self = home.input(input),
Screens::Setup(setup) => *self = setup.input(input),
}
}
}
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)]
@ -511,6 +683,22 @@ fn PCINT0() {
})
}
#[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() -> ! {
// Get peripherals, pins, eeprom and delay
@ -568,9 +756,18 @@ fn main() -> ! {
let mut encoder = Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input());
encoder.update();
// Enable interrupts for encoder // TODO: and switch
dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0001));
// Init button
let button = pins.pc0.into_pull_up_input();
// Init Timer/Counter1
let tc1 = dp.TC1;
tc1.ocr1a.write(|w| unsafe { w.bits(65535) });
// Enable interrupts for encoder, button and timer
dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0011));
dp.EXINT.pcmsk0.write(|w| w.pcint().bits(0b1100_0000));
dp.EXINT.pcmsk1.write(|w| w.pcint().bits(0b0000_0001));
tc1.timsk1.write(|w| w.ocie1a().set_bit());
// Create screen
let mut screen = Screens::Splash(SplashScreen);
@ -613,6 +810,33 @@ fn main() -> ! {
})
}
if UPDATE_TIMER.load(Ordering::SeqCst) {
tc1.tccr1b.write(|w| w.cs1().no_clock());
screen.input(&Input::Back);
update_screen = true;
UPDATE_TIMER.store(false, Ordering::SeqCst);
}
if UPDATE_BUTTON.load(Ordering::SeqCst) {
delay.delay_ms(2_u8);
if button.is_low() {
// Press
if tc1.tccr1b.read().cs1().is_no_clock() {
tc1.tcnt1.write(|w| unsafe { w.bits(0) });
tc1.tccr1b.write(|w| w.cs1().prescale_64());
}
} else {
// Release
if tc1.tccr1b.read().cs1().is_prescale_64() {
tc1.tccr1b.write(|w| w.cs1().no_clock());
screen.input(&Input::Select);
update_screen = true;
}
}
UPDATE_BUTTON.store(false, Ordering::SeqCst);
}
if update_screen {
lcd.draw(&screen);
update_screen = false;