control segment brightness; test pattern
This commit is contained in:
@@ -82,11 +82,19 @@ impl SegmentPins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[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 {
|
impl Segment {
|
||||||
pub fn new() -> 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 {
|
pub fn off(&mut self) -> &mut Self {
|
||||||
@@ -134,7 +142,7 @@ impl Segment {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply(&self, seg: &mut SegmentPins) {
|
pub fn apply(&self, seg: &mut SegmentPins) -> Brightness {
|
||||||
seg.set_off();
|
seg.set_off();
|
||||||
if self.0 & 0b1000_0000 != 0 {
|
if self.0 & 0b1000_0000 != 0 {
|
||||||
seg.set_a();
|
seg.set_a();
|
||||||
@@ -160,6 +168,7 @@ impl Segment {
|
|||||||
if self.0 & 0b0000_0001 != 0 {
|
if self.0 & 0b0000_0001 != 0 {
|
||||||
seg.set_dp();
|
seg.set_dp();
|
||||||
}
|
}
|
||||||
|
self.1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num(&mut self, no: u8) -> &mut Self {
|
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 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)
|
// 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;
|
||||||
@@ -49,7 +49,12 @@ pub const DISPLAY_SEGMENT_EXP_MINUS: usize = 6;
|
|||||||
// Timing
|
// Timing
|
||||||
// Note: it takes ~224 μs to read keyboard after segment off
|
// 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_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
|
// Calculator setup
|
||||||
pub const STACK_DEPTH: usize = 7;
|
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 IO_LOOP: Mutex<RefCell<Option<IOLoop>>> = Mutex::new(RefCell::new(None));
|
||||||
static KEY_PRESS: Mutex<RefCell<Option<KeyPress>>> = 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 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>(
|
fn try_access<'cs, 'v: 'cs, T, O>(
|
||||||
v: &'v Mutex<RefCell<Option<T>>>,
|
v: &'v Mutex<RefCell<Option<T>>>,
|
||||||
@@ -83,17 +89,20 @@ fn try_access<'cs, 'v: 'cs, T, O>(
|
|||||||
#[avr_device::interrupt(atmega328p)]
|
#[avr_device::interrupt(atmega328p)]
|
||||||
unsafe fn TIMER0_COMPA() {
|
unsafe fn TIMER0_COMPA() {
|
||||||
avr_device::interrupt::free(|cs| {
|
avr_device::interrupt::free(|cs| {
|
||||||
try_access(&LED, cs, |led| led.set_high()).expect("LED not available (COMPA)");
|
// try_access(&LED, cs, |led| led.set_high()).expect("LED not available (COMPA)");
|
||||||
try_access(&IO_LOOP, cs, |io_loop| {
|
let brightness = try_access(&IO_LOOP, cs, |io_loop| io_loop.display_on());
|
||||||
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)]
|
#[avr_device::interrupt(atmega328p)]
|
||||||
unsafe fn TIMER0_COMPB() {
|
unsafe fn TIMER0_COMPB() {
|
||||||
avr_device::interrupt::free(|cs| {
|
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| {
|
try_access(&IO_LOOP, cs, |io_loop| {
|
||||||
io_loop.display_off();
|
io_loop.display_off();
|
||||||
try_access(&ADC, cs, |adc| {
|
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();
|
self.select_off();
|
||||||
let segment = self.dispaly[self.index];
|
let segment = self.dispaly[self.index];
|
||||||
segment.apply(&mut self.segment_pins);
|
let brighness = segment.apply(&mut self.segment_pins);
|
||||||
self.select_on();
|
self.select_on();
|
||||||
|
brighness
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_off(&mut self) {
|
pub fn display_off(&mut self) {
|
||||||
@@ -514,6 +524,7 @@ fn main() -> ! {
|
|||||||
let io_loop = IOLoop::new(io_pins, segment_pins, keyboard);
|
let io_loop = IOLoop::new(io_pins, segment_pins, keyboard);
|
||||||
let led = pins.d13.into_output();
|
let led = pins.d13.into_output();
|
||||||
let adc = Adc::new(dp.ADC, Default::default());
|
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();
|
let mut number_input = NumberInput::default();
|
||||||
|
|
||||||
@@ -530,12 +541,8 @@ fn main() -> ! {
|
|||||||
LED.borrow(cs).replace(Some(led));
|
LED.borrow(cs).replace(Some(led));
|
||||||
IO_LOOP.borrow(cs).replace(Some(io_loop));
|
IO_LOOP.borrow(cs).replace(Some(io_loop));
|
||||||
ADC.borrow(cs).replace(Some(adc));
|
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 {
|
unsafe {
|
||||||
avr_device::interrupt::enable();
|
avr_device::interrupt::enable();
|
||||||
}
|
}
|
||||||
@@ -603,6 +610,9 @@ fn main() -> ! {
|
|||||||
{
|
{
|
||||||
seg.dp();
|
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(),
|
TransientState::Err { .. } => display.error(),
|
||||||
}
|
}
|
||||||
|
|||||||
94
src/timer.rs
94
src/timer.rs
@@ -13,55 +13,51 @@ const fn us_to_ticks(us: u32) -> u32 {
|
|||||||
TIMER_FREQ * us / 1_000_000
|
TIMER_FREQ * us / 1_000_000
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets up timer to rise two interrupts:
|
// Timer 0 (8bit)
|
||||||
// 1. TIMER0_COMPA - segment_switch_us - time in μs to switch to next segment
|
pub struct SegmentTimer(TC0);
|
||||||
// 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"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use CTC mode: reset counter when matches compare value
|
impl SegmentTimer {
|
||||||
tc0.tccr0a.write(|w| w.wgm0().ctc());
|
// Sets up timer to rise interrupts:
|
||||||
// Set the compare value for TOP (reset)
|
// 1. TIMER0_COMPA - segment_switch_us - time in μs to switch to next segment
|
||||||
tc0.ocr0a.write(|w| {
|
// 2. TIMER0_COMPB - set by set_segment_on_time to keep segment LEDs on
|
||||||
w.bits(
|
pub fn init(tc0: TC0, segment_switch_us: u32) -> SegmentTimer {
|
||||||
ocra.try_into()
|
// 16_000_000 / 64 * 1000 / 1_000_000 => 250
|
||||||
.expect("timer init segment_switch_us out of rage"),
|
let ocra = us_to_ticks(segment_switch_us);
|
||||||
)
|
|
||||||
});
|
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set for how long the segment LEDs should be on in μs
|
// Use CTC mode: reset counter when matches compare value
|
||||||
pub fn set_segment_on_time(tc0: TC0, segment_on_us: u32) {
|
tc0.tccr0a.write(|w| w.wgm0().ctc());
|
||||||
let ocra = tc0.ocr0a.read().bits();
|
// Set the compare value for TOP (reset)
|
||||||
let ocrb = us_to_ticks(segment_on_us);
|
tc0.ocr0a.write(|w| {
|
||||||
assert!(
|
w.bits(
|
||||||
ocra as u32 > ocrb + BUFFER_TICKS,
|
ocra.try_into()
|
||||||
"segment_on_us cannot be longer than segment_switch_us - buffer"
|
.expect("timer init segment_switch_us out of rage"),
|
||||||
);
|
)
|
||||||
// Set the compare value for B match
|
});
|
||||||
tc0.ocr0b.write(|w| {
|
// Slow down the timer (CLK / prescale)
|
||||||
w.bits(
|
tc0.tccr0b.write(|w| w.cs0().prescale_64());
|
||||||
ocrb.try_into()
|
// Raise interrupt on TOP (reset)
|
||||||
.expect("timer init segment_on_us out of rage"),
|
// 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"),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user