use timer to update display and read keyboard

This commit is contained in:
2025-07-18 20:31:23 +01:00
parent 1f961964f0
commit 56f2738ea2
4 changed files with 190 additions and 97 deletions

View File

@@ -218,7 +218,7 @@ impl Default for Segment {
} }
} }
#[derive(Default)] #[derive(Default, Clone, Copy)]
pub struct DispalyState([Segment; DISPLAY_SEGMENTS]); pub struct DispalyState([Segment; DISPLAY_SEGMENTS]);
impl Index<usize> for DispalyState { impl Index<usize> for DispalyState {
@@ -236,6 +236,10 @@ impl IndexMut<usize> for DispalyState {
} }
impl DispalyState { impl DispalyState {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter_segments(&self) -> impl Iterator<Item = &Segment> { pub fn iter_segments(&self) -> impl Iterator<Item = &Segment> {
self.0.iter() self.0.iter()
} }

52
src/io.rs Normal file
View File

@@ -0,0 +1,52 @@
use core::ops::{Index, IndexMut};
use arduino_hal::{
hal::port::{PB3, PB4, PC1, PC2, PC3, PD2, PD3, PD4, PD7},
port::{mode::Output, Pin},
};
pub struct IOPins([Pin<Output>; 9]);
impl IOPins {
pub fn new(
kd_d1_1c: Pin<Output, PD2>,
kd_d2_5div: Pin<Output, PD3>,
kd_d3_6mul: Pin<Output, PD4>,
kd_d4_7up: Pin<Output, PC3>,
kd_d5_8e: Pin<Output, PC2>,
kd_d6_90: Pin<Output, PD7>,
kd_d7_2down: Pin<Output, PC1>,
kd_d8_3plus: Pin<Output, PB3>,
kd_d9_4min: Pin<Output, PB4>,
) -> IOPins {
IOPins([
kd_d1_1c.downgrade(),
kd_d2_5div.downgrade(),
kd_d3_6mul.downgrade(),
kd_d4_7up.downgrade(),
kd_d5_8e.downgrade(),
kd_d6_90.downgrade(),
kd_d7_2down.downgrade(),
kd_d8_3plus.downgrade(),
kd_d9_4min.downgrade(),
])
}
pub fn len(&self) -> usize {
self.0.len()
}
}
impl Index<usize> for IOPins {
type Output = Pin<Output>;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl IndexMut<usize> for IOPins {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.0[index]
}
}

View File

@@ -1,4 +1,7 @@
use arduino_hal::{adc::AdcChannel, hal::Atmega, pac::ADC, Adc}; use arduino_hal::{
adc::channel::{ADC6, ADC7},
Adc,
};
use ufmt::derive::uDebug; use ufmt::derive::uDebug;
use crate::{DEBOUNCE_DEPTH, KEYBOARD_ADC_THRESHOLD}; use crate::{DEBOUNCE_DEPTH, KEYBOARD_ADC_THRESHOLD};
@@ -49,13 +52,13 @@ pub struct KeyReadout {
ko: bool, ko: bool,
} }
pub struct Keyboard<KN, KO> { pub struct Keyboard {
kn: KN, kn: ADC7,
ko: KO, ko: ADC6,
} }
impl<KN: AdcChannel<Atmega, ADC>, KO: AdcChannel<Atmega, ADC>> Keyboard<KN, KO> { impl Keyboard {
pub fn new(kn: KN, ko: KO) -> Keyboard<KN, KO> { pub fn new(kn: ADC7, ko: ADC6) -> Keyboard {
Keyboard { kn, ko } Keyboard { kn, ko }
} }

View File

@@ -3,27 +3,30 @@
#![no_main] #![no_main]
mod display; mod display;
mod io;
mod keyboard; mod keyboard;
mod panic; mod panic;
mod timer; mod timer;
use avr_device::interrupt::Mutex; use avr_device::interrupt::{CriticalSection, Mutex};
use calc_math::{ use calc_math::{
calc::{StackCalc, StackCalcError}, calc::{StackCalc, StackCalcError},
Decimal, Decimal,
}; };
use core::{cell::RefCell, ops::DerefMut}; use core::cell::RefCell;
use display::{DispalyState, SegmentPins, Show}; use display::{DispalyState, SegmentPins, Show};
use keyboard::{Debounce, KeyPress, KeyReadout, Keyboard}; use keyboard::{Debounce, KeyPress, KeyReadout, Keyboard};
use arduino_hal::{ use arduino_hal::{
adc::channel::{ADC6, ADC7}, adc::channel::{ADC6, ADC7},
hal::port::{PB3, PB4, PB5, PC1, PC2, PC3, PD2, PD3, PD4, PD7}, hal::port::PB5,
port::{mode::Output, Pin}, port::{mode::Output, Pin},
Adc, Adc,
}; };
use ufmt::derive::uDebug; use ufmt::derive::uDebug;
use crate::io::IOPins;
// NOTE: 115200 @ 16MHz is 3.5% off, try 9600 or 1M if it causes issues (https://wormfood.net/avrbaudcalc.php) // NOTE: 115200 @ 16MHz is 3.5% off, try 9600 or 1M if it causes issues (https://wormfood.net/avrbaudcalc.php)
const SERIAL_BAUD: u32 = 115200; const SERIAL_BAUD: u32 = 115200;
// Analog threshold level of key press on ADC read from KN or KO // Analog threshold level of key press on ADC read from KN or KO
@@ -41,8 +44,8 @@ pub const DISPLAY_SEGMENTS_EXP: usize = 2;
pub const DISPLAY_SEGMENT_EXP_MINUS: usize = 6; pub const DISPLAY_SEGMENT_EXP_MINUS: usize = 6;
// Timing // Timing
pub const IO_SEG_REFRESH_FREQ: u32 = 100; // Segment timer freq; from 62 to 15779 for 8bit counter pub const IO_SEG_REFRESH_FREQ: u32 = 1000; // Segment timer freq; from 62 to 15779 for 8bit counter
pub const IO_SEG_ON_DIV: u32 = 10; // How long to hold segment LEDs on as part of time for segment timer pub const IO_SEG_ON_DIV: u32 = 4; // How long to hold segment LEDs on as part of time for segment timer
pub const STACK_DEPTH: usize = 7; pub const STACK_DEPTH: usize = 7;
@@ -56,82 +59,117 @@ type Calc = StackCalc<f32, STACK_DEPTH, 5, u8>;
// * On another timer interrupt expire disable display (LEDs off) and handle keyboard input. // * On another timer interrupt expire disable display (LEDs off) and handle keyboard input.
static LED: Mutex<RefCell<Option<Pin<Output, PB5>>>> = Mutex::new(RefCell::new(None)); static LED: Mutex<RefCell<Option<Pin<Output, PB5>>>> = Mutex::new(RefCell::new(None));
static IO_LOOP: Mutex<RefCell<Option<IOLoop>>> = Mutex::new(RefCell::new(None));
static KEY_PRESS: Mutex<RefCell<Option<KeyPress>>> = Mutex::new(RefCell::new(None));
static ADC: Mutex<RefCell<Option<Adc>>> = Mutex::new(RefCell::new(None));
fn try_access<'cs, 'v: 'cs, T, O>(
v: &'v Mutex<RefCell<Option<T>>>,
cs: CriticalSection<'cs>,
f: impl for<'t> FnOnce(&'t mut T) -> O,
) -> Option<O> {
if let Some(mut v) = v.borrow(cs).try_borrow_mut().ok() {
if let Some(v) = v.as_mut() {
return Some(f(v));
}
}
None
}
#[avr_device::interrupt(atmega328p)] #[avr_device::interrupt(atmega328p)]
unsafe fn TIMER0_COMPA() { unsafe fn TIMER0_COMPA() {
//TODO: Handle timer interrupt to display next segment avr_device::interrupt::free(|cs| {
// file:///home/hxd/projects/avr-calc/target/avr-none/doc/avr_device/interrupt/index.html try_access(&LED, cs, |led| led.set_high()).expect("LED not available (COMPA)");
// ... try_access(&IO_LOOP, cs, |io_loop| {
avr_device::interrupt::free( io_loop.display_on();
|cs| match LED.borrow(cs).borrow_mut().deref_mut().as_mut() { })
// Some(led) => led.toggle(), });
Some(led) => led.set_high(),
None => panic!(),
},
);
} }
#[avr_device::interrupt(atmega328p)] #[avr_device::interrupt(atmega328p)]
unsafe fn TIMER0_COMPB() { unsafe fn TIMER0_COMPB() {
//TODO: Disable display segment and handle keyboard input for current segment avr_device::interrupt::free(|cs| {
avr_device::interrupt::free( try_access(&LED, cs, |led| led.set_low()).expect("LED not available (COMPB)");
|cs| match LED.borrow(cs).borrow_mut().deref_mut().as_mut() { try_access(&IO_LOOP, cs, |io_loop| {
Some(led) => led.set_low(), io_loop.display_off();
None => panic!(), try_access(&ADC, cs, |adc| {
}, io_loop.read_key(adc);
); });
if let Some(key) = io_loop.advance() {
if let Some(mut key_press) = KEY_PRESS.borrow(cs).try_borrow_mut().ok() {
key_press.replace(key);
}
}
});
});
} }
struct IOSelect<'p> { pub struct IOLoop {
display_no: usize, index: usize,
pin: &'p mut Pin<Output>, io_pins: IOPins,
segment_pins: SegmentPins,
dispaly: DispalyState,
keyboard: Keyboard,
readount: Option<KeyReadout>,
debounce: Debounce,
} }
impl IOSelect<'_> { impl IOLoop {
fn display_no(&self) -> usize { pub fn new(io_pins: IOPins, segment_pins: SegmentPins, keyboard: Keyboard) -> IOLoop {
self.display_no IOLoop {
index: 0,
io_pins,
segment_pins,
dispaly: Default::default(),
keyboard,
readount: None,
debounce: Default::default(),
}
} }
fn set_on(&mut self) { pub fn update_display(&mut self, display: &DispalyState) {
self.pin.set_high() self.dispaly = *display;
}
fn set_off(&mut self) {
self.pin.set_low()
}
}
struct IOPins([Pin<Output>; 9]);
impl IOPins {
fn new(
kd_d1_1c: Pin<Output, PD2>,
kd_d2_5div: Pin<Output, PD3>,
kd_d3_6mul: Pin<Output, PD4>,
kd_d4_7up: Pin<Output, PC3>,
kd_d5_8e: Pin<Output, PC2>,
kd_d6_90: Pin<Output, PD7>,
kd_d7_2down: Pin<Output, PC1>,
kd_d8_3plus: Pin<Output, PB3>,
kd_d9_4min: Pin<Output, PB4>,
) -> IOPins {
IOPins([
kd_d1_1c.downgrade(),
kd_d2_5div.downgrade(),
kd_d3_6mul.downgrade(),
kd_d4_7up.downgrade(),
kd_d5_8e.downgrade(),
kd_d6_90.downgrade(),
kd_d7_2down.downgrade(),
kd_d8_3plus.downgrade(),
kd_d9_4min.downgrade(),
])
} }
fn iter_mut(&mut self) -> impl Iterator<Item = IOSelect> { fn select_on(&mut self) {
self.0 self.io_pins[self.index].set_high();
.iter_mut() }
.enumerate()
.map(|(display_no, pin)| IOSelect { display_no, pin }) fn select_off(&mut self) {
self.io_pins[self.index].set_low();
}
pub fn advance(&mut self) -> Option<KeyPress> {
self.index += 1;
if self.index == self.io_pins.len() || self.index == self.dispaly.len() {
// Start from first segment
self.index = 0;
// Full keyboard scan complete, debounce and return result
self.debounce.input(self.readount.take())
} else {
None
}
}
pub fn display_on(&mut self) {
self.select_off();
let segment = self.dispaly[self.index];
segment.apply(&mut self.segment_pins);
self.select_on();
}
pub fn display_off(&mut self) {
self.select_off();
self.segment_pins.set_off();
}
pub fn read_key(&mut self, adc: &mut Adc) {
self.select_on();
if let key_readout @ Some(_) = self.keyboard.read(adc, self.index) {
self.readount = key_readout;
}
self.select_off();
} }
} }
@@ -447,7 +485,7 @@ fn main() -> ! {
ufmt::uwriteln!(&mut serial, "Hello from Arduino!").ok(); ufmt::uwriteln!(&mut serial, "Hello from Arduino!").ok();
let mut io = IOPins::new( let io_pins = IOPins::new(
pins.d2.into_output(), pins.d2.into_output(),
pins.d3.into_output(), pins.d3.into_output(),
pins.d4.into_output(), pins.d4.into_output(),
@@ -458,8 +496,7 @@ fn main() -> ! {
pins.d11.into_output(), pins.d11.into_output(),
pins.d12.into_output(), pins.d12.into_output(),
); );
let segment_pins = SegmentPins::new(
let mut seg = SegmentPins::new(
pins.d5.into_output(), pins.d5.into_output(),
pins.d8.into_output(), pins.d8.into_output(),
pins.d10.into_output(), pins.d10.into_output(),
@@ -469,10 +506,11 @@ fn main() -> ! {
pins.a0.into_output(), pins.a0.into_output(),
pins.a4.into_output(), pins.a4.into_output(),
); );
let mut adc = Adc::new(dp.ADC, Default::default());
let keyboard = Keyboard::new(ADC7, ADC6); let keyboard = Keyboard::new(ADC7, ADC6);
let mut debounce = Debounce::default(); let io_loop = IOLoop::new(io_pins, segment_pins, keyboard);
let led = pins.d13.into_output();
let adc = Adc::new(dp.ADC, Default::default());
let mut number_input = NumberInput::default(); let mut number_input = NumberInput::default();
let mut display = DispalyState::default(); let mut display = DispalyState::default();
@@ -485,7 +523,9 @@ fn main() -> ! {
}; };
avr_device::interrupt::free(|cs| { avr_device::interrupt::free(|cs| {
LED.borrow(cs).replace(Some(pins.d13.into_output())); LED.borrow(cs).replace(Some(led));
IO_LOOP.borrow(cs).replace(Some(io_loop));
ADC.borrow(cs).replace(Some(adc));
}); });
timer::segment_timer_init( timer::segment_timer_init(
dp.TC0, // Timer0 (8bit) dp.TC0, // Timer0 (8bit)
@@ -497,22 +537,10 @@ fn main() -> ! {
} }
loop { loop {
let mut last_key_readout: Option<KeyReadout> = None; let mut key = None;
// Timing: 1ms display, 0.230ms read key; every 12ms/83Hz avr_device::interrupt::free(|cs| {
for (mut io_select, ss) in io.iter_mut().zip(display.iter_segments()) { key = KEY_PRESS.borrow(cs).borrow_mut().take();
ss.apply(&mut seg); });
io_select.set_on();
arduino_hal::delay_ms(1);
io_select.set_off();
seg.set_off();
io_select.set_on();
if let key_readout @ Some(_) = keyboard.read(&mut adc, io_select.display_no()) {
last_key_readout = key_readout;
}
io_select.set_off();
}
let key = debounce.input(last_key_readout);
if let TransientState::Done = state.transient { if let TransientState::Done = state.transient {
if let Some(key) = key { if let Some(key) = key {
@@ -569,6 +597,12 @@ fn main() -> ! {
TransientState::Err { .. } => display.error(), TransientState::Err { .. } => display.error(),
} }
arduino_hal::delay_ms(1); avr_device::interrupt::free(|cs| {
try_access(&IO_LOOP, cs, |io_loop| {
io_loop.update_display(&display);
});
});
arduino_hal::delay_ms(10);
} }
} }