Compare commits
10 Commits
59a001b94c
...
d6a4b0fe22
| Author | SHA1 | Date | |
|---|---|---|---|
|
d6a4b0fe22
|
|||
|
cba331b939
|
|||
|
56f2738ea2
|
|||
|
1f961964f0
|
|||
|
7b524aaae2
|
|||
|
8c27acbf50
|
|||
|
ef97c334b5
|
|||
|
d69c3eb56d
|
|||
|
46cb2886f2
|
|||
|
82fed85229
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -36,6 +36,7 @@ name = "avr-calc"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arduino-hal",
|
"arduino-hal",
|
||||||
|
"avr-device",
|
||||||
"calc-math",
|
"calc-math",
|
||||||
"embedded-hal 1.0.0",
|
"embedded-hal 1.0.0",
|
||||||
"nb 1.1.0",
|
"nb 1.1.0",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ ufmt = "0.2.0"
|
|||||||
nb = "1.1.0"
|
nb = "1.1.0"
|
||||||
embedded-hal = "1.0"
|
embedded-hal = "1.0"
|
||||||
calc-math = { path = "../calc-math", features = ["ufmt"] }
|
calc-math = { path = "../calc-math", features = ["ufmt"] }
|
||||||
|
avr-device = "0.7.0"
|
||||||
|
|
||||||
[dependencies.arduino-hal]
|
[dependencies.arduino-hal]
|
||||||
git = "https://github.com/rahix/avr-hal"
|
git = "https://github.com/rahix/avr-hal"
|
||||||
|
|||||||
186
src/display.rs
186
src/display.rs
@@ -82,169 +82,133 @@ impl SegmentPins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Segment {
|
pub struct Segment(u8);
|
||||||
a: bool,
|
|
||||||
b: bool,
|
|
||||||
c: bool,
|
|
||||||
d: bool,
|
|
||||||
e: bool,
|
|
||||||
f: bool,
|
|
||||||
g: bool,
|
|
||||||
dp: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Segment {
|
impl Segment {
|
||||||
pub fn new() -> Segment {
|
pub fn new() -> Segment {
|
||||||
Segment {
|
Segment(0)
|
||||||
a: false,
|
}
|
||||||
b: false,
|
|
||||||
c: false,
|
pub fn off(&mut self) -> &mut Self {
|
||||||
d: false,
|
self.0 = 0;
|
||||||
e: false,
|
self
|
||||||
f: false,
|
}
|
||||||
g: false,
|
|
||||||
dp: false,
|
pub fn a(&mut self) -> &mut Self {
|
||||||
}
|
self.0 |= 0b1000_0000;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn b(&mut self) -> &mut Self {
|
||||||
|
self.0 |= 0b0100_0000;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn c(&mut self) -> &mut Self {
|
||||||
|
self.0 |= 0b0010_0000;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn d(&mut self) -> &mut Self {
|
||||||
|
self.0 |= 0b0001_0000;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn e(&mut self) -> &mut Self {
|
||||||
|
self.0 |= 0b0000_1000;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn f(&mut self) -> &mut Self {
|
||||||
|
self.0 |= 0b0000_0100;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn g(&mut self) -> &mut Self {
|
||||||
|
self.0 |= 0b0000_0010;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dp(&mut self) -> &mut Self {
|
||||||
|
self.0 |= 0b0000_0001;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply(&self, seg: &mut SegmentPins) {
|
pub fn apply(&self, seg: &mut SegmentPins) {
|
||||||
seg.set_off();
|
seg.set_off();
|
||||||
if self.a {
|
if self.0 & 0b1000_0000 != 0 {
|
||||||
seg.set_a();
|
seg.set_a();
|
||||||
}
|
}
|
||||||
if self.b {
|
if self.0 & 0b0100_0000 != 0 {
|
||||||
seg.set_b();
|
seg.set_b();
|
||||||
}
|
}
|
||||||
if self.c {
|
if self.0 & 0b0010_0000 != 0 {
|
||||||
seg.set_c();
|
seg.set_c();
|
||||||
}
|
}
|
||||||
if self.d {
|
if self.0 & 0b0001_0000 != 0 {
|
||||||
seg.set_d();
|
seg.set_d();
|
||||||
}
|
}
|
||||||
if self.e {
|
if self.0 & 0b0000_1000 != 0 {
|
||||||
seg.set_e();
|
seg.set_e();
|
||||||
}
|
}
|
||||||
if self.f {
|
if self.0 & 0b0000_0100 != 0 {
|
||||||
seg.set_f();
|
seg.set_f();
|
||||||
}
|
}
|
||||||
if self.g {
|
if self.0 & 0b0000_0010 != 0 {
|
||||||
seg.set_g();
|
seg.set_g();
|
||||||
}
|
}
|
||||||
if self.dp {
|
if self.0 & 0b0000_0001 != 0 {
|
||||||
seg.set_dp();
|
seg.set_dp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn off(&mut self) -> &mut Self {
|
|
||||||
self.a = false;
|
|
||||||
self.b = false;
|
|
||||||
self.c = false;
|
|
||||||
self.d = false;
|
|
||||||
self.e = false;
|
|
||||||
self.f = false;
|
|
||||||
self.g = false;
|
|
||||||
self.dp = false;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn num(&mut self, no: u8) -> &mut Self {
|
pub fn num(&mut self, no: u8) -> &mut Self {
|
||||||
match no {
|
match no {
|
||||||
0 => {
|
0 => {
|
||||||
self.a = true;
|
self.a().b().c().d().e().f();
|
||||||
self.b = true;
|
|
||||||
self.c = true;
|
|
||||||
self.d = true;
|
|
||||||
self.e = true;
|
|
||||||
self.f = true;
|
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
self.b = true;
|
self.b().c();
|
||||||
self.c = true;
|
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
self.a = true;
|
self.a().b().g().e().d();
|
||||||
self.b = true;
|
|
||||||
self.g = true;
|
|
||||||
self.e = true;
|
|
||||||
self.d = true;
|
|
||||||
}
|
}
|
||||||
3 => {
|
3 => {
|
||||||
self.a = true;
|
self.a().b().g().c().d();
|
||||||
self.b = true;
|
|
||||||
self.g = true;
|
|
||||||
self.c = true;
|
|
||||||
self.d = true;
|
|
||||||
}
|
}
|
||||||
4 => {
|
4 => {
|
||||||
self.f = true;
|
self.f().g().b().c();
|
||||||
self.g = true;
|
|
||||||
self.b = true;
|
|
||||||
self.c = true;
|
|
||||||
}
|
}
|
||||||
5 => {
|
5 => {
|
||||||
self.a = true;
|
self.a().f().g().c().d();
|
||||||
self.f = true;
|
|
||||||
self.g = true;
|
|
||||||
self.c = true;
|
|
||||||
self.d = true;
|
|
||||||
}
|
}
|
||||||
6 => {
|
6 => {
|
||||||
self.a = true;
|
self.a().f().g().c().d().e();
|
||||||
self.f = true;
|
|
||||||
self.g = true;
|
|
||||||
self.c = true;
|
|
||||||
self.d = true;
|
|
||||||
self.e = true;
|
|
||||||
}
|
}
|
||||||
7 => {
|
7 => {
|
||||||
self.a = true;
|
self.a().b().c();
|
||||||
self.b = true;
|
|
||||||
self.c = true;
|
|
||||||
}
|
}
|
||||||
8 => {
|
8 => {
|
||||||
self.a = true;
|
self.a().b().c().d().e().f().g();
|
||||||
self.b = true;
|
|
||||||
self.c = true;
|
|
||||||
self.d = true;
|
|
||||||
self.e = true;
|
|
||||||
self.f = true;
|
|
||||||
self.g = true;
|
|
||||||
}
|
}
|
||||||
9 => {
|
9 => {
|
||||||
self.a = true;
|
self.a().b().c().d().f().g();
|
||||||
self.b = true;
|
|
||||||
self.c = true;
|
|
||||||
self.d = true;
|
|
||||||
self.f = true;
|
|
||||||
self.g = true;
|
|
||||||
}
|
}
|
||||||
_ => panic!("Num out of range"),
|
_ => panic!("Num out of range"),
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dp(&mut self) -> &mut Self {
|
|
||||||
self.dp = true;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn minus(&mut self) -> &mut Self {
|
pub fn minus(&mut self) -> &mut Self {
|
||||||
self.g = true;
|
self.g()
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prompt(&mut self) -> &mut Self {
|
pub fn prompt(&mut self) -> &mut Self {
|
||||||
self.d = true;
|
self.d()
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn e(&mut self) -> &mut Self {
|
pub fn sym_e(&mut self) -> &mut Self {
|
||||||
self.a = true;
|
self.a().d().e().f().g()
|
||||||
self.d = true;
|
|
||||||
self.e = true;
|
|
||||||
self.f = true;
|
|
||||||
self.g = true;
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,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 {
|
||||||
@@ -272,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()
|
||||||
}
|
}
|
||||||
@@ -293,12 +261,16 @@ impl DispalyState {
|
|||||||
self[1].off();
|
self[1].off();
|
||||||
self[2].off();
|
self[2].off();
|
||||||
self[3].off().minus();
|
self[3].off().minus();
|
||||||
self[4].off().e();
|
self[4].off().sym_e();
|
||||||
self[5].off().minus();
|
self[5].off().minus();
|
||||||
self[6].off();
|
self[6].off();
|
||||||
self[7].off();
|
self[7].off();
|
||||||
self[8].off();
|
self[8].off();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn slice(&mut self, start: usize, len: usize) -> &mut [Segment] {
|
||||||
|
&mut self.0.as_mut_slice()[start..(start + len)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show data on segment display
|
// Show data on segment display
|
||||||
|
|||||||
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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,26 +81,24 @@ impl<KN: AdcChannel<Atmega, ADC>, KO: AdcChannel<Atmega, ADC>> Keyboard<KN, KO>
|
|||||||
pub struct Debounce {
|
pub struct Debounce {
|
||||||
record: [Option<KeyReadout>; DEBOUNCE_DEPTH],
|
record: [Option<KeyReadout>; DEBOUNCE_DEPTH],
|
||||||
pos: usize,
|
pos: usize,
|
||||||
last: Option<KeyReadout>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debounce {
|
impl Debounce {
|
||||||
pub fn input(&mut self, key: Option<KeyReadout>) -> Option<KeyPress> {
|
pub fn input(&mut self, key: Option<KeyReadout>) -> Option<KeyPress> {
|
||||||
|
// React to fresh key press immediately if it was not pressed for depth of record
|
||||||
|
let new_key = key.and_then(|key| {
|
||||||
|
self.record
|
||||||
|
.iter()
|
||||||
|
.all(|hist| hist != &Some(key))
|
||||||
|
.then_some(key)
|
||||||
|
});
|
||||||
|
|
||||||
self.record[self.pos] = key;
|
self.record[self.pos] = key;
|
||||||
self.pos += 1;
|
self.pos += 1;
|
||||||
if self.pos >= self.record.len() {
|
if self.pos >= self.record.len() {
|
||||||
self.pos = 0;
|
self.pos = 0;
|
||||||
}
|
}
|
||||||
if self.record.iter().all(|hist| hist == &key) && self.last != key {
|
|
||||||
self.last = key;
|
new_key.and_then(|key| KeyPress::map(key.display_no, key.kn, key.ko))
|
||||||
key.and_then(|key_readout| {
|
|
||||||
KeyPress::map(key_readout.display_no, key_readout.kn, key_readout.ko)
|
|
||||||
})
|
|
||||||
} else if self.record.iter().all(|hist| hist == &None) {
|
|
||||||
self.last = None;
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
294
src/main.rs
294
src/main.rs
@@ -1,25 +1,35 @@
|
|||||||
|
#![feature(abi_avr_interrupt)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
mod display;
|
mod display;
|
||||||
|
mod io;
|
||||||
mod keyboard;
|
mod keyboard;
|
||||||
mod panic;
|
mod panic;
|
||||||
|
mod timer;
|
||||||
|
|
||||||
|
use avr_device::{
|
||||||
|
asm::sleep,
|
||||||
|
interrupt::{CriticalSection, Mutex},
|
||||||
|
};
|
||||||
use calc_math::{
|
use calc_math::{
|
||||||
calc::{StackCalc, StackCalcError},
|
calc::{StackCalc, StackCalcError},
|
||||||
Decimal,
|
Decimal,
|
||||||
};
|
};
|
||||||
|
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, 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
|
||||||
@@ -36,58 +46,134 @@ pub const DISPLAY_SEGMENT_SIG_MINUS: usize = 0;
|
|||||||
pub const DISPLAY_SEGMENTS_EXP: usize = 2;
|
pub const DISPLAY_SEGMENTS_EXP: usize = 2;
|
||||||
pub const DISPLAY_SEGMENT_EXP_MINUS: usize = 6;
|
pub const DISPLAY_SEGMENT_EXP_MINUS: usize = 6;
|
||||||
|
|
||||||
type Calc = StackCalc<f32, 3, 5, u8>;
|
// Timing
|
||||||
|
// Note: it takes ~224 μs to read keyboard after segment off
|
||||||
|
pub const IO_SEGMENT_RATE_US: u32 = 1000; // Time in μs between segment updates
|
||||||
|
pub const IO_SEGMENT_ON_US: u32 = 200; // How long in μs to hold segment LEDs on
|
||||||
|
|
||||||
struct IOSelect<'p> {
|
// Calculator setup
|
||||||
display_no: usize,
|
pub const STACK_DEPTH: usize = 7;
|
||||||
pin: &'p mut Pin<Output>,
|
type Calc = StackCalc<f32, STACK_DEPTH, 5, u8>;
|
||||||
|
|
||||||
|
// Interrupt driven I/O
|
||||||
|
//
|
||||||
|
// * Run timer at 100Hz (10ms) to step through segments.
|
||||||
|
// * On each segment interrupt configure segments and enable output (LEDs on).
|
||||||
|
// * Set another timer to run for 1ms.
|
||||||
|
// * 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
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IOSelect<'_> {
|
#[avr_device::interrupt(atmega328p)]
|
||||||
fn display_no(&self) -> usize {
|
unsafe fn TIMER0_COMPA() {
|
||||||
self.display_no
|
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| {
|
||||||
fn set_on(&mut self) {
|
io_loop.display_on();
|
||||||
self.pin.set_high()
|
})
|
||||||
}
|
});
|
||||||
fn set_off(&mut self) {
|
|
||||||
self.pin.set_low()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IOPins([Pin<Output>; 9]);
|
#[avr_device::interrupt(atmega328p)]
|
||||||
|
unsafe fn TIMER0_COMPB() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
impl IOPins {
|
pub struct IOLoop {
|
||||||
fn new(
|
index: usize,
|
||||||
kd_d1_1c: Pin<Output, PD2>,
|
io_pins: IOPins,
|
||||||
kd_d2_5div: Pin<Output, PD3>,
|
segment_pins: SegmentPins,
|
||||||
kd_d3_6mul: Pin<Output, PD4>,
|
dispaly: DispalyState,
|
||||||
kd_d4_7up: Pin<Output, PC3>,
|
keyboard: Keyboard,
|
||||||
kd_d5_8e: Pin<Output, PC2>,
|
readount: Option<KeyReadout>,
|
||||||
kd_d6_90: Pin<Output, PD7>,
|
debounce: Debounce,
|
||||||
kd_d7_2down: Pin<Output, PC1>,
|
}
|
||||||
kd_d8_3plus: Pin<Output, PB3>,
|
|
||||||
kd_d9_4min: Pin<Output, PB4>,
|
impl IOLoop {
|
||||||
) -> IOPins {
|
pub fn new(io_pins: IOPins, segment_pins: SegmentPins, keyboard: Keyboard) -> IOLoop {
|
||||||
IOPins([
|
IOLoop {
|
||||||
kd_d1_1c.downgrade(),
|
index: 0,
|
||||||
kd_d2_5div.downgrade(),
|
io_pins,
|
||||||
kd_d3_6mul.downgrade(),
|
segment_pins,
|
||||||
kd_d4_7up.downgrade(),
|
dispaly: Default::default(),
|
||||||
kd_d5_8e.downgrade(),
|
keyboard,
|
||||||
kd_d6_90.downgrade(),
|
readount: None,
|
||||||
kd_d7_2down.downgrade(),
|
debounce: Default::default(),
|
||||||
kd_d8_3plus.downgrade(),
|
}
|
||||||
kd_d9_4min.downgrade(),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_mut(&mut self) -> impl Iterator<Item = IOSelect> {
|
pub fn update_display(&mut self, display: &DispalyState) {
|
||||||
self.0
|
self.dispaly = *display;
|
||||||
.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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,22 +278,47 @@ impl Show for NumberInput {
|
|||||||
fn show(&self, display: &mut DispalyState) {
|
fn show(&self, display: &mut DispalyState) {
|
||||||
display.off();
|
display.off();
|
||||||
display[1].dp();
|
display[1].dp();
|
||||||
if !self.enter_exponent && self.significant_pos < DISPLAY_SEGMENTS_SIG {
|
|
||||||
display[self.significant_pos + 1].prompt();
|
let (minus, sig) = display.slice(0, 1 + DISPLAY_SEGMENTS_SIG).split_at_mut(1);
|
||||||
} else if self.enter_exponent && self.exponent_pos < DISPLAY_SEGMENTS_EXP {
|
|
||||||
display[DISPLAY_SEGMENTS_SIG + 2 + self.exponent_pos].prompt();
|
|
||||||
}
|
|
||||||
if self.minus {
|
if self.minus {
|
||||||
display[DISPLAY_SEGMENT_SIG_MINUS].minus();
|
minus[0].minus();
|
||||||
}
|
}
|
||||||
for i in 0..self.significant_pos {
|
|
||||||
display[DISPLAY_SEGMENT_SIG_MINUS + 1 + i].num(self.significant[i]);
|
for (seg, num) in sig
|
||||||
|
.iter_mut()
|
||||||
|
.zip(self.significant.iter())
|
||||||
|
.take(self.significant_pos)
|
||||||
|
{
|
||||||
|
seg.num(*num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.enter_exponent {
|
||||||
|
if let Some(seg) = sig.get_mut(self.significant_pos) {
|
||||||
|
seg.prompt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (minus, exp) = display
|
||||||
|
.slice(1 + DISPLAY_SEGMENTS_SIG, 1 + DISPLAY_SEGMENTS_EXP)
|
||||||
|
.split_at_mut(1);
|
||||||
|
|
||||||
if self.minus_exponent {
|
if self.minus_exponent {
|
||||||
display[DISPLAY_SEGMENT_EXP_MINUS].minus();
|
minus[0].minus();
|
||||||
}
|
}
|
||||||
for i in 0..self.exponent_pos {
|
|
||||||
display[DISPLAY_SEGMENT_EXP_MINUS + 1 + i].num(self.exponent[i]);
|
for (seg, num) in exp
|
||||||
|
.iter_mut()
|
||||||
|
.zip(self.exponent.iter())
|
||||||
|
.take(self.exponent_pos)
|
||||||
|
{
|
||||||
|
seg.num(*num);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.enter_exponent {
|
||||||
|
if let Some(seg) = exp.get_mut(self.exponent_pos) {
|
||||||
|
seg.prompt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -378,7 +489,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(),
|
||||||
@@ -389,8 +500,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(),
|
||||||
@@ -400,10 +510,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();
|
||||||
@@ -415,24 +526,28 @@ fn main() -> ! {
|
|||||||
calculator: CalcluclatorState::EnterSignificant,
|
calculator: CalcluclatorState::EnterSignificant,
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
avr_device::interrupt::free(|cs| {
|
||||||
let mut last_key_readout: Option<KeyReadout> = None;
|
LED.borrow(cs).replace(Some(led));
|
||||||
for (mut io_select, ss) in io.iter_mut().zip(display.iter_segments()) {
|
IO_LOOP.borrow(cs).replace(Some(io_loop));
|
||||||
ss.apply(&mut seg);
|
ADC.borrow(cs).replace(Some(adc));
|
||||||
io_select.set_on();
|
});
|
||||||
arduino_hal::delay_ms(1);
|
timer::segment_timer_init(
|
||||||
io_select.set_off();
|
dp.TC0, // Timer0 (8bit)
|
||||||
seg.set_off();
|
IO_SEGMENT_RATE_US,
|
||||||
io_select.set_on();
|
IO_SEGMENT_ON_US,
|
||||||
if let key_readout @ Some(_) = keyboard.read(&mut adc, io_select.display_no()) {
|
);
|
||||||
last_key_readout = key_readout;
|
unsafe {
|
||||||
}
|
avr_device::interrupt::enable();
|
||||||
io_select.set_off();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let key = debounce.input(last_key_readout);
|
loop {
|
||||||
|
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 TransientState::Done = state.transient {
|
||||||
|
// No transient state - handle key or wait for key
|
||||||
if let Some(key) = key {
|
if let Some(key) = key {
|
||||||
ufmt::uwriteln!(
|
ufmt::uwriteln!(
|
||||||
&mut serial,
|
&mut serial,
|
||||||
@@ -467,16 +582,35 @@ fn main() -> ! {
|
|||||||
for (i, dec) in calc.iter().enumerate() {
|
for (i, dec) in calc.iter().enumerate() {
|
||||||
ufmt::uwriteln!(&mut serial, "[{}] {}", i, dec).ok();
|
ufmt::uwriteln!(&mut serial, "[{}] {}", i, dec).ok();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Wait for key
|
||||||
|
sleep(); // Enter idle mode; wake up on interrupt
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.transient.on_frame(key)
|
// In transient state - handle animation
|
||||||
|
arduino_hal::delay_ms(10);
|
||||||
|
state.transient.on_frame(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
match &state.transient {
|
match &state.transient {
|
||||||
TransientState::Done => number_input.show(&mut display),
|
TransientState::Done => {
|
||||||
|
number_input.show(&mut display);
|
||||||
|
for seg in display
|
||||||
|
.slice(0, DISPLAY_SEGMENTS)
|
||||||
|
.iter_mut()
|
||||||
|
.rev()
|
||||||
|
.take(calc.len())
|
||||||
|
{
|
||||||
|
seg.dp();
|
||||||
|
}
|
||||||
|
}
|
||||||
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/timer.rs
Normal file
26
src/timer.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
use arduino_hal::{clock::Clock, pac::TC0, DefaultClock};
|
||||||
|
|
||||||
|
// Sets up timer to rise two interrupts:
|
||||||
|
// 1. TIMER0_COMPA - every segment_rate_us μs
|
||||||
|
// 2. TIMER0_COMPB - segment_on_us μs after TIMER0_COMPA
|
||||||
|
pub fn segment_timer_init(tc0: TC0, segment_rate_us: u32, segment_on_us: u32) {
|
||||||
|
// 16_000_000 / 64 * 1000 / 1_000_000 => 250
|
||||||
|
let ocra = DefaultClock::FREQ / 64 * segment_rate_us / 1_000_000;
|
||||||
|
let ocrb = DefaultClock::FREQ / 64 * segment_on_us / 1_000_000;
|
||||||
|
assert!(ocra > ocrb);
|
||||||
|
|
||||||
|
// Use CTC mode: reset counter when matches compare value
|
||||||
|
tc0.tccr0a.write(|w| w.wgm0().ctc());
|
||||||
|
// Set the compare value for TOP (reset)
|
||||||
|
tc0.ocr0a
|
||||||
|
.write(|w| w.bits(ocra.try_into().expect("timer init seg_freq out of rage")));
|
||||||
|
// Set the compare value for B match
|
||||||
|
tc0.ocr0b
|
||||||
|
.write(|w| w.bits(ocrb.try_into().expect("timer init on_div out of rage")));
|
||||||
|
// Slow down the timer (CLK / prescale)
|
||||||
|
tc0.tccr0b.write(|w| w.cs0().prescale_64());
|
||||||
|
// Raise interrupt on TOP (reset)
|
||||||
|
// Raise interrupt on B match
|
||||||
|
tc0.timsk0
|
||||||
|
.write(|w| w.ocie0a().set_bit().ocie0b().set_bit());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user