autd3_driver/firmware/fpga/
fpga_state.rs

1use autd3_core::{firmware::Segment, link::RxMessage};
2
3/// FPGA state.
4#[repr(C)]
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct FPGAState(u8);
7
8impl FPGAState {
9    const THERMAL_ASSERT_BIT: u8 = 1 << 0;
10    const CURRENT_MOD_SEGMENT_BIT: u8 = 1 << 1;
11    const CURRENT_STM_SEGMENT_BIT: u8 = 1 << 2;
12    const CURRENT_GAIN_SEGMENT_BIT: u8 = 1 << 2;
13    const IS_GAIN_MODE_BIT: u8 = 1 << 3;
14    const READS_FPGA_STATE_ENABLED: u8 = 1 << 7;
15
16    const fn contains(&self, bit: u8) -> bool {
17        (self.0 & bit) != 0
18    }
19}
20
21impl FPGAState {
22    #[doc(hidden)]
23    pub const fn bits(&self) -> u8 {
24        self.0
25    }
26
27    /// `true` if the thermal sensor is asserted.
28    #[must_use]
29    pub const fn is_thermal_assert(&self) -> bool {
30        self.contains(Self::THERMAL_ASSERT_BIT)
31    }
32
33    /// The current Modulation segment.
34    #[must_use]
35    pub const fn current_mod_segment(&self) -> Segment {
36        if self.contains(Self::CURRENT_MOD_SEGMENT_BIT) {
37            Segment::S1
38        } else {
39            Segment::S0
40        }
41    }
42
43    /// The current STM segment. `None` if the current mode is not STM.
44    #[must_use]
45    pub const fn current_stm_segment(&self) -> Option<Segment> {
46        if !self.is_stm_mode() {
47            return None;
48        }
49        if self.contains(Self::CURRENT_STM_SEGMENT_BIT) {
50            Some(Segment::S1)
51        } else {
52            Some(Segment::S0)
53        }
54    }
55
56    /// The current Gain segment. `None` if the current mode is not Gain.
57    #[must_use]
58    pub const fn current_gain_segment(&self) -> Option<Segment> {
59        if !self.is_gain_mode() {
60            return None;
61        }
62        if self.contains(Self::CURRENT_GAIN_SEGMENT_BIT) {
63            Some(Segment::S1)
64        } else {
65            Some(Segment::S0)
66        }
67    }
68
69    /// `true` if the current mode is Gain.
70    #[must_use]
71    pub const fn is_gain_mode(&self) -> bool {
72        self.contains(Self::IS_GAIN_MODE_BIT)
73    }
74
75    /// `true` if the current mode is STM.
76    #[must_use]
77    pub const fn is_stm_mode(&self) -> bool {
78        !self.is_gain_mode()
79    }
80
81    #[doc(hidden)]
82    #[must_use]
83    pub fn from_rx(msg: &RxMessage) -> Option<Self> {
84        let flag = FPGAState(msg.data());
85        if !flag.contains(Self::READS_FPGA_STATE_ENABLED) {
86            return None;
87        }
88        Some(flag)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use std::mem::size_of;
95
96    use super::*;
97
98    #[test]
99    fn size() {
100        assert_eq!(1, size_of::<FPGAState>());
101    }
102
103    #[test]
104    fn bits() {
105        let state = FPGAState(0b1010_1101);
106        assert_eq!(0b1010_1101, state.bits());
107    }
108
109    #[rstest::rstest]
110    #[case(false, 0b0)]
111    #[case(true, 0b1)]
112    fn is_thermal_assert(#[case] expected: bool, #[case] state: u8) {
113        assert_eq!(expected, FPGAState(state).is_thermal_assert());
114    }
115
116    #[rstest::rstest]
117    #[case(Segment::S0, 0b00)]
118    #[case(Segment::S1, 0b10)]
119    fn current_mod_segment(#[case] expected: Segment, #[case] state: u8) {
120        assert_eq!(expected, FPGAState(state).current_mod_segment());
121    }
122
123    #[rstest::rstest]
124    #[case(false, 0b0000)]
125    #[case(true, 0b1000)]
126    fn is_gain_mode(#[case] expected: bool, #[case] state: u8) {
127        assert_eq!(expected, FPGAState(state).is_gain_mode());
128    }
129
130    #[rstest::rstest]
131    #[case(false, 0b1000)]
132    #[case(true, 0b0000)]
133    fn is_stm_mode(#[case] expected: bool, #[case] state: u8) {
134        assert_eq!(expected, FPGAState(state).is_stm_mode());
135    }
136
137    #[rstest::rstest]
138    #[case(None, 0b1000)]
139    #[case(Some(Segment::S0), 0b0000)]
140    #[case(Some(Segment::S1), 0b0100)]
141    fn current_stm_segment(#[case] expected: Option<Segment>, #[case] state: u8) {
142        assert_eq!(expected, FPGAState(state).current_stm_segment());
143    }
144
145    #[rstest::rstest]
146    #[case(None, 0b0000)]
147    #[case(Some(Segment::S0), 0b1000)]
148    #[case(Some(Segment::S1), 0b1100)]
149    fn current_gain_segment(#[case] expected: Option<Segment>, #[case] state: u8) {
150        assert_eq!(expected, FPGAState(state).current_gain_segment());
151    }
152}