|
@@ -1,9 +1,11 @@
|
|
|
-use stm32l4xx_hal::prelude::{
|
|
|
- _embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write,
|
|
|
- _embedded_hal_blocking_i2c_WriteRead,
|
|
|
+use stm32l4xx_hal::{
|
|
|
+ prelude::{
|
|
|
+ _embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write,
|
|
|
+ _embedded_hal_blocking_i2c_WriteRead,
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
-use crate::ds3231;
|
|
|
+use crate::{ds3231, pca9685};
|
|
|
|
|
|
pub const DS3231_ADDR: u8 = 0x68;
|
|
|
pub const TUSB322_ADDR: u8 = 0x47;
|
|
@@ -305,19 +307,6 @@ impl Clock {
|
|
|
|
|
|
static mut CLOCK: Clock = Clock::default();
|
|
|
|
|
|
-// pub fn set_digit(tube: usize, digit: usize, pwm_start: u32, pwm_end: u32) {
|
|
|
-// assert!(pwm_end >= pwm_start);
|
|
|
-// assert!(tube < NUM_TUBES);
|
|
|
-// assert!(digit < NUM_DIGITS);
|
|
|
-// unsafe {
|
|
|
-// CLOCK.tubes[tube].digits[digit].current_state = DigitState::Incrementing;
|
|
|
-// CLOCK.tubes[tube].digits[digit].pwm_start = pwm_start;
|
|
|
-// CLOCK.tubes[tube].digits[digit].pwm_end = pwm_end;
|
|
|
-// CLOCK.tubes[tube].digits[digit].value = pwm_end - pwm_start;
|
|
|
-// CLOCK.tubes[tube].digits[digit].updated = true;
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
pub fn fade_in_out_digit(tube: usize, digit: Option<usize>, fade_duration: u32, refresh_cmd: bool) {
|
|
|
// assert!(tube < NUM_TUBES);
|
|
|
// assert!(Some(digit) < NUM_DIGITS);
|
|
@@ -354,33 +343,33 @@ pub fn refresh_frame() {
|
|
|
let mut pending_refresh: bool = false;
|
|
|
unsafe {
|
|
|
let ticks = CLOCK.fade_duration / REFRESH_RATE_US;
|
|
|
- for tube in 0..NUM_TUBES {
|
|
|
- let steps = ((DIGIT_MAX_BRIGHTNESS - DIGIT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
|
|
|
- for digit in 0..NUM_DIGITS {
|
|
|
- let mut d = &mut CLOCK.tubes[tube].digits[digit];
|
|
|
- match d.current_state {
|
|
|
+ let steps = ((DIGIT_MAX_BRIGHTNESS - DIGIT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
|
|
|
+ CLOCK.tubes.iter_mut().for_each(|tube| {
|
|
|
+ tube.digits.iter_mut().for_each(|digit| {
|
|
|
+ match digit.current_state {
|
|
|
DigitState::Incrementing => {
|
|
|
- d.value = d.value.saturating_add(steps);
|
|
|
- if d.value >= DIGIT_MAX_BRIGHTNESS {
|
|
|
- d.value = DIGIT_MAX_BRIGHTNESS;
|
|
|
- d.current_state = DigitState::Idle;
|
|
|
+ digit.value = digit.value.saturating_add(steps);
|
|
|
+ if digit.value >= DIGIT_MAX_BRIGHTNESS {
|
|
|
+ digit.value = DIGIT_MAX_BRIGHTNESS;
|
|
|
+ digit.current_state = DigitState::Idle;
|
|
|
}
|
|
|
- d.updated = true;
|
|
|
+ digit.updated = true;
|
|
|
pending_refresh = true;
|
|
|
}
|
|
|
DigitState::Decrementing => {
|
|
|
- d.value = d.value.saturating_sub(steps);
|
|
|
- if d.value <= DIGIT_MIN_BRIGHTNESS {
|
|
|
- d.value = DIGIT_MIN_BRIGHTNESS;
|
|
|
- d.current_state = DigitState::Idle;
|
|
|
+ digit.value = digit.value.saturating_sub(steps);
|
|
|
+ if digit.value <= DIGIT_MIN_BRIGHTNESS {
|
|
|
+ digit.value = DIGIT_MIN_BRIGHTNESS;
|
|
|
+ digit.current_state = DigitState::Idle;
|
|
|
}
|
|
|
- d.updated = true;
|
|
|
+ digit.updated = true;
|
|
|
pending_refresh = true;
|
|
|
}
|
|
|
DigitState::Idle => (),
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ };
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
// Handle dot
|
|
|
let steps = ((DOT_MAX_BRIGHTNESS - DOT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
|
|
|
match CLOCK.dot.current_state {
|
|
@@ -483,6 +472,30 @@ where
|
|
|
+ _embedded_hal_blocking_i2c_Read
|
|
|
+ _embedded_hal_blocking_i2c_Write,
|
|
|
{
|
|
|
+ compute_pwm_offset();
|
|
|
+
|
|
|
+ unsafe {
|
|
|
+ for (t, tube) in CLOCK.tubes.iter().enumerate() {
|
|
|
+ for (d, digit) in tube.digits.iter().filter(|d| d.updated).enumerate() {
|
|
|
+ pca9685::set_digit(
|
|
|
+ TUBE_MAPPING.driver[t].digit[d].address,
|
|
|
+ i2c,
|
|
|
+ TUBE_MAPPING.driver[t].digit[d].pin,
|
|
|
+ digit.pwm_start,
|
|
|
+ digit.pwm_end,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if CLOCK.dot.updated {
|
|
|
+ pca9685::set_digit(
|
|
|
+ TUBE_MAPPING.dot_address,
|
|
|
+ i2c,
|
|
|
+ TUBE_MAPPING.dot_pin,
|
|
|
+ CLOCK.dot.pwm_start,
|
|
|
+ CLOCK.dot.pwm_end,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
pub fn rng_tick<T>(i2c: &mut T)
|
|
@@ -492,3 +505,95 @@ where
|
|
|
+ _embedded_hal_blocking_i2c_Write,
|
|
|
{
|
|
|
}
|
|
|
+
|
|
|
+// 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;
|
|
|
+
|
|
|
+ // 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
|
|
|
+ // int overlap = (totalOnTime - PCA9685_Max_Brightness) / (validOutputs - 1);
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|