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]);
impl Index<usize> for DispalyState {
@@ -236,6 +236,10 @@ impl IndexMut<usize> for DispalyState {
}
impl DispalyState {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter_segments(&self) -> impl Iterator<Item = &Segment> {
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 crate::{DEBOUNCE_DEPTH, KEYBOARD_ADC_THRESHOLD};
@@ -49,13 +52,13 @@ pub struct KeyReadout {
ko: bool,
}
pub struct Keyboard<KN, KO> {
kn: KN,
ko: KO,
pub struct Keyboard {
kn: ADC7,
ko: ADC6,
}
impl<KN: AdcChannel<Atmega, ADC>, KO: AdcChannel<Atmega, ADC>> Keyboard<KN, KO> {
pub fn new(kn: KN, ko: KO) -> Keyboard<KN, KO> {
impl Keyboard {
pub fn new(kn: ADC7, ko: ADC6) -> Keyboard {
Keyboard { kn, ko }
}

View File

@@ -3,27 +3,30 @@
#![no_main]
mod display;
mod io;
mod keyboard;
mod panic;
mod timer;
use avr_device::interrupt::Mutex;
use avr_device::interrupt::{CriticalSection, Mutex};
use calc_math::{
calc::{StackCalc, StackCalcError},
Decimal,
};
use core::{cell::RefCell, ops::DerefMut};
use core::cell::RefCell;
use display::{DispalyState, SegmentPins, Show};
use keyboard::{Debounce, KeyPress, KeyReadout, Keyboard};
use arduino_hal::{
adc::channel::{ADC6, ADC7},
hal::port::{PB3, PB4, PB5, PC1, PC2, PC3, PD2, PD3, PD4, PD7},
hal::port::PB5,
port::{mode::Output, Pin},
Adc,
};
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)
const SERIAL_BAUD: u32 = 115200;
// 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;
// Timing
pub const IO_SEG_REFRESH_FREQ: u32 = 100; // 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_REFRESH_FREQ: u32 = 1000; // Segment timer freq; from 62 to 15779 for 8bit counter
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;
@@ -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.
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)]
unsafe fn TIMER0_COMPA() {
//TODO: Handle timer interrupt to display next segment
// file:///home/hxd/projects/avr-calc/target/avr-none/doc/avr_device/interrupt/index.html
// ...
avr_device::interrupt::free(
|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::free(|cs| {
try_access(&LED, cs, |led| led.set_high()).expect("LED not available (COMPA)");
try_access(&IO_LOOP, cs, |io_loop| {
io_loop.display_on();
})
});
}
#[avr_device::interrupt(atmega328p)]
unsafe fn TIMER0_COMPB() {
//TODO: Disable display segment and handle keyboard input for current segment
avr_device::interrupt::free(
|cs| match LED.borrow(cs).borrow_mut().deref_mut().as_mut() {
Some(led) => led.set_low(),
None => panic!(),
},
);
avr_device::interrupt::free(|cs| {
try_access(&LED, cs, |led| led.set_low()).expect("LED not available (COMPB)");
try_access(&IO_LOOP, cs, |io_loop| {
io_loop.display_off();
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> {
display_no: usize,
pin: &'p mut Pin<Output>,
pub struct IOLoop {
index: usize,
io_pins: IOPins,
segment_pins: SegmentPins,
dispaly: DispalyState,
keyboard: Keyboard,
readount: Option<KeyReadout>,
debounce: Debounce,
}
impl IOSelect<'_> {
fn display_no(&self) -> usize {
self.display_no
impl IOLoop {
pub fn new(io_pins: IOPins, segment_pins: SegmentPins, keyboard: Keyboard) -> IOLoop {
IOLoop {
index: 0,
io_pins,
segment_pins,
dispaly: Default::default(),
keyboard,
readount: None,
debounce: Default::default(),
}
}
fn set_on(&mut self) {
self.pin.set_high()
}
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(),
])
pub fn update_display(&mut self, display: &DispalyState) {
self.dispaly = *display;
}
fn iter_mut(&mut self) -> impl Iterator<Item = IOSelect> {
self.0
.iter_mut()
.enumerate()
.map(|(display_no, pin)| IOSelect { display_no, pin })
fn select_on(&mut self) {
self.io_pins[self.index].set_high();
}
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();
let mut io = IOPins::new(
let io_pins = IOPins::new(
pins.d2.into_output(),
pins.d3.into_output(),
pins.d4.into_output(),
@@ -458,8 +496,7 @@ fn main() -> ! {
pins.d11.into_output(),
pins.d12.into_output(),
);
let mut seg = SegmentPins::new(
let segment_pins = SegmentPins::new(
pins.d5.into_output(),
pins.d8.into_output(),
pins.d10.into_output(),
@@ -469,10 +506,11 @@ fn main() -> ! {
pins.a0.into_output(),
pins.a4.into_output(),
);
let mut adc = Adc::new(dp.ADC, Default::default());
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 display = DispalyState::default();
@@ -485,7 +523,9 @@ fn main() -> ! {
};
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(
dp.TC0, // Timer0 (8bit)
@@ -497,22 +537,10 @@ fn main() -> ! {
}
loop {
let mut last_key_readout: Option<KeyReadout> = None;
// Timing: 1ms display, 0.230ms read key; every 12ms/83Hz
for (mut io_select, ss) in io.iter_mut().zip(display.iter_segments()) {
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);
let mut key = None;
avr_device::interrupt::free(|cs| {
key = KEY_PRESS.borrow(cs).borrow_mut().take();
});
if let TransientState::Done = state.transient {
if let Some(key) = key {
@@ -569,6 +597,12 @@ fn main() -> ! {
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);
}
}