1use crate::drivers::drv8873::{Drv8873, Fault};
16use crate::hw::spi::CsControl;
17use crate::hw::SpiBus;
18
19use stm32f7xx_hal::{
20 gpio::{self, Output, PushPull},
21 prelude::*,
22 spi,
23};
24
25#[derive(Copy, Clone, Debug, PartialEq)]
27pub enum Direction {
28 Extend,
29 Retract,
30 Brake,
31}
32
33pub struct ActuonixLinear<
46 CS: CsControl,
47 const SLP_P: char,
48 const SLP_N: u8,
49 const DIS_P: char,
50 const DIS_N: u8,
51 Pwm1,
52 Pwm2,
53 ReadPos,
54 const N: usize,
55> {
56 drv: Drv8873<CS>,
57 pwm1: Pwm1,
58 pwm2: Pwm2,
59 nsleep: gpio::Pin<SLP_P, SLP_N, Output<PushPull>>,
60 disable: gpio::Pin<DIS_P, DIS_N, Output<PushPull>>,
61 read_positions: ReadPos,
62 adc_history: [[u16; N]; 5],
63 adc_idx: usize,
64 last_medians: [u16; N],
65 inverted: [bool; N],
66 enabled: [bool; N],
67 stroke_len_mm: f32,
68 inverted_pair_sum_mm: f32,
69 buffer_bottom_mm: f32,
70 buffer_top_mm: f32,
71 current_speed: f32,
72 limit_brake_active: bool,
73}
74
75impl<
76 CS: CsControl,
77 const SLP_P: char,
78 const SLP_N: u8,
79 const DIS_P: char,
80 const DIS_N: u8,
81 Pwm1,
82 Pwm2,
83 ReadPos,
84 const N: usize,
85 > ActuonixLinear<CS, SLP_P, SLP_N, DIS_P, DIS_N, Pwm1, Pwm2, ReadPos, N>
86where
87 Pwm1: _embedded_hal_PwmPin<Duty = u16>,
88 Pwm2: _embedded_hal_PwmPin<Duty = u16>,
89 ReadPos: FnMut() -> [u16; N],
90{
91 pub fn new<SlpMode, DisMode>(
97 drv: Drv8873<CS>,
98 pwm1: Pwm1,
99 pwm2: Pwm2,
100 nsleep: gpio::Pin<SLP_P, SLP_N, SlpMode>,
101 disable: gpio::Pin<DIS_P, DIS_N, DisMode>,
102 mut read_positions: ReadPos,
103 inverted: [bool; N],
104 stroke_len_mm: f32,
105 inverted_pair_sum_mm: f32,
106 buffer_bottom_mm: f32,
107 buffer_top_mm: f32,
108 ) -> Self {
109 let mut nsleep = nsleep.into_push_pull_output();
110 let mut disable = disable.into_push_pull_output();
111
112 nsleep.set_high();
114 disable.set_low();
115
116 let initial = (read_positions)();
117
118 Self {
119 drv,
120 pwm1,
121 pwm2,
122 nsleep,
123 disable,
124 read_positions,
125 adc_history: [initial; 5],
126 adc_idx: 0,
127 last_medians: initial,
128 inverted,
129 enabled: [true; N],
130 stroke_len_mm,
131 inverted_pair_sum_mm,
132 buffer_bottom_mm,
133 buffer_top_mm,
134 current_speed: 0.0,
135 limit_brake_active: false,
136 }
137 }
138
139 pub fn set_channel_enabled(&mut self, channel: usize, enabled: bool) {
143 if channel < N {
144 self.enabled[channel] = enabled;
145 }
146 }
147
148 #[inline]
150 pub fn any_channel_enabled(&self) -> bool {
151 self.enabled.iter().any(|e| *e)
152 }
153
154 #[inline]
157 pub fn channel_medians(&self) -> &[u16; N] {
158 &self.last_medians
159 }
160
161 fn refresh(&mut self) {
164 let raw = (self.read_positions)();
165 self.adc_history[self.adc_idx] = raw;
166 self.adc_idx = (self.adc_idx + 1) % 5;
167
168 for i in 0..N {
169 let mut column = [0u16; 5];
170 for r in 0..5 {
171 column[r] = self.adc_history[r][i];
172 }
173 column.sort_unstable();
174 self.last_medians[i] = column[2];
175 }
176 }
177
178 fn fused_raw_from_cache(&self) -> Option<u16> {
181 let mut sum: u32 = 0;
182 let mut count: u32 = 0;
183 for i in 0..N {
184 if self.enabled[i] {
185 let m = self.last_medians[i] as u32;
186 let logical = if self.inverted[i] {
187 let offset = (self.inverted_pair_sum_mm / self.stroke_len_mm * 4095.0) as u32;
188 offset.saturating_sub(m)
189 } else {
190 m
191 };
192 sum += logical;
193 count += 1;
194 }
195 }
196 if count == 0 {
197 None
198 } else {
199 Some((sum / count) as u16)
200 }
201 }
202
203 pub fn set_speed(&mut self, speed: f32) {
210 self.limit_brake_active = false;
212
213 let mut speed = speed.clamp(-1.0, 1.0);
215
216 if let Some(pos) = self.position_mm() {
217 let max_pos = self.stroke_len_mm - self.buffer_top_mm;
218 let min_pos = self.buffer_bottom_mm;
219
220 if speed > 0.0 && pos >= max_pos {
222 speed = 0.0;
223 } else if speed < 0.0 && pos <= min_pos {
224 speed = 0.0;
225 }
226 }
227
228 self.current_speed = speed;
229
230 let max_duty = self.pwm1.get_max_duty(); let duty = (speed.abs() * max_duty as f32) as u16;
234
235 if speed > 0.001 {
236 self.pwm1.set_duty(duty);
238 self.pwm2.set_duty(0);
239 self.pwm1.enable();
240 self.pwm2.enable();
241 } else if speed < -0.001 {
242 self.pwm1.set_duty(0);
244 self.pwm2.set_duty(duty);
245 self.pwm1.enable();
246 self.pwm2.enable();
247 } else {
248 self.brake();
249 }
250 }
251
252 pub fn enforce_limits(&mut self) {
256 if self.current_speed.abs() < 0.001 {
257 return;
258 }
259
260 let Some(pos) = self.position_mm() else {
261 return;
262 };
263 let max_pos = self.stroke_len_mm - self.buffer_top_mm;
264 let min_pos = self.buffer_bottom_mm;
265
266 if self.current_speed > 0.0 && pos >= max_pos {
267 self.brake_due_to_limit();
268 self.current_speed = 0.0;
269 } else if self.current_speed < 0.0 && pos <= min_pos {
270 self.brake_due_to_limit();
271 self.current_speed = 0.0;
272 }
273 }
274
275 #[inline]
277 pub fn extend(&mut self) {
278 self.set_speed(1.0);
279 }
280
281 #[inline]
283 pub fn retract(&mut self) {
284 self.set_speed(-1.0);
285 }
286
287 #[inline]
289 pub fn brake(&mut self) {
290 self.limit_brake_active = false;
291 self.brake_raw();
292 }
293
294 #[inline]
295 fn brake_due_to_limit(&mut self) {
296 self.limit_brake_active = true;
297 self.brake_raw();
298 }
299
300 #[inline]
301 fn brake_raw(&mut self) {
302 let max = self.pwm1.get_max_duty();
303 self.pwm1.set_duty(max);
304 self.pwm2.set_duty(max);
305 self.pwm1.enable();
306 self.pwm2.enable();
307 }
308
309 #[inline]
311 pub fn is_limit_braking(&self) -> bool {
312 self.limit_brake_active
313 }
314
315 #[inline]
318 pub fn position_raw(&mut self) -> Option<u16> {
319 self.refresh();
320 self.fused_raw_from_cache()
321 }
322
323 pub fn position_percent(&mut self) -> Option<f32> {
325 self.position_raw().map(|r| (r as f32) / 4095.0)
326 }
327
328 pub fn position_mm(&mut self) -> Option<f32> {
330 self.position_percent().map(|p| p * self.stroke_len_mm)
331 }
332
333 pub fn stroke_len_mm(&self) -> f32 {
335 self.stroke_len_mm
336 }
337
338 pub fn drv(&mut self) -> &mut Drv8873<CS> {
340 &mut self.drv
341 }
342
343 #[inline]
347 pub fn sleep(&mut self) {
348 self.nsleep.set_low();
349 }
350
351 #[inline]
353 pub fn wake(&mut self) {
354 self.nsleep.set_high();
355 }
356
357 #[inline]
359 pub fn enable_outputs(&mut self) {
360 self.wake();
361 self.disable.set_low();
362 }
363
364 #[inline]
366 pub fn disable_outputs(&mut self) {
367 self.brake();
368 self.disable.set_high();
369 }
370
371 pub fn read_fault<I, PINS>(
373 &mut self,
374 spi_bus: &mut SpiBus<I, PINS>,
375 ) -> Result<Fault, spi::Error>
376 where
377 I: spi::Instance,
378 PINS: spi::Pins<I>,
379 {
380 self.drv.read_fault(spi_bus)
381 }
382}