fw-rust: Add setup screen and handle button input
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
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:
parent
0003717408
commit
91e120b258
1 changed files with 236 additions and 12 deletions
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue