diff --git a/src/display.rs b/src/display.rs index ba4d452..c03555c 100644 --- a/src/display.rs +++ b/src/display.rs @@ -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 { diff --git a/src/main.rs b/src/main.rs index 90baa4d..e769479 100644 --- a/src/main.rs +++ b/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>>> = Mutex::new(RefCell::new(N 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)); +static SEGMENT_TIMER: Mutex>> = Mutex::new(RefCell::new(None)); fn try_access<'cs, 'v: 'cs, T, O>( v: &'v Mutex>>, @@ -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(), } diff --git a/src/timer.rs b/src/timer.rs index c22c37a..ef5be87 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -13,55 +13,51 @@ const fn us_to_ticks(us: u32) -> u32 { TIMER_FREQ * us / 1_000_000 } -// Sets up timer to rise two 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) { - // 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" - ); +// Timer 0 (8bit) +pub struct SegmentTimer(TC0); - // 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 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()); -} +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 - 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); -// 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(); - 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| { - w.bits( - ocrb.try_into() - .expect("timer init segment_on_us out of rage"), - ) - }); + // 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 segment_switch_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 + // 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 + self.0.ocr0b.write(|w| { + w.bits( + ocrb.try_into() + .expect("timer init segment_on_us out of rage"), + ) + }); + } }