From fa9ac741f945e173eed9b8df54e2ab14d9517eb6 Mon Sep 17 00:00:00 2001
From: Samuel Tardieu <sam@rfc1149.net>
Date: Tue, 16 Jul 2024 20:29:06 +0200
Subject: [PATCH] Use queue to handle timeout

---
 controller/src/logic.rs | 47 +++++++++++++++++++----------------------
 1 file changed, 22 insertions(+), 25 deletions(-)

diff --git a/controller/src/logic.rs b/controller/src/logic.rs
index c45ee77..c8fdeeb 100644
--- a/controller/src/logic.rs
+++ b/controller/src/logic.rs
@@ -2,16 +2,19 @@ use crate::{
     encoders::Encoders,
     tb6612fng::{Movement, Tb6612fng},
 };
-use core::sync::atomic::{AtomicU32, Ordering};
+use core::sync::atomic::{AtomicU8, Ordering};
 use embassy_stm32::{
     pac::{self, wwdg::vals::Wdgtb},
     peripherals::WWDG,
     time::hz,
 };
+use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
 use embassy_time::{Duration, Instant, Ticker};
 use heapless::Deque;
 use i2c2_target::{I2C2, MESSAGE_SIZE};
 
+static WATCHDOG_TICKS: AtomicU8 = AtomicU8::new(5);
+
 struct State {
     i2c: I2C2,
     motors: Tb6612fng<'static>,
@@ -19,8 +22,6 @@ struct State {
     max_motor_percentage: u8,
     motor_speed: [u8; 2],
     standby: bool,
-    /// Number of 1/10th of seconds
-    watchdog_ticks: u8,
 }
 
 impl State {
@@ -32,7 +33,6 @@ impl State {
             max_motor_percentage: 100,
             motor_speed: [0, 0],
             standby: true,
-            watchdog_ticks: 5,
         }
     }
 
@@ -87,17 +87,6 @@ impl State {
 
 static mut STATE: Option<State> = None;
 
-/// Date of the next watchdog expiration. A u32 value can hold up to
-/// 49 days which is more than enough. It is not stored in the state
-/// to be able to be checked outside any critical section.
-static WATCHDOG_EXPIRATION: AtomicU32 = AtomicU32::new(0);
-
-/// Number of milliseconds since boot
-#[allow(clippy::cast_possible_truncation)]
-fn current_millis() -> u32 {
-    Instant::now().as_millis() as u32
-}
-
 fn wwdg_configure() {
     // Enable WWDG clock
     pac::RCC.apb1enr().modify(|w| w.set_wwdgen(true));
@@ -133,16 +122,25 @@ pub async fn start_i2c_target(
         STATE = Some(State::new(i2c2, motors, encoders));
         STATE.as_mut().unwrap().i2c.start(&i2c_callback);
     }
+    watchdog().await;
+}
+
+static WATCHDOG_PING: Signal<CriticalSectionRawMutex, Instant> = Signal::new();
+
+async fn watchdog() {
     wwdg_configure();
     let mut ticker = Ticker::every(Duration::from_millis(50));
+    let mut watchdog_expiration: Instant = Instant::now();
     loop {
         ticker.next().await;
         wwdg_pet();
-        if current_millis() >= WATCHDOG_EXPIRATION.load(Ordering::Relaxed) {
+        if let Some(ping) = WATCHDOG_PING.try_take() {
+            watchdog_expiration = ping
+                + Duration::from_millis(100 * u64::from(WATCHDOG_TICKS.load(Ordering::Relaxed)));
+        }
+        if Instant::now() >= watchdog_expiration {
             State::with_critical_section(|state| {
-                if state.is_moving()
-                    && current_millis() >= WATCHDOG_EXPIRATION.load(Ordering::Relaxed)
-                {
+                if state.is_moving() {
                     defmt::debug!("stopping motors after watchdog has expired");
                     state.standby();
                 }
@@ -155,10 +153,7 @@ fn i2c_callback(command: &[u8], response: &mut Deque<u8, MESSAGE_SIZE>) {
     #[allow(static_mut_refs)]
     let state = unsafe { STATE.as_mut().unwrap() };
     if process_command(state, command, response) {
-        WATCHDOG_EXPIRATION.store(
-            current_millis() + 100 * u32::from(state.watchdog_ticks),
-            Ordering::Relaxed,
-        );
+        WATCHDOG_PING.signal(Instant::now());
     } else {
         state.standby();
     }
@@ -225,14 +220,16 @@ fn process_command(
         }
         &[MOTOR_SHUTDOWN_TIMEOUT, ticks] => {
             if (1..=100).contains(&ticks) {
-                state.watchdog_ticks = ticks;
+                WATCHDOG_TICKS.store(ticks, Ordering::Relaxed);
             } else {
                 defmt::warn!("ticks out of range: {}", ticks);
                 return false;
             }
         }
         [MOTOR_SHUTDOWN_TIMEOUT] => {
-            response.push_back(state.watchdog_ticks).unwrap();
+            response
+                .push_back(WATCHDOG_TICKS.load(Ordering::Relaxed))
+                .unwrap();
         }
         [ENCODER_TICKS] => {
             let (left, right) = state.encoders.ticks();
-- 
GitLab