Browse Source

Port over tube refresh helper functions

Kevin Lee 2 years ago
parent
commit
42869127bb
1 changed files with 175 additions and 8 deletions
  1. 175 8
      Nixie_Firmware_Rust/src/nixie.rs

+ 175 - 8
Nixie_Firmware_Rust/src/nixie.rs

@@ -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
+    }
+}