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