use timer to update display and read keyboard
This commit is contained in:
@@ -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
52
src/io.rs
Normal 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]
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
212
src/main.rs
212
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<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
|
||||
}
|
||||
|
||||
fn set_on(&mut self) {
|
||||
self.pin.set_high()
|
||||
}
|
||||
fn set_off(&mut self) {
|
||||
self.pin.set_low()
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user