|
@@ -1,3 +1,5 @@
|
|
|
+use core::future::pending;
|
|
|
+
|
|
|
pub const DS3231_ADDR: u8 = 0x68;
|
|
|
pub const TUSB322_ADDR: u8 = 0x47;
|
|
|
pub const PCA9685_ADDR_1: u8 = 0x41;
|
|
@@ -15,6 +17,24 @@ const MAP_DOT_PIN: u8 = 15;
|
|
|
const MAP_ADDR: usize = 0;
|
|
|
const MAP_PIN: usize = 1;
|
|
|
|
|
|
+const DIGIT_FADE_DURATION_US: u32 = 1_000_000;
|
|
|
+const REFRESH_RATE_US: u32 = 1000;
|
|
|
+
|
|
|
+const DIGIT_RNG_FADE_DURATION_US: u32 = 200_000;
|
|
|
+const DIGIT_RNG_FADE_ITERATIONS: usize = 20;
|
|
|
+const DIGIT_RNG_REFRESH_INTERVAL: usize = 60;
|
|
|
+const DIGIT_RNG_REFRESH_VARIANCE: usize = 30;
|
|
|
+
|
|
|
+const DOT_MIN_BRIGHTNESS: u32 = 256;
|
|
|
+const DOT_MAX_BRIGHTNESS: u32 = 640;
|
|
|
+const DOT_FADE_DURATION_US: u32 = 1_000_000;
|
|
|
+
|
|
|
+const DIGIT_MAX_BRIGHTNESS: u32 = 4096;
|
|
|
+const DIGIT_MIN_BRIGHTNESS: u32 = 0;
|
|
|
+
|
|
|
+const NUM_TUBES: usize = 4;
|
|
|
+const NUM_DIGITS: usize = 10;
|
|
|
+
|
|
|
struct DigitToPin {
|
|
|
address: u8,
|
|
|
pin: usize,
|
|
@@ -215,9 +235,11 @@ static TUBE_MAPPING: PwmOutputMap = {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+#[derive(PartialEq)]
|
|
|
enum DigitState {
|
|
|
+ Idle,
|
|
|
+ Incrementing,
|
|
|
Decrementing,
|
|
|
- Incrementing
|
|
|
}
|
|
|
|
|
|
struct Digit {
|
|
@@ -228,14 +250,159 @@ struct Digit {
|
|
|
updated: bool,
|
|
|
}
|
|
|
|
|
|
+impl Digit {
|
|
|
+ const fn default() -> Self {
|
|
|
+ Self {
|
|
|
+ current_state: DigitState::Idle,
|
|
|
+ value: 0,
|
|
|
+ pwm_start: 0,
|
|
|
+ pwm_end: 0,
|
|
|
+ updated: false,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
struct Tube {
|
|
|
- digits: [Digit; 10],
|
|
|
- last_active: usize,
|
|
|
- refresh_last: usize,
|
|
|
- active: bool,
|
|
|
- fade_duration: u32,
|
|
|
+ digits: [Digit; NUM_DIGITS],
|
|
|
+ last_active_digit: Option<usize>,
|
|
|
+ refresh_last_digit: Option<usize>,
|
|
|
+ refresh_active: bool,
|
|
|
+}
|
|
|
+
|
|
|
+impl Tube {
|
|
|
+ const fn default() -> Self {
|
|
|
+ const DIGIT_INIT: Digit = Digit::default();
|
|
|
+ Self {
|
|
|
+ digits: [DIGIT_INIT; 10],
|
|
|
+ last_active_digit: None,
|
|
|
+ refresh_last_digit: None,
|
|
|
+ refresh_active: false,
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
struct Clock {
|
|
|
- tubes: [Tube; 4],
|
|
|
-}
|
|
|
+ tubes: [Tube; NUM_TUBES],
|
|
|
+ dot: Digit,
|
|
|
+ fade_duration: u32,
|
|
|
+}
|
|
|
+
|
|
|
+impl Clock {
|
|
|
+ const fn default() -> Self {
|
|
|
+ const TUBE_INIT: Tube = Tube::default();
|
|
|
+ Self {
|
|
|
+ tubes: [TUBE_INIT; NUM_TUBES],
|
|
|
+ dot: Digit::default(),
|
|
|
+ fade_duration: 0,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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);
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ CLOCK.tubes[tube].digits[digit].current_state = DigitState::Decrementing;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Fade in the specified digit
|
|
|
+ if let Some(digit) = digit {
|
|
|
+ CLOCK.tubes[tube].digits[digit].current_state = DigitState::Incrementing;
|
|
|
+ }
|
|
|
+
|
|
|
+ CLOCK.tubes[tube].last_active_digit = digit;
|
|
|
+ CLOCK.fade_duration = fade_duration;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO! trigger refresh timer
|
|
|
+}
|
|
|
+
|
|
|
+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 {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ d.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;
|
|
|
+ }
|
|
|
+ d.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 {
|
|
|
+ DigitState::Incrementing => {
|
|
|
+ CLOCK.dot.value = CLOCK.dot.value.saturating_add(steps);
|
|
|
+ if CLOCK.dot.value >= DIGIT_MAX_BRIGHTNESS {
|
|
|
+ CLOCK.dot.value = DIGIT_MAX_BRIGHTNESS;
|
|
|
+ CLOCK.dot.current_state = DigitState::Idle;
|
|
|
+ }
|
|
|
+ CLOCK.dot.updated = true;
|
|
|
+ pending_refresh = true;
|
|
|
+ },
|
|
|
+ DigitState::Decrementing => {
|
|
|
+ CLOCK.dot.value = CLOCK.dot.value.saturating_sub(steps);
|
|
|
+ if CLOCK.dot.value >= DIGIT_MIN_BRIGHTNESS {
|
|
|
+ CLOCK.dot.value = DIGIT_MIN_BRIGHTNESS;
|
|
|
+ CLOCK.dot.current_state = DigitState::Idle;
|
|
|
+ }
|
|
|
+ CLOCK.dot.updated = true;
|
|
|
+ pending_refresh = true;
|
|
|
+ },
|
|
|
+ DigitState::Idle => (),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if pending_refresh {
|
|
|
+ // TODO! trigger refresh timer
|
|
|
+ }
|
|
|
+}
|