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]);
|
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
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 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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
214
src/main.rs
214
src/main.rs
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user