autocore_std/fb/f_trig.rs
1/// Falling Edge Trigger (F_TRIG)
2///
3/// Detects a falling edge (true → false transition) on the input signal.
4/// The output `q` is `true` for exactly one cycle when the input `clk`
5/// transitions from `true` to `false`.
6///
7/// This is equivalent to the IEC 61131-3 F_TRIG function block.
8///
9/// # Example
10///
11/// ```
12/// use autocore_std::fb::FTrig;
13///
14/// let mut trigger = FTrig::new();
15///
16/// // Signal starts low
17/// assert_eq!(trigger.call(false), false);
18///
19/// // Signal goes high
20/// assert_eq!(trigger.call(true), false);
21///
22/// // Falling edge detected!
23/// assert_eq!(trigger.call(false), true);
24///
25/// // Signal still low, edge already passed
26/// assert_eq!(trigger.call(false), false);
27/// ```
28///
29/// # Timing Diagram
30///
31/// ```text
32/// clk: _____|‾‾‾‾‾‾‾‾‾|_____|‾‾‾‾‾
33/// q: _______________|‾|________
34/// ```
35///
36/// # Use Cases
37///
38/// - Detecting button releases
39/// - Detecting signal loss
40/// - Triggering actions when a condition ends
41#[derive(Debug, Clone)]
42pub struct FTrig {
43 /// Current input value
44 pub clk: bool,
45 /// Output: true for one cycle on falling edge
46 pub q: bool,
47 /// Internal memory of previous input state
48 m: bool,
49}
50
51impl FTrig {
52 /// Creates a new falling edge trigger with all values initialized to `false`.
53 ///
54 /// # Example
55 ///
56 /// ```
57 /// use autocore_std::fb::FTrig;
58 ///
59 /// let trigger = FTrig::new();
60 /// assert_eq!(trigger.q, false);
61 /// ```
62 pub fn new() -> Self {
63 Self {
64 clk: false,
65 q: false,
66 m: false,
67 }
68 }
69
70 /// Executes the falling edge detection logic.
71 ///
72 /// Call this method once per control cycle with the current input value.
73 /// Returns `true` for exactly one cycle when a falling edge is detected.
74 ///
75 /// # Arguments
76 ///
77 /// * `clk` - The current state of the input signal
78 ///
79 /// # Returns
80 ///
81 /// `true` if a falling edge (true → false transition) was detected, `false` otherwise.
82 ///
83 /// # Example
84 ///
85 /// ```
86 /// use autocore_std::fb::FTrig;
87 ///
88 /// let mut trigger = FTrig::new();
89 ///
90 /// // Simulate button release
91 /// trigger.call(true); // Button held
92 /// if trigger.call(false) { // Button released
93 /// println!("Button was just released!");
94 /// }
95 /// ```
96 pub fn call(&mut self, clk: bool) -> bool {
97 self.clk = clk;
98 self.q = !self.clk && self.m;
99 self.m = self.clk;
100 self.q
101 }
102}
103
104impl Default for FTrig {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_ftrig_falling_edge() {
116 let mut trigger = FTrig::new();
117
118 // No edge initially
119 assert_eq!(trigger.call(false), false);
120
121 // No edge on rising
122 assert_eq!(trigger.call(true), false);
123
124 // Falling edge
125 assert_eq!(trigger.call(false), true);
126
127 // No edge while low
128 assert_eq!(trigger.call(false), false);
129
130 // Rising, then falling edge
131 assert_eq!(trigger.call(true), false);
132 assert_eq!(trigger.call(false), true);
133 }
134}