autd3_driver/firmware/fpga/
fpga_state.rs

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