control segment brightness; test pattern
This commit is contained in:
@@ -82,11 +82,19 @@ impl SegmentPins {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Segment(u8);
|
||||
pub struct Brightness(pub u8);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Segment(u8, Brightness);
|
||||
|
||||
impl Segment {
|
||||
pub fn new() -> Segment {
|
||||
Segment(0)
|
||||
Segment(0, Brightness(0xFF))
|
||||
}
|
||||
|
||||
pub fn brightness(&mut self, b: u8) -> &mut Self {
|
||||
self.1 = Brightness(b);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn off(&mut self) -> &mut Self {
|
||||
@@ -134,7 +142,7 @@ impl Segment {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn apply(&self, seg: &mut SegmentPins) {
|
||||
pub fn apply(&self, seg: &mut SegmentPins) -> Brightness {
|
||||
seg.set_off();
|
||||
if self.0 & 0b1000_0000 != 0 {
|
||||
seg.set_a();
|
||||
@@ -160,6 +168,7 @@ impl Segment {
|
||||
if self.0 & 0b0000_0001 != 0 {
|
||||
seg.set_dp();
|
||||
}
|
||||
self.1
|
||||
}
|
||||
|
||||
pub fn num(&mut self, no: u8) -> &mut Self {
|
||||
|
||||
38
src/main.rs
38
src/main.rs
@@ -28,7 +28,7 @@ use arduino_hal::{
|
||||
};
|
||||
use ufmt::derive::uDebug;
|
||||
|
||||
use crate::io::IOPins;
|
||||
use crate::{display::Brightness, io::IOPins, timer::SegmentTimer};
|
||||
|
||||
// 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;
|
||||
@@ -49,7 +49,12 @@ pub const DISPLAY_SEGMENT_EXP_MINUS: usize = 6;
|
||||
// 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
|
||||
pub const IO_SEGMENT_ON_MIN_US: u32 = 80; // How long in μs to hold segment LEDs on (dark)
|
||||
pub const IO_SEGMENT_ON_MAX_US: u32 = 700; // How long in μs to hold segment LEDs on (bright)
|
||||
|
||||
const fn scale_brightness(b: u8) -> u32 {
|
||||
IO_SEGMENT_ON_MIN_US + (IO_SEGMENT_ON_MAX_US - IO_SEGMENT_ON_MIN_US) * b as u32 / 0xFF
|
||||
}
|
||||
|
||||
// Calculator setup
|
||||
pub const STACK_DEPTH: usize = 7;
|
||||
@@ -66,6 +71,7 @@ static LED: Mutex<RefCell<Option<Pin<Output, PB5>>>> = Mutex::new(RefCell::new(N
|
||||
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));
|
||||
static SEGMENT_TIMER: Mutex<RefCell<Option<SegmentTimer>>> = Mutex::new(RefCell::new(None));
|
||||
|
||||
fn try_access<'cs, 'v: 'cs, T, O>(
|
||||
v: &'v Mutex<RefCell<Option<T>>>,
|
||||
@@ -83,17 +89,20 @@ fn try_access<'cs, 'v: 'cs, T, O>(
|
||||
#[avr_device::interrupt(atmega328p)]
|
||||
unsafe fn TIMER0_COMPA() {
|
||||
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();
|
||||
})
|
||||
// try_access(&LED, cs, |led| led.set_high()).expect("LED not available (COMPA)");
|
||||
let brightness = try_access(&IO_LOOP, cs, |io_loop| io_loop.display_on());
|
||||
if let Some(brightness) = brightness {
|
||||
try_access(&SEGMENT_TIMER, cs, |st| {
|
||||
st.segment_on_time(scale_brightness(brightness.0))
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[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(&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| {
|
||||
@@ -156,11 +165,12 @@ impl IOLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_on(&mut self) {
|
||||
pub fn display_on(&mut self) -> Brightness {
|
||||
self.select_off();
|
||||
let segment = self.dispaly[self.index];
|
||||
segment.apply(&mut self.segment_pins);
|
||||
let brighness = segment.apply(&mut self.segment_pins);
|
||||
self.select_on();
|
||||
brighness
|
||||
}
|
||||
|
||||
pub fn display_off(&mut self) {
|
||||
@@ -514,6 +524,7 @@ fn main() -> ! {
|
||||
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 segment_timer = SegmentTimer::init(dp.TC0, IO_SEGMENT_RATE_US);
|
||||
|
||||
let mut number_input = NumberInput::default();
|
||||
|
||||
@@ -530,12 +541,8 @@ fn main() -> ! {
|
||||
LED.borrow(cs).replace(Some(led));
|
||||
IO_LOOP.borrow(cs).replace(Some(io_loop));
|
||||
ADC.borrow(cs).replace(Some(adc));
|
||||
SEGMENT_TIMER.borrow(cs).replace(Some(segment_timer));
|
||||
});
|
||||
timer::segment_timer_init(
|
||||
dp.TC0, // Timer0 (8bit)
|
||||
IO_SEGMENT_RATE_US,
|
||||
IO_SEGMENT_ON_US,
|
||||
);
|
||||
unsafe {
|
||||
avr_device::interrupt::enable();
|
||||
}
|
||||
@@ -603,6 +610,9 @@ fn main() -> ! {
|
||||
{
|
||||
seg.dp();
|
||||
}
|
||||
for (no, seg) in display.slice(0, DISPLAY_SEGMENTS).iter_mut().enumerate() {
|
||||
seg.brightness((no * 0xFF / DISPLAY_SEGMENTS) as u8);
|
||||
}
|
||||
}
|
||||
TransientState::Err { .. } => display.error(),
|
||||
}
|
||||
|
||||
32
src/timer.rs
32
src/timer.rs
@@ -13,17 +13,16 @@ const fn us_to_ticks(us: u32) -> u32 {
|
||||
TIMER_FREQ * us / 1_000_000
|
||||
}
|
||||
|
||||
// Sets up timer to rise two interrupts:
|
||||
// Timer 0 (8bit)
|
||||
pub struct SegmentTimer(TC0);
|
||||
|
||||
impl SegmentTimer {
|
||||
// Sets up timer to rise interrupts:
|
||||
// 1. TIMER0_COMPA - segment_switch_us - time in μs to switch to next segment
|
||||
// 2. TIMER0_COMPB - segment_on_us - time in μs to keep segment LEDs on
|
||||
pub fn segment_timer_init(tc0: TC0, segment_switch_us: u32, segment_on_us: u32) {
|
||||
// 2. TIMER0_COMPB - set by set_segment_on_time to keep segment LEDs on
|
||||
pub fn init(tc0: TC0, segment_switch_us: u32) -> SegmentTimer {
|
||||
// 16_000_000 / 64 * 1000 / 1_000_000 => 250
|
||||
let ocra = us_to_ticks(segment_switch_us);
|
||||
let ocrb = us_to_ticks(segment_on_us);
|
||||
assert!(
|
||||
ocra > ocrb + BUFFER_TICKS,
|
||||
"segment_on_us cannot be longer than segment_switch_us - buffer"
|
||||
);
|
||||
|
||||
// Use CTC mode: reset counter when matches compare value
|
||||
tc0.tccr0a.write(|w| w.wgm0().ctc());
|
||||
@@ -34,34 +33,31 @@ pub fn segment_timer_init(tc0: TC0, segment_switch_us: u32, segment_on_us: u32)
|
||||
.expect("timer init segment_switch_us out of rage"),
|
||||
)
|
||||
});
|
||||
// Set the compare value for B match
|
||||
tc0.ocr0b.write(|w| {
|
||||
w.bits(
|
||||
ocrb.try_into()
|
||||
.expect("timer init segment_on_us 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());
|
||||
|
||||
SegmentTimer(tc0)
|
||||
}
|
||||
|
||||
// Set for how long the segment LEDs should be on in μs
|
||||
pub fn set_segment_on_time(tc0: TC0, segment_on_us: u32) {
|
||||
let ocra = tc0.ocr0a.read().bits();
|
||||
// Controls TIMER0_COMPB interrupt time after TIMER0_COMPA
|
||||
pub fn segment_on_time(&mut self, segment_on_us: u32) {
|
||||
let ocra = self.0.ocr0a.read().bits();
|
||||
let ocrb = us_to_ticks(segment_on_us);
|
||||
assert!(
|
||||
ocra as u32 > ocrb + BUFFER_TICKS,
|
||||
"segment_on_us cannot be longer than segment_switch_us - buffer"
|
||||
);
|
||||
// Set the compare value for B match
|
||||
tc0.ocr0b.write(|w| {
|
||||
self.0.ocr0b.write(|w| {
|
||||
w.bits(
|
||||
ocrb.try_into()
|
||||
.expect("timer init segment_on_us out of rage"),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user