autd3_driver/firmware/v10/fpga/
fpga_state.rs

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