|
@@ -1,6 +1,6 @@
|
|
|
-use core::ops::DerefMut;
|
|
|
+use core::{cell::RefCell, ops::DerefMut};
|
|
|
|
|
|
-use cortex_m::interrupt::free;
|
|
|
+use cortex_m::interrupt::{free, Mutex};
|
|
|
use stm32l4xx_hal::{
|
|
|
prelude::{
|
|
|
_embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write,
|
|
@@ -246,7 +246,7 @@ static TUBE_MAPPING: PwmOutputMap = {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-#[derive(PartialEq)]
|
|
|
+#[derive(Debug, PartialEq)]
|
|
|
enum State {
|
|
|
Idle,
|
|
|
Incrementing,
|
|
@@ -292,10 +292,14 @@ impl Tube {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static CLOCK: Mutex<RefCell<Clock>> = Mutex::new(RefCell::new(Clock::default()));
|
|
|
+
|
|
|
struct Clock {
|
|
|
tubes: [Tube; NUM_TUBES],
|
|
|
dot: Digit,
|
|
|
fade_duration: u32,
|
|
|
+ minute: Option<u32>,
|
|
|
+ hour: Option<u32>,
|
|
|
}
|
|
|
|
|
|
impl Clock {
|
|
@@ -305,112 +309,86 @@ impl Clock {
|
|
|
tubes: [TUBE_INIT; NUM_TUBES],
|
|
|
dot: Digit::default(),
|
|
|
fade_duration: 0,
|
|
|
+ minute: None,
|
|
|
+ hour: None,
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-static mut CLOCK: Clock = Clock::default();
|
|
|
-
|
|
|
-pub fn fade_in_out_digit(tube: usize, digit: Option<usize>, fade_duration: u32, refresh_cmd: bool) {
|
|
|
- unsafe {
|
|
|
- // If the tube is in the middle of a refresh sequence and a call comes
|
|
|
- // in to update the tube digit (for time), override the last value of
|
|
|
- // the refresh sequence with the new digit.
|
|
|
- if CLOCK.tubes[tube].refresh_active && !refresh_cmd {
|
|
|
- CLOCK.tubes[tube].refresh_last_digit = digit;
|
|
|
- }
|
|
|
|
|
|
- // Dont update if actively refreshing tube unless RngUpdate is set
|
|
|
- if (!CLOCK.tubes[tube].refresh_active && !refresh_cmd) || refresh_cmd {
|
|
|
- // Fade out all digits
|
|
|
- for digit in 0..NUM_DIGITS {
|
|
|
- if CLOCK.tubes[tube].digits[digit].value != DIGIT_MIN_BRIGHTNESS {
|
|
|
- CLOCK.tubes[tube].digits[digit].state = State::Decrementing;
|
|
|
+ pub fn rtc_tick(&mut self, second: u32, minute: u32, hour: u32) {
|
|
|
+ match self.hour {
|
|
|
+ Some(prev_hour) if prev_hour / 10 == hour / 10 => {
|
|
|
+ if hour / 10 == 0 {
|
|
|
+ self.fade_in_out_digit(0, None, DIGIT_FADE_DURATION_US, false);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // Fade in the specified digit
|
|
|
- if let Some(digit) = digit {
|
|
|
- if CLOCK.tubes[tube].digits[digit].value != DIGIT_MAX_BRIGHTNESS {
|
|
|
- CLOCK.tubes[tube].digits[digit].state = State::Incrementing;
|
|
|
- }
|
|
|
+ _ => {
|
|
|
+ self.fade_in_out_digit(
|
|
|
+ 0,
|
|
|
+ Some((hour / 10) as usize),
|
|
|
+ DIGIT_FADE_DURATION_US,
|
|
|
+ false,
|
|
|
+ );
|
|
|
}
|
|
|
-
|
|
|
- CLOCK.tubes[tube].last_active_digit = digit;
|
|
|
- CLOCK.fade_duration = fade_duration;
|
|
|
}
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-pub fn rtc_tick<T>(i2c: &mut T)
|
|
|
-where
|
|
|
- T: _embedded_hal_blocking_i2c_WriteRead
|
|
|
- + _embedded_hal_blocking_i2c_Read
|
|
|
- + _embedded_hal_blocking_i2c_Write,
|
|
|
-{
|
|
|
- static mut STARTUP: bool = true;
|
|
|
- static mut PREV_MINUTE: u32 = 0;
|
|
|
- static mut PREV_HOUR: u32 = 0;
|
|
|
-
|
|
|
- let (second, minute, hour) = ds3231::get_time(DS3231_ADDR, i2c);
|
|
|
- let (weekday, day, month, _, _) = ds3231::get_date(DS3231_ADDR, i2c);
|
|
|
-
|
|
|
- let hour = if ds3231::in_dst(weekday, day, month, hour) {
|
|
|
- (hour + 1) % 12
|
|
|
- } else {
|
|
|
- hour % 12
|
|
|
- };
|
|
|
- let hour = if hour == 0 { 12 } else { hour };
|
|
|
-
|
|
|
- unsafe {
|
|
|
- if STARTUP || PREV_HOUR / 10 != hour / 10 {
|
|
|
- fade_in_out_digit(
|
|
|
- 0,
|
|
|
- if hour / 10 != 0 {
|
|
|
- Some((hour / 10) as usize)
|
|
|
- } else {
|
|
|
- None
|
|
|
- },
|
|
|
- DIGIT_FADE_DURATION_US,
|
|
|
- false,
|
|
|
- )
|
|
|
- }
|
|
|
- if STARTUP || PREV_HOUR % 10 != hour % 10 {
|
|
|
- fade_in_out_digit(
|
|
|
- 1,
|
|
|
- Some((hour % 10) as usize),
|
|
|
- DIGIT_FADE_DURATION_US,
|
|
|
- false
|
|
|
- );
|
|
|
+ match self.hour {
|
|
|
+ Some(prev_hour) if prev_hour % 10 == hour % 10 => {}
|
|
|
+ _ => {
|
|
|
+ self.fade_in_out_digit(
|
|
|
+ 1,
|
|
|
+ Some((hour % 10) as usize),
|
|
|
+ DIGIT_FADE_DURATION_US,
|
|
|
+ false,
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
- if STARTUP || PREV_MINUTE / 10 != minute / 10 {
|
|
|
- fade_in_out_digit(
|
|
|
- 2,
|
|
|
- Some((minute / 10) as usize),
|
|
|
- DIGIT_FADE_DURATION_US,
|
|
|
- false,
|
|
|
- );
|
|
|
+
|
|
|
+ match self.minute {
|
|
|
+ Some(prev_minute) if prev_minute / 10 == minute / 10 => {}
|
|
|
+ _ => {
|
|
|
+ self.fade_in_out_digit(
|
|
|
+ 2,
|
|
|
+ Some((minute / 10) as usize),
|
|
|
+ DIGIT_FADE_DURATION_US,
|
|
|
+ false,
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
- if STARTUP || PREV_MINUTE % 10 != minute % 10 {
|
|
|
- fade_in_out_digit(
|
|
|
- 3,
|
|
|
- Some((minute % 10) as usize),
|
|
|
- DIGIT_FADE_DURATION_US,
|
|
|
- false,
|
|
|
- );
|
|
|
+
|
|
|
+ match self.minute {
|
|
|
+ Some(prev_minute) if prev_minute % 10 == minute % 10 => {}
|
|
|
+ _ => {
|
|
|
+ self.fade_in_out_digit(
|
|
|
+ 3,
|
|
|
+ Some((minute % 10) as usize),
|
|
|
+ DIGIT_FADE_DURATION_US,
|
|
|
+ false,
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- CLOCK.dot.state = match second % 2 {
|
|
|
+ #[cfg(test)]
|
|
|
+ println!(
|
|
|
+ "RTC tick: {}{}:{}{}",
|
|
|
+ hour / 10,
|
|
|
+ hour % 10,
|
|
|
+ minute / 10,
|
|
|
+ minute % 10
|
|
|
+ );
|
|
|
+
|
|
|
+ self.dot.state = match second % 2 {
|
|
|
0 => State::Incrementing,
|
|
|
1 => State::Decrementing,
|
|
|
_ => State::Idle,
|
|
|
};
|
|
|
|
|
|
- PREV_MINUTE = minute;
|
|
|
- PREV_HOUR = hour;
|
|
|
+ #[cfg(test)]
|
|
|
+ println!("RTC tick: dot state is {:?}", self.dot.state);
|
|
|
|
|
|
- STARTUP = false;
|
|
|
+ self.hour = Some(hour);
|
|
|
+ self.minute = Some(minute);
|
|
|
|
|
|
+ #[cfg(not(test))]
|
|
|
free(|cs| {
|
|
|
let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
|
|
|
if let Some(ref mut timer) = timer_ref.deref_mut() {
|
|
@@ -418,236 +396,294 @@ where
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-pub fn refresh_tick<T>(i2c: &mut T)
|
|
|
-where
|
|
|
- T: _embedded_hal_blocking_i2c_WriteRead
|
|
|
- + _embedded_hal_blocking_i2c_Read
|
|
|
- + _embedded_hal_blocking_i2c_Write,
|
|
|
-{
|
|
|
- let mut pending_refresh: bool = false;
|
|
|
- unsafe {
|
|
|
- let ticks = CLOCK.fade_duration / REFRESH_RATE_HZ;
|
|
|
+ pub fn refresh_tick(&mut self) -> bool {
|
|
|
+ let mut pending_refresh: bool = false;
|
|
|
+
|
|
|
+ let mut update_fn = |digit: &mut Digit, min: u32, max: u32, steps: u32| {
|
|
|
+ match digit.state {
|
|
|
+ State::Incrementing => {
|
|
|
+ if digit.value >= max {
|
|
|
+ digit.value = max;
|
|
|
+ digit.state = State::Idle;
|
|
|
+ } else {
|
|
|
+ digit.value = digit.value.saturating_add(steps).clamp(min, max);
|
|
|
+ digit.updated = true;
|
|
|
+ pending_refresh = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ State::Decrementing => {
|
|
|
+ if digit.value <= min {
|
|
|
+ digit.value = min;
|
|
|
+ digit.state = State::Idle;
|
|
|
+ } else {
|
|
|
+ digit.value = digit.value.saturating_sub(steps).clamp(min, max);
|
|
|
+ digit.updated = true;
|
|
|
+ pending_refresh = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ State::Idle => (),
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ let ticks = self.fade_duration / REFRESH_RATE_HZ;
|
|
|
let steps = ((DIGIT_MAX_BRIGHTNESS - DIGIT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
|
|
|
|
|
|
- CLOCK.tubes.iter_mut().for_each(|tube| {
|
|
|
+ #[cfg(not(test))]
|
|
|
+ self.tubes.iter_mut().for_each(|tube| {
|
|
|
tube.digits.iter_mut().for_each(|digit| {
|
|
|
- match digit.state {
|
|
|
- State::Incrementing => {
|
|
|
- if digit.value >= DIGIT_MAX_BRIGHTNESS {
|
|
|
- digit.value = DIGIT_MAX_BRIGHTNESS;
|
|
|
- digit.state = State::Idle;
|
|
|
- } else {
|
|
|
- digit.value = digit
|
|
|
- .value
|
|
|
- .saturating_add(steps)
|
|
|
- .clamp(DIGIT_MIN_BRIGHTNESS, DIGIT_MAX_BRIGHTNESS);
|
|
|
- digit.updated = true;
|
|
|
- pending_refresh = true;
|
|
|
- }
|
|
|
- }
|
|
|
- State::Decrementing => {
|
|
|
- if digit.value <= DIGIT_MIN_BRIGHTNESS {
|
|
|
- digit.value = DIGIT_MIN_BRIGHTNESS;
|
|
|
- digit.state = State::Idle;
|
|
|
- } else {
|
|
|
- digit.value = digit
|
|
|
- .value
|
|
|
- .saturating_sub(steps)
|
|
|
- .clamp(DIGIT_MIN_BRIGHTNESS, DIGIT_MAX_BRIGHTNESS);
|
|
|
- digit.updated = true;
|
|
|
- pending_refresh = true;
|
|
|
- }
|
|
|
- }
|
|
|
- State::Idle => (),
|
|
|
- };
|
|
|
+ update_fn(digit, DIGIT_MIN_BRIGHTNESS, DIGIT_MAX_BRIGHTNESS, steps);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
+ #[cfg(test)]
|
|
|
+ for (t, tube) in self.tubes.iter_mut().enumerate() {
|
|
|
+ for (d, digit) in tube.digits.iter_mut().enumerate() {
|
|
|
+ update_fn(digit, DIGIT_MIN_BRIGHTNESS, DIGIT_MAX_BRIGHTNESS, steps);
|
|
|
+
|
|
|
+ if digit.updated {
|
|
|
+ println!(
|
|
|
+ "Refresh tick: updated tube {} digit {} to value {}",
|
|
|
+ t, d, digit.value
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Handle dot
|
|
|
let steps = ((DOT_MAX_BRIGHTNESS - DOT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
|
|
|
- match CLOCK.dot.state {
|
|
|
- State::Incrementing => {
|
|
|
- CLOCK.dot.value = CLOCK
|
|
|
- .dot
|
|
|
- .value
|
|
|
- .saturating_add(steps)
|
|
|
- .clamp(DOT_MIN_BRIGHTNESS, DOT_MAX_BRIGHTNESS);
|
|
|
- if CLOCK.dot.value >= DOT_MAX_BRIGHTNESS {
|
|
|
- CLOCK.dot.value = DOT_MAX_BRIGHTNESS;
|
|
|
- CLOCK.dot.state = State::Idle;
|
|
|
+ update_fn(&mut self.dot, DOT_MIN_BRIGHTNESS, DOT_MAX_BRIGHTNESS, steps);
|
|
|
+
|
|
|
+ #[cfg(test)]
|
|
|
+ if self.dot.updated {
|
|
|
+ println!("Refresh tick: updated dot to value {}", self.dot.value);
|
|
|
+ }
|
|
|
+
|
|
|
+ if pending_refresh {
|
|
|
+ self.distribute_pwm();
|
|
|
+ }
|
|
|
+
|
|
|
+ pending_refresh
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn rng_tick<T>(&mut self, _i2c: &mut T)
|
|
|
+ where
|
|
|
+ T: _embedded_hal_blocking_i2c_WriteRead
|
|
|
+ + _embedded_hal_blocking_i2c_Read
|
|
|
+ + _embedded_hal_blocking_i2c_Write,
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn write_i2c<T>(&mut self, i2c: &mut T)
|
|
|
+ where
|
|
|
+ T: _embedded_hal_blocking_i2c_WriteRead
|
|
|
+ + _embedded_hal_blocking_i2c_Read
|
|
|
+ + _embedded_hal_blocking_i2c_Write,
|
|
|
+ {
|
|
|
+ for (t, tube) in self.tubes.iter().enumerate() {
|
|
|
+ for (d, digit) in tube.digits.iter().enumerate() {
|
|
|
+ if digit.updated {
|
|
|
+ pca9685::set_digit(
|
|
|
+ i2c,
|
|
|
+ TUBE_MAPPING.driver[t].digit[d].address,
|
|
|
+ TUBE_MAPPING.driver[t].digit[d].pin,
|
|
|
+ digit.pwm_start,
|
|
|
+ digit.pwm_end,
|
|
|
+ );
|
|
|
}
|
|
|
- CLOCK.dot.updated = true;
|
|
|
- pending_refresh = true;
|
|
|
}
|
|
|
- State::Decrementing => {
|
|
|
- CLOCK.dot.value = CLOCK
|
|
|
- .dot
|
|
|
- .value
|
|
|
- .saturating_sub(steps)
|
|
|
- .clamp(DOT_MIN_BRIGHTNESS, DOT_MAX_BRIGHTNESS);
|
|
|
- if CLOCK.dot.value <= DOT_MIN_BRIGHTNESS {
|
|
|
- CLOCK.dot.value = DOT_MIN_BRIGHTNESS;
|
|
|
- CLOCK.dot.state = State::Idle;
|
|
|
+ }
|
|
|
+
|
|
|
+ if self.dot.updated {
|
|
|
+ pca9685::set_digit(
|
|
|
+ i2c,
|
|
|
+ TUBE_MAPPING.dot_address,
|
|
|
+ TUBE_MAPPING.dot_pin,
|
|
|
+ self.dot.pwm_start,
|
|
|
+ self.dot.pwm_end,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn fade_in_out_digit(
|
|
|
+ &mut self,
|
|
|
+ tube: usize,
|
|
|
+ digit: Option<usize>,
|
|
|
+ fade_duration: u32,
|
|
|
+ refresh_cmd: bool,
|
|
|
+ ) {
|
|
|
+ // If the tube is in the middle of a refresh sequence and a call comes
|
|
|
+ // in to update the tube digit (for time), override the last value of
|
|
|
+ // the refresh sequence with the new digit.
|
|
|
+ if self.tubes[tube].refresh_active && !refresh_cmd {
|
|
|
+ self.tubes[tube].refresh_last_digit = digit;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Dont update if actively refreshing tube unless RngUpdate is set
|
|
|
+ if (!self.tubes[tube].refresh_active && !refresh_cmd) || refresh_cmd {
|
|
|
+ // Fade out all digits
|
|
|
+ for digit in 0..NUM_DIGITS {
|
|
|
+ if self.tubes[tube].digits[digit].value != DIGIT_MIN_BRIGHTNESS {
|
|
|
+ self.tubes[tube].digits[digit].state = State::Decrementing;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Fade in the specified digit
|
|
|
+ if let Some(digit) = digit {
|
|
|
+ if self.tubes[tube].digits[digit].value != DIGIT_MAX_BRIGHTNESS {
|
|
|
+ self.tubes[tube].digits[digit].state = State::Incrementing;
|
|
|
}
|
|
|
- CLOCK.dot.updated = true;
|
|
|
- pending_refresh = true;
|
|
|
}
|
|
|
- State::Idle => (),
|
|
|
+
|
|
|
+ self.tubes[tube].last_active_digit = digit;
|
|
|
+ self.fade_duration = fade_duration;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if !pending_refresh {
|
|
|
- free(|cs| {
|
|
|
- let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
|
|
|
- if let Some(ref mut timer) = timer_ref.deref_mut() {
|
|
|
- timer.unlisten(Event::TimeOut);
|
|
|
- }
|
|
|
- })
|
|
|
- } else {
|
|
|
- compute_pwm_offset();
|
|
|
-
|
|
|
- unsafe {
|
|
|
- for (t, tube) in CLOCK.tubes.iter().enumerate() {
|
|
|
- for (d, digit) in tube.digits.iter().enumerate() {
|
|
|
- if digit.updated {
|
|
|
- pca9685::set_digit(
|
|
|
- TUBE_MAPPING.driver[t].digit[d].address,
|
|
|
- i2c,
|
|
|
- TUBE_MAPPING.driver[t].digit[d].pin,
|
|
|
- digit.pwm_start,
|
|
|
- digit.pwm_end,
|
|
|
- );
|
|
|
+ // In the event that there are multiple PWM outputs at less than 100% duty cycle,
|
|
|
+ // stagger the start time of each PWM to reduce the switch on surge current. If the
|
|
|
+ // duty cycle is greater than 100%, distribute the PWM outputs as much as possible
|
|
|
+ // to keep the current consumption at a minimum.
|
|
|
+ fn distribute_pwm(&mut self) {
|
|
|
+ let mut last_pwm: u32 = 0;
|
|
|
+ let mut incrementing: bool = true;
|
|
|
+
|
|
|
+ // Closure to avoid duplicate code
|
|
|
+ let mut update_digit = |digit: &mut Digit| {
|
|
|
+ if digit.value == DIGIT_MIN_BRIGHTNESS {
|
|
|
+ digit.pwm_start = 0;
|
|
|
+ digit.pwm_end = 0;
|
|
|
+ } else if digit.value == DIGIT_MAX_BRIGHTNESS {
|
|
|
+ digit.pwm_start = 0;
|
|
|
+ digit.pwm_end = DIGIT_MAX_BRIGHTNESS;
|
|
|
+ } else {
|
|
|
+ if incrementing {
|
|
|
+ if last_pwm + digit.value > DIGIT_MAX_BRIGHTNESS {
|
|
|
+ digit.pwm_start = DIGIT_MAX_BRIGHTNESS - digit.value;
|
|
|
+ digit.pwm_end = DIGIT_MAX_BRIGHTNESS;
|
|
|
+ last_pwm = digit.pwm_start;
|
|
|
+ incrementing = false;
|
|
|
+ } else {
|
|
|
+ digit.pwm_start = last_pwm;
|
|
|
+ digit.pwm_end = digit.pwm_start + digit.value;
|
|
|
+ last_pwm = digit.pwm_end;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if last_pwm - DIGIT_MIN_BRIGHTNESS < digit.value {
|
|
|
+ digit.pwm_start = DIGIT_MIN_BRIGHTNESS;
|
|
|
+ digit.pwm_end = digit.pwm_start + digit.value;
|
|
|
+ last_pwm = digit.pwm_end;
|
|
|
+ incrementing = true;
|
|
|
+ } else {
|
|
|
+ digit.pwm_end = last_pwm;
|
|
|
+ digit.pwm_start = digit.pwm_end - digit.value;
|
|
|
+ last_pwm = digit.pwm_start;
|
|
|
}
|
|
|
}
|
|
|
+ digit.updated = true;
|
|
|
}
|
|
|
- if CLOCK.dot.updated {
|
|
|
- pca9685::set_digit(
|
|
|
- TUBE_MAPPING.dot_address,
|
|
|
- i2c,
|
|
|
- TUBE_MAPPING.dot_pin,
|
|
|
- CLOCK.dot.pwm_start,
|
|
|
- CLOCK.dot.pwm_end,
|
|
|
- );
|
|
|
+ };
|
|
|
+
|
|
|
+ #[cfg(not(test))]
|
|
|
+ self.tubes.iter_mut().for_each(|tube| {
|
|
|
+ tube.digits.iter_mut().for_each(|digit| {
|
|
|
+ update_digit(digit);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ #[cfg(test)]
|
|
|
+ for (t, tube) in self.tubes.iter_mut().enumerate() {
|
|
|
+ for (d, digit) in tube.digits.iter_mut().enumerate() {
|
|
|
+ update_digit(digit);
|
|
|
+
|
|
|
+ if digit.updated {
|
|
|
+ println!(
|
|
|
+ "Distribute PWM: tube {} digit {} start {} end {}",
|
|
|
+ t, d, digit.pwm_start, digit.pwm_end
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- free(|cs| {
|
|
|
- let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
|
|
|
- if let Some(ref mut timer) = timer_ref.deref_mut() {
|
|
|
- timer.clear_interrupt(Event::TimeOut);
|
|
|
- }
|
|
|
- })
|
|
|
+ update_digit(&mut self.dot);
|
|
|
+
|
|
|
+ #[cfg(test)]
|
|
|
+ println!(
|
|
|
+ "Distribute PWM: dot start {} end {}",
|
|
|
+ self.dot.pwm_start, self.dot.pwm_end
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-pub fn rng_tick<T>(_i2c: &mut T)
|
|
|
+pub fn rtc_interrupt<T>(i2c: &mut T)
|
|
|
where
|
|
|
T: _embedded_hal_blocking_i2c_WriteRead
|
|
|
+ _embedded_hal_blocking_i2c_Read
|
|
|
+ _embedded_hal_blocking_i2c_Write,
|
|
|
{
|
|
|
+ let (second, minute, hour) = ds3231::get_time(DS3231_ADDR, i2c);
|
|
|
+ let (weekday, day, month, _, _) = ds3231::get_date(DS3231_ADDR, i2c);
|
|
|
+
|
|
|
+ let hour = if ds3231::in_dst(weekday, day, month, hour) {
|
|
|
+ (hour + 1) % 12
|
|
|
+ } else {
|
|
|
+ hour % 12
|
|
|
+ };
|
|
|
+ let hour = if hour == 0 { 12 } else { hour };
|
|
|
+
|
|
|
+ free(|cs| {
|
|
|
+ let mut clock_ref = CLOCK.borrow(cs).borrow_mut();
|
|
|
+ let clock = clock_ref.deref_mut();
|
|
|
+ clock.rtc_tick(second, minute, hour);
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
-// In the event that there are multiple PWM outputs at less than 100% duty cycle,
|
|
|
-// stagger the start time of each PWM to reduce the switch on surge current.
|
|
|
-fn compute_pwm_offset() {
|
|
|
- // let mut active_digits: u32 = 0;
|
|
|
- // let mut total_on_time: u32 = 0;
|
|
|
- // let mut last_pwm_end: u32 = 0;
|
|
|
+pub fn refresh_interrupt<T>(i2c: &mut T)
|
|
|
+where
|
|
|
+ T: _embedded_hal_blocking_i2c_WriteRead
|
|
|
+ + _embedded_hal_blocking_i2c_Read
|
|
|
+ + _embedded_hal_blocking_i2c_Write,
|
|
|
+{
|
|
|
+ free(|cs| {
|
|
|
+ let mut clock_ref = CLOCK.borrow(cs).borrow_mut();
|
|
|
+ let clock = clock_ref.deref_mut();
|
|
|
+ let refresh = clock.refresh_tick();
|
|
|
+ if refresh {
|
|
|
+ clock.write_i2c(i2c);
|
|
|
+ free(|cs| {
|
|
|
+ let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
|
|
|
+ if let Some(ref mut timer) = timer_ref.deref_mut() {
|
|
|
+ timer.clear_interrupt(Event::TimeOut);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ free(|cs| {
|
|
|
+ let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
|
|
|
+ if let Some(ref mut timer) = timer_ref.deref_mut() {
|
|
|
+ timer.unlisten(Event::TimeOut);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
|
|
|
- unsafe {
|
|
|
- CLOCK.tubes.iter_mut().for_each(|tube| {
|
|
|
- tube.digits.iter_mut().for_each(|digit| {
|
|
|
- digit.pwm_start = 0;
|
|
|
- digit.pwm_end = digit.value;
|
|
|
- });
|
|
|
- });
|
|
|
- CLOCK.dot.pwm_start = 0;
|
|
|
- CLOCK.dot.pwm_end = CLOCK.dot.value;
|
|
|
- }
|
|
|
+#[cfg(test)]
|
|
|
+mod test {
|
|
|
+ use super::*;
|
|
|
+ use std::println;
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn pwm_calc_test() {
|
|
|
+ let mut clock: Clock = Clock::default();
|
|
|
|
|
|
- // Determine the number of active outputs as well as the total on-time across all outputs.
|
|
|
- // Ignore outputs that are off (min) or fully on (max) as they have no surge impact.
|
|
|
- // unsafe {
|
|
|
- // CLOCK.tubes.iter().for_each(|tube| {
|
|
|
- // tube.digits.iter().for_each(|digit| {
|
|
|
- // if digit.value != DIGIT_MAX_BRIGHTNESS && digit.value != DIGIT_MIN_BRIGHTNESS {
|
|
|
- // active_digits = active_digits + 1;
|
|
|
- // total_on_time = total_on_time + digit.value;
|
|
|
- // }
|
|
|
- // });
|
|
|
- // });
|
|
|
- // if CLOCK.dot.value != DIGIT_MAX_BRIGHTNESS && CLOCK.dot.value != DIGIT_MIN_BRIGHTNESS {
|
|
|
- // active_digits = active_digits + 1;
|
|
|
- // total_on_time = total_on_time + CLOCK.dot.value;
|
|
|
- // }
|
|
|
-
|
|
|
- // // If the total on-time across all outputs is less than one PWM period, stagger each
|
|
|
- // // output such that the rise of one pulse begins at the end of the previous pulse.
|
|
|
- // if total_on_time <= DIGIT_MAX_BRIGHTNESS {
|
|
|
- // CLOCK.tubes.iter_mut().for_each(|tube| {
|
|
|
- // tube.digits.iter_mut().for_each(|digit| {
|
|
|
- // if digit.value == DIGIT_MIN_BRIGHTNESS {
|
|
|
- // digit.pwm_start = 0;
|
|
|
- // digit.pwm_end = 0;
|
|
|
- // } else if digit.value == DIGIT_MAX_BRIGHTNESS {
|
|
|
- // digit.pwm_start = 0;
|
|
|
- // digit.pwm_end = DIGIT_MAX_BRIGHTNESS;
|
|
|
- // } else {
|
|
|
- // digit.pwm_start = last_pwm_end;
|
|
|
- // digit.pwm_end = last_pwm_end + digit.value;
|
|
|
- // last_pwm_end = digit.pwm_end;
|
|
|
- // digit.updated = true;
|
|
|
- // }
|
|
|
- // });
|
|
|
- // });
|
|
|
-
|
|
|
- // if CLOCK.dot.value == DIGIT_MIN_BRIGHTNESS {
|
|
|
- // CLOCK.dot.pwm_start = 0;
|
|
|
- // CLOCK.dot.pwm_end = 0;
|
|
|
- // } else if CLOCK.dot.value == DIGIT_MAX_BRIGHTNESS {
|
|
|
- // CLOCK.dot.pwm_start = 0;
|
|
|
- // CLOCK.dot.pwm_end = DIGIT_MAX_BRIGHTNESS;
|
|
|
- // } else {
|
|
|
- // CLOCK.dot.pwm_start = last_pwm_end;
|
|
|
- // CLOCK.dot.pwm_end = last_pwm_end + CLOCK.dot.value;
|
|
|
- // CLOCK.dot.updated = true;
|
|
|
- // }
|
|
|
- // } else {
|
|
|
- // // Compute the amount of overlap between all outputs
|
|
|
- // let overlap = (total_on_time - DIGIT_MAX_BRIGHTNESS) / (active_digits - 1);
|
|
|
-
|
|
|
- // // Compute the staggered output period for each output
|
|
|
- // CLOCK.tubes.iter_mut().for_each(|tube| {
|
|
|
- // tube.digits.iter_mut().for_each(|digit| {
|
|
|
- // if digit.value == DIGIT_MIN_BRIGHTNESS {
|
|
|
- // digit.pwm_start = 0;
|
|
|
- // digit.pwm_end = 0;
|
|
|
- // } else if digit.value == DIGIT_MAX_BRIGHTNESS {
|
|
|
- // digit.pwm_start = 0;
|
|
|
- // digit.pwm_end = DIGIT_MAX_BRIGHTNESS;
|
|
|
- // } else {
|
|
|
- // digit.pwm_start = last_pwm_end.saturating_sub(overlap);
|
|
|
- // digit.pwm_end = digit.pwm_start + digit.value;
|
|
|
- // last_pwm_end = digit.pwm_end;
|
|
|
- // digit.updated = true;
|
|
|
- // }
|
|
|
- // });
|
|
|
- // });
|
|
|
-
|
|
|
- // if CLOCK.dot.value == DIGIT_MIN_BRIGHTNESS {
|
|
|
- // CLOCK.dot.pwm_start = 0;
|
|
|
- // CLOCK.dot.pwm_end = 0;
|
|
|
- // } else if CLOCK.dot.value == DIGIT_MAX_BRIGHTNESS {
|
|
|
- // CLOCK.dot.pwm_start = 0;
|
|
|
- // CLOCK.dot.pwm_end = DIGIT_MAX_BRIGHTNESS;
|
|
|
- // } else {
|
|
|
- // CLOCK.dot.pwm_start = last_pwm_end.saturating_sub(overlap);
|
|
|
- // CLOCK.dot.pwm_end = CLOCK.dot.pwm_start + CLOCK.dot.value;
|
|
|
- // CLOCK.dot.updated = true;
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
+ clock.rtc_tick(10, 23, 12);
|
|
|
+
|
|
|
+ for tick in 0..1005 {
|
|
|
+ println!("\nRefresh tick: {}", tick);
|
|
|
+ if !clock.refresh_tick() {
|
|
|
+ println!("Refresh halted");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|