Browse Source

Preliminary cycle implementation

Kevin Lee 2 years ago
parent
commit
f7f411f0cd
2 changed files with 222 additions and 110 deletions
  1. 43 10
      Nixie_Firmware_Rust/src/main.rs
  2. 179 100
      Nixie_Firmware_Rust/src/nixie.rs

+ 43 - 10
Nixie_Firmware_Rust/src/main.rs

@@ -14,7 +14,7 @@ use cortex_m::{interrupt::free, interrupt::Mutex, peripheral::NVIC};
 use cortex_m_rt::entry;
 use stm32l4xx_hal::{
     delay::Delay,
-    device::{I2C1, TIM2},
+    device::{I2C1, TIM2, TIM7},
     gpio::{
         Alternate, Edge, Floating, Input, OpenDrain, Output, PullUp, PushPull, AF4, PA3, PB5, PC15,
     },
@@ -50,7 +50,8 @@ static I2C: Mutex<
         >,
     >,
 > = Mutex::new(RefCell::new(None));
-static REFRESH_TIMER: Mutex<RefCell<Option<Timer<TIM2>>>> = Mutex::new(RefCell::new(None));
+static FPS_TIMER: Mutex<RefCell<Option<Timer<TIM2>>>> = Mutex::new(RefCell::new(None));
+static CYCLE_TIMER: Mutex<RefCell<Option<Timer<TIM7>>>> = Mutex::new(RefCell::new(None));
 
 // unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
 //     core::slice::from_raw_parts(
@@ -180,9 +181,7 @@ fn main() -> ! {
     rtc_int.trigger_on_edge(&mut dp.EXTI, Edge::FALLING);
 
     // Configure NVIC mask to enable interrupt source
-    unsafe {
-        NVIC::unmask(Interrupt::EXTI9_5);
-    }
+    unsafe { NVIC::unmask(Interrupt::EXTI9_5); }
 
     // Store RTC interrupt in static singleton so that interrupt has access to it
     free(|cs| {
@@ -215,7 +214,7 @@ fn main() -> ! {
     );
 
     // Initialize the PCA9685 display refresh timer
-    let refresh_timer = Timer::tim2(
+    let fps_timer = Timer::tim2(
         dp.TIM2,
         nixie::REFRESH_RATE_HZ.hz(),
         clocks,
@@ -227,7 +226,7 @@ fn main() -> ! {
 
     // Save display refresh timer in static singleton so that interrupt has access to it
     free(|cs| {
-        REFRESH_TIMER.borrow(cs).replace(Some(refresh_timer));
+        FPS_TIMER.borrow(cs).replace(Some(fps_timer));
     });
 
     // Small delay to ensure that PCA9685 is fully powered on before writing to it
@@ -244,10 +243,39 @@ fn main() -> ! {
         I2C.borrow(cs).replace(Some(i2c));
     });
 
+    let cycle_timer = Timer::tim7(
+        dp.TIM7,
+        (1_000_000 / nixie::CYCLE_FADE_DURATION_MS).hz(),
+        clocks,
+        &mut rcc.apb1r1,
+    );
+
+    unsafe { NVIC::unmask(Interrupt::TIM7) };
+
+    // Save display refresh timer in static singleton so that interrupt has access to it
+    free(|cs| {
+        CYCLE_TIMER.borrow(cs).replace(Some(cycle_timer));
+    });
+
+    // let rng = dp.RNG.enable(&mut rcc.ahb2, clocks);
+
     // Enable the high voltage power supply last
     hv_enable.set_high().unwrap();
 
-    loop {}
+    nixie::cycle_start(0);
+    nixie::cycle_start(1);
+    nixie::cycle_start(2);
+    nixie::cycle_start(3);
+
+    loop {
+        // let rng_delay =
+        //     nixie::RNG_REFRESH_INTERVAL + (rng.get_random_data() % nixie::RNG_REFRESH_VARIANCE);
+
+        // delay_timer.delay_ms(rng_delay * 1000);
+
+        // let tube = (rng.get_random_data() % 4) as usize;
+        // nixie::rng_refresh(tube);
+    }
 }
 
 fn set_fault_led(state: State) {
@@ -270,7 +298,7 @@ fn EXTI9_5() {
         if let Some(ref mut rtc_int) = rtc_int_ref.deref_mut() {
             if let Some(ref mut i2c) = i2c_int_ref.deref_mut() {
                 if rtc_int.check_interrupt() {
-                    nixie::rtc_interrupt(i2c);
+                    // nixie::rtc_interrupt(i2c);
                     rtc_int.clear_interrupt_pending_bit();
                 }
             }
@@ -296,11 +324,16 @@ fn TIM2() {
     free(|cs| {
         let mut i2c_int_ref = I2C.borrow(cs).borrow_mut();
         if let Some(ref mut i2c) = i2c_int_ref.deref_mut() {
-            nixie::refresh_interrupt(i2c);
+            nixie::fps_interrupt(i2c);
         }
     });
 }
 
+#[interrupt]
+fn TIM7() {
+    nixie::cycle_interrupt();
+}
+
 #[panic_handler]
 #[cfg(not(test))]
 /// Custom panic handler

+ 179 - 100
Nixie_Firmware_Rust/src/nixie.rs

@@ -1,4 +1,4 @@
-use core::{cell::RefCell, ops::DerefMut};
+use core::{cell::RefCell, ops::DerefMut, panic};
 
 use cortex_m::interrupt::{free, Mutex};
 use stm32l4xx_hal::{
@@ -24,11 +24,11 @@ pub const PCA9685_SUB_CALL_3: u8 = 0x73; // Default disabled
 
 pub const REFRESH_RATE_HZ: u32 = 1000;
 
-const DIGIT_FADE_DURATION_US: u32 = 1_000_000;
-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;
+pub const DIGIT_FADE_DURATION_MS: u32 = 1_000_000;
+pub const CYCLE_FADE_DURATION_MS: u32 = 200_000;
+pub const CYCLE_ITERATIONS: usize = 20;
+pub const CYCLE_REFRESH_INTERVAL: u32 = 60;
+pub const CYCLE_REFRESH_VARIANCE: u32 = 30;
 
 const DOT_MIN_BRIGHTNESS: u32 = 256;
 const DOT_MAX_BRIGHTNESS: u32 = 640;
@@ -258,6 +258,7 @@ struct Digit {
     value: u32,
     pwm_start: u32,
     pwm_end: u32,
+    fade_duration: Option<u32>,
     updated: bool,
 }
 
@@ -268,16 +269,22 @@ impl Digit {
             value: 0,
             pwm_start: 0,
             pwm_end: 0,
+            fade_duration: None,
             updated: false,
         }
     }
 }
 
+struct CycleSettings {
+    last_digit: Option<u32>,
+    next_digit: u32,
+    iteration: usize,
+}
+
 struct Tube {
     digits: [Digit; NUM_DIGITS],
-    last_active_digit: Option<usize>,
-    refresh_last_digit: Option<usize>,
-    refresh_active: bool,
+    last_active_digit: Option<u32>,
+    cycle: Option<CycleSettings>,
 }
 
 impl Tube {
@@ -286,8 +293,44 @@ impl Tube {
         Self {
             digits: [DIGIT_INIT; 10],
             last_active_digit: None,
-            refresh_last_digit: None,
-            refresh_active: false,
+            cycle: None,
+        }
+    }
+
+    fn fade_in_out_digit(
+        &mut self,
+        digit: Option<u32>,
+        fade_duration: u32,
+        cycle_cmd: bool,
+    ) {
+        // If the tube is in the middle of a cycle sequence and a call comes
+        // in to update the tube digit (for time), override the last value of
+        // the cycle sequence with the new digit.
+        if let Some(ref mut cycle) = self.cycle {
+            if !cycle_cmd {
+                cycle.last_digit = digit;
+            }
+        }
+
+        // Dont update if actively cycling tube unless cycle_cmd is set
+        if (self.cycle.is_none() && !cycle_cmd) || cycle_cmd {
+            // Fade out all digits
+            for digit in 0..NUM_DIGITS {
+                if self.digits[digit].value != DIGIT_MIN_BRIGHTNESS {
+                    self.digits[digit].state = State::Decrementing;
+                    self.digits[digit].fade_duration = Some(fade_duration);
+                }
+            }
+
+            // Fade in the specified digit
+            if let Some(digit) = digit {
+                if self.digits[digit as usize].value != DIGIT_MAX_BRIGHTNESS {
+                    self.digits[digit as usize].state = State::Incrementing;
+                    self.digits[digit as usize].fade_duration = Some(fade_duration);
+                }
+            }
+
+            self.last_active_digit = digit;
         }
     }
 }
@@ -297,7 +340,6 @@ 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>,
 }
@@ -308,7 +350,6 @@ impl Clock {
         Self {
             tubes: [TUBE_INIT; NUM_TUBES],
             dot: Digit::default(),
-            fade_duration: 0,
             minute: None,
             hour: None,
         }
@@ -318,52 +359,32 @@ impl Clock {
         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);
+                    self.tubes[0].fade_in_out_digit(None, DIGIT_FADE_DURATION_MS, false);
                 }
             }
             _ => {
-                self.fade_in_out_digit(
-                    0,
-                    Some((hour / 10) as usize),
-                    DIGIT_FADE_DURATION_US,
-                    false,
-                );
+                self.tubes[0].fade_in_out_digit(Some(hour / 10), DIGIT_FADE_DURATION_MS, 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,
-                );
+                self.tubes[1].fade_in_out_digit(Some(hour % 10), DIGIT_FADE_DURATION_MS, 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,
-                );
+                self.tubes[2].fade_in_out_digit(Some(minute / 10), DIGIT_FADE_DURATION_MS, 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,
-                );
+                self.tubes[3].fade_in_out_digit(Some(minute % 10), DIGIT_FADE_DURATION_MS, false);
             }
         }
 
@@ -381,6 +402,7 @@ impl Clock {
             1 => State::Decrementing,
             _ => State::Idle,
         };
+        self.dot.fade_duration = Some(DIGIT_FADE_DURATION_MS);
 
         #[cfg(test)]
         println!("RTC tick: dot state is {:?}", self.dot.state);
@@ -390,14 +412,14 @@ impl Clock {
 
         #[cfg(not(test))]
         free(|cs| {
-            let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
+            let mut timer_ref = super::FPS_TIMER.borrow(cs).borrow_mut();
             if let Some(ref mut timer) = timer_ref.deref_mut() {
                 timer.listen(Event::TimeOut);
             }
-        })
+        });
     }
 
-    pub fn refresh_tick(&mut self) -> bool {
+    pub fn fps_tick(&mut self) -> bool {
         let mut pending_refresh: bool = false;
 
         let mut update_fn = |digit: &mut Digit, min: u32, max: u32, steps: u32| {
@@ -411,7 +433,7 @@ impl Clock {
                         digit.updated = true;
                         pending_refresh = true;
                     }
-                }
+                },
                 State::Decrementing => {
                     if digit.value <= min {
                         digit.value = min;
@@ -421,25 +443,34 @@ impl Clock {
                         digit.updated = true;
                         pending_refresh = true;
                     }
-                }
-                State::Idle => (),
+                },
+                State::Idle => {
+                    digit.fade_duration = None;
+                },
             };
         };
 
-        let ticks = self.fade_duration / REFRESH_RATE_HZ;
-        let steps = ((DIGIT_MAX_BRIGHTNESS - DIGIT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
-
         #[cfg(not(test))]
         self.tubes.iter_mut().for_each(|tube| {
             tube.digits.iter_mut().for_each(|digit| {
-                update_fn(digit, DIGIT_MIN_BRIGHTNESS, DIGIT_MAX_BRIGHTNESS, steps);
+
+                if let Some(fade_duration) = digit.fade_duration {
+                    let ticks = fade_duration / REFRESH_RATE_HZ;
+                    let steps = ((DIGIT_MAX_BRIGHTNESS - DIGIT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
+                    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 let Some(fade_duration) = digit.fade_duration {
+                    let ticks = fade_duration / REFRESH_RATE_HZ;
+                    let steps = ((DIGIT_MAX_BRIGHTNESS - DIGIT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
+                    update_fn(digit, DIGIT_MIN_BRIGHTNESS, DIGIT_MAX_BRIGHTNESS, steps);
+                }
 
                 if digit.updated {
                     println!(
@@ -451,8 +482,11 @@ impl Clock {
         }
 
         // Handle dot
-        let steps = ((DOT_MAX_BRIGHTNESS - DOT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
-        update_fn(&mut self.dot, DOT_MIN_BRIGHTNESS, DOT_MAX_BRIGHTNESS, steps);
+        if let Some(fade_duration) = self.dot.fade_duration {
+            let ticks = fade_duration / REFRESH_RATE_HZ;
+            let steps = ((DOT_MAX_BRIGHTNESS - DOT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
+            update_fn(&mut self.dot, DOT_MIN_BRIGHTNESS, DOT_MAX_BRIGHTNESS, steps);
+        }
 
         #[cfg(test)]
         if self.dot.updated {
@@ -466,12 +500,44 @@ impl Clock {
         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 cycle_tick(&mut self) -> bool {
+        let mut cycle_ended = true;
+
+        self.tubes.iter_mut().for_each(|tube| {
+            if let Some(cycle) = tube.cycle.as_mut() {
+
+                #[cfg(test)]
+                println!("Cycle tick: iteration {}", cycle.iteration);
+
+                if cycle.iteration > 0 {
+                    let next_digit = cycle.next_digit;
+                    cycle.next_digit = if cycle.next_digit == 9 { 0 } else { cycle.next_digit + 1 };
+                    cycle.iteration = cycle.iteration - 1;
+                    tube.fade_in_out_digit(Some(next_digit), CYCLE_FADE_DURATION_MS, true);
+                    cycle_ended = false;
+                } else {
+                    let last_digit = cycle.last_digit;
+                    tube.cycle = None;
+                    tube.fade_in_out_digit(last_digit, CYCLE_FADE_DURATION_MS, true);
+                }
+            }
+        });
+
+        // self.tubes.iter_mut().for_each(|tube| {
+        //     tube.fade_in_out_digit(Some(0), CYCLE_FADE_DURATION_US, true);
+        // });
+
+        // self.tubes[3].fade_in_out_digit(Some(0), CYCLE_FADE_DURATION_US, true);
+
+        #[cfg(not(test))]
+        free(|cs| {
+            let mut timer_ref = super::FPS_TIMER.borrow(cs).borrow_mut();
+            if let Some(ref mut timer) = timer_ref.deref_mut() {
+                timer.listen(Event::TimeOut);
+            }
+        });
+
+        cycle_ended
     }
 
     pub fn write_i2c<T>(&mut self, i2c: &mut T)
@@ -480,8 +546,8 @@ impl Clock {
             + _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() {
+        for (t, tube) in self.tubes.iter_mut().enumerate() {
+            for (d, digit) in tube.digits.iter_mut().enumerate() {
                 if digit.updated {
                     pca9685::set_digit(
                         i2c,
@@ -490,6 +556,7 @@ impl Clock {
                         digit.pwm_start,
                         digit.pwm_end,
                     );
+                    digit.updated = false;
                 }
             }
         }
@@ -502,41 +569,7 @@ impl Clock {
                 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;
-                }
-            }
-
-            self.tubes[tube].last_active_digit = digit;
-            self.fade_duration = fade_duration;
+            self.dot.updated = false;
         }
     }
 
@@ -638,7 +671,9 @@ where
     });
 }
 
-pub fn refresh_interrupt<T>(i2c: &mut T)
+// This function is called by an interrupt that is triggered every
+// REFRESH_RATE_HZ to update the display with a new brightness value.
+pub fn fps_interrupt<T>(i2c: &mut T)
 where
     T: _embedded_hal_blocking_i2c_WriteRead
         + _embedded_hal_blocking_i2c_Read
@@ -647,18 +682,18 @@ where
     free(|cs| {
         let mut clock_ref = CLOCK.borrow(cs).borrow_mut();
         let clock = clock_ref.deref_mut();
-        let refresh = clock.refresh_tick();
-        if refresh {
+        let updated = clock.fps_tick();
+        if updated {
             clock.write_i2c(i2c);
             free(|cs| {
-                let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
+                let mut timer_ref = super::FPS_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();
+                let mut timer_ref = super::FPS_TIMER.borrow(cs).borrow_mut();
                 if let Some(ref mut timer) = timer_ref.deref_mut() {
                     timer.unlisten(Event::TimeOut);
                 }
@@ -667,6 +702,50 @@ where
     });
 }
 
+// This function is called by an interrupt that is triggered every
+// DIGIT_CYCLE_FADE_DURATION_HZ to update the digit being refreshed.
+pub fn cycle_interrupt() {
+    free(|cs| {
+        let mut clock_ref = CLOCK.borrow(cs).borrow_mut();
+        let clock = clock_ref.deref_mut();
+
+        let cycle_ended = clock.cycle_tick();
+        free(|cs| {
+            let mut cycle_timer_ref = super::CYCLE_TIMER.borrow(cs).borrow_mut();
+            if let Some(ref mut cycle_timer) = cycle_timer_ref.deref_mut() {
+                if cycle_ended {
+                    cycle_timer.unlisten(Event::TimeOut);
+                } else {
+                    cycle_timer.clear_interrupt(Event::TimeOut);
+                }
+            }
+        });
+    });
+}
+
+// This function is called to start cycling through all digits for a
+// tube to prevent damage to the nixie tube due to cathode poisoning.
+pub fn cycle_start(tube: usize) {
+    free(|cs| {
+        let mut clock_ref = CLOCK.borrow(cs).borrow_mut();
+        let clock = clock_ref.deref_mut();
+
+        clock.tubes[tube].cycle = Some(CycleSettings {
+            last_digit: clock.tubes[tube].last_active_digit,
+            next_digit: 0,
+            iteration: CYCLE_ITERATIONS,
+        });
+    });
+
+    // Start the timer to cycle through all digits
+    free(|cs| {
+        let mut cycle_timer_ref = super::CYCLE_TIMER.borrow(cs).borrow_mut();
+        if let Some(ref mut cycle_timer) = cycle_timer_ref.deref_mut() {
+            cycle_timer.listen(Event::TimeOut);
+        }
+    });
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
@@ -680,7 +759,7 @@ mod test {
 
         for tick in 0..1005 {
             println!("\nRefresh tick: {}", tick);
-            if !clock.refresh_tick() {
+            if !clock.fps_tick() {
                 println!("Refresh halted");
                 break;
             }