From 56f2738ea2b910e45a13825e265b2c33ac31e29c Mon Sep 17 00:00:00 2001 From: Hexa Dust Date: Fri, 18 Jul 2025 20:31:23 +0100 Subject: [PATCH] use timer to update display and read keyboard --- src/display.rs | 6 +- src/io.rs | 52 ++++++++++++ src/keyboard.rs | 15 ++-- src/main.rs | 214 ++++++++++++++++++++++++++++-------------------- 4 files changed, 190 insertions(+), 97 deletions(-) create mode 100644 src/io.rs diff --git a/src/display.rs b/src/display.rs index 25319d3..ba4d452 100644 --- a/src/display.rs +++ b/src/display.rs @@ -218,7 +218,7 @@ impl Default for Segment { } } -#[derive(Default)] +#[derive(Default, Clone, Copy)] pub struct DispalyState([Segment; DISPLAY_SEGMENTS]); impl Index for DispalyState { @@ -236,6 +236,10 @@ impl IndexMut for DispalyState { } impl DispalyState { + pub fn len(&self) -> usize { + self.0.len() + } + pub fn iter_segments(&self) -> impl Iterator { self.0.iter() } diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..67767e7 --- /dev/null +++ b/src/io.rs @@ -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; 9]); + +impl IOPins { + pub fn new( + kd_d1_1c: Pin, + kd_d2_5div: Pin, + kd_d3_6mul: Pin, + kd_d4_7up: Pin, + kd_d5_8e: Pin, + kd_d6_90: Pin, + kd_d7_2down: Pin, + kd_d8_3plus: Pin, + kd_d9_4min: Pin, + ) -> 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 for IOPins { + type Output = Pin; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl IndexMut for IOPins { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } +} diff --git a/src/keyboard.rs b/src/keyboard.rs index f7ea0c0..0406089 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -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: KN, - ko: KO, +pub struct Keyboard { + kn: ADC7, + ko: ADC6, } -impl, KO: AdcChannel> Keyboard { - pub fn new(kn: KN, ko: KO) -> Keyboard { +impl Keyboard { + pub fn new(kn: ADC7, ko: ADC6) -> Keyboard { Keyboard { kn, ko } } diff --git a/src/main.rs b/src/main.rs index d3154a1..91e81e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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; // * On another timer interrupt expire disable display (LEDs off) and handle keyboard input. static LED: Mutex>>> = Mutex::new(RefCell::new(None)); +static IO_LOOP: Mutex>> = Mutex::new(RefCell::new(None)); +static KEY_PRESS: Mutex>> = Mutex::new(RefCell::new(None)); +static ADC: Mutex>> = Mutex::new(RefCell::new(None)); + +fn try_access<'cs, 'v: 'cs, T, O>( + v: &'v Mutex>>, + cs: CriticalSection<'cs>, + f: impl for<'t> FnOnce(&'t mut T) -> O, +) -> Option { + 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, +pub struct IOLoop { + index: usize, + io_pins: IOPins, + segment_pins: SegmentPins, + dispaly: DispalyState, + keyboard: Keyboard, + readount: Option, + 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; 9]); - -impl IOPins { - fn new( - kd_d1_1c: Pin, - kd_d2_5div: Pin, - kd_d3_6mul: Pin, - kd_d4_7up: Pin, - kd_d5_8e: Pin, - kd_d6_90: Pin, - kd_d7_2down: Pin, - kd_d8_3plus: Pin, - kd_d9_4min: Pin, - ) -> 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 { - 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 { + 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 = 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); } }