1use crate::calibration::{Calibration, CalibrationData, NoCustom, Point};
2use crate::pwm_cluster::{DynPin, GlobalState, GlobalStates, PwmCluster, PwmClusterBuilder};
3use crate::servo_state::{self, ServoState, DEFAULT_FREQUENCY};
4use crate::{initialize_array_by_index, initialize_array_from, initialize_arrays_from};
5use defmt::Format;
6use fugit::HertzU32;
7use rp2040_hal::clocks::SystemClock;
8use rp2040_hal::dma::{Channel, ChannelIndex};
9use rp2040_hal::gpio::Function;
10use rp2040_hal::pio::{PIOExt, StateMachineIndex, UninitStateMachine, PIO};
11use rp2040_hal::Clock;
12
13#[derive(Copy, Clone)]
15pub struct ServoIdx(u8);
16
17pub struct ServoCluster<const NUM_SERVOS: usize, P, SM, Cal = NoCustom>
19where
20 P: PIOExt,
21 SM: StateMachineIndex,
22{
23 pwms: PwmCluster<NUM_SERVOS, P, SM>,
25 pwm_period: u32,
27 pwm_frequency: f32,
29 states: [ServoState<Cal>; NUM_SERVOS],
31 servo_phases: [f32; NUM_SERVOS],
37}
38
39pub struct ServoClusterBuilder<
41 'a,
42 Cal,
43 C1,
44 C2,
45 P,
46 SM,
47 F,
48 const NUM_SERVOS: usize,
49 const NUM_CHANNELS: usize,
50> where
51 C1: ChannelIndex + 'static,
52 C2: ChannelIndex + 'static,
53 P: PIOExt<PinFunction = F> + 'static,
54 F: Function,
55 SM: StateMachineIndex + 'static,
56{
57 pio: &'a mut PIO<P>,
59 sm: UninitStateMachine<(P, SM)>,
61 dma_channels: (Channel<C1>, Channel<C2>),
63 global_states: &'static mut GlobalStates<NUM_CHANNELS>,
65 pins: Option<[DynPin<F>; NUM_SERVOS]>,
67 #[cfg(feature = "debug_pio")]
69 side_set_pin: Option<DynPin<F>>,
70 pwm_frequency: Option<f32>,
72 calibrations: Option<[Calibration<Cal>; NUM_SERVOS]>,
74 auto_phase: Option<bool>,
76}
77
78pub struct ServoData<C, F>
79where
80 F: Function,
81{
82 pub pin: DynPin<F>,
83 pub calibration: Calibration<C>,
84}
85
86impl<'a, Cal, C1, C2, P, SM, F, const NUM_SERVOS: usize, const NUM_CHANNELS: usize>
87 ServoClusterBuilder<'a, Cal, C1, C2, P, SM, F, NUM_SERVOS, NUM_CHANNELS>
88where
89 Cal: CalibrationData + Default + Clone,
90 for<'i> <Cal as CalibrationData>::Iterator<'i>: Iterator<Item = (Point, Point)>,
91 C1: ChannelIndex,
92 C2: ChannelIndex,
93 P: PIOExt<PinFunction = F>,
94 F: Function,
95 SM: StateMachineIndex,
96{
97 pub fn calibrations(mut self, calibrations: [Calibration<Cal>; NUM_SERVOS]) -> Self {
99 self.calibrations = Some(calibrations);
100 self
101 }
102
103 pub fn auto_phase(mut self, auto_phase: bool) -> Self {
105 self.auto_phase = Some(auto_phase);
106 self
107 }
108
109 pub fn pins_and_calibration(mut self, pin_data: [ServoData<Cal, F>; NUM_SERVOS]) -> Self {
156 let (dyn_pins, calibrations) =
157 initialize_arrays_from(pin_data, |data| (data.pin, data.calibration));
158 self.pins = Some(dyn_pins);
159 self.calibrations = Some(calibrations);
160 self
161 }
162
163 #[cfg(feature = "debug_pio")]
165 pub fn side_set_pin(mut self, side_set_pin: DynPin<F>) -> Self {
166 self.side_set_pin = Some(side_set_pin);
167 self
168 }
169
170 pub fn pwm_frequency(mut self, pwm_frequency: f32) -> Self {
172 self.pwm_frequency = Some(pwm_frequency);
173 self
174 }
175
176 pub fn build(
179 mut self,
180 system_clock: &SystemClock,
181 maybe_global_state: &'static mut Option<GlobalState<C1, C2, P, SM>>,
182 ) -> Result<ServoCluster<NUM_SERVOS, P, SM, Cal>, ServoClusterBuilderError> {
183 let pins = self.pins.ok_or(ServoClusterBuilderError::MissingPins)?;
184 let calibrations = self
185 .calibrations
186 .ok_or(ServoClusterBuilderError::MissingCalibrations)?;
187 let (states, servo_phases) = ServoCluster::<NUM_SERVOS, P, SM, _>::create_servo_states(
188 calibrations,
189 self.auto_phase.unwrap_or(true),
190 );
191 let global_state = self.global_states.get_mut(&mut self.dma_channels, move || {
192 PwmClusterBuilder::<NUM_SERVOS, P>::prep_global_state(maybe_global_state)
193 });
194 let mut pwms = {
195 {
196 unsafe {
197 #[allow(unused_mut)]
198 let mut cluster = PwmCluster::<NUM_SERVOS, P, SM>::builder().pins(&pins);
199 #[cfg(feature = "debug_pio")]
200 {
201 let side_set_pin = self
202 .side_set_pin
203 .ok_or(ServoClusterBuilderError::MissingSideSet)?;
204 cluster = cluster.side_pin(&side_set_pin);
205 }
206 cluster.build(
207 pins,
208 self.pio,
209 self.sm,
210 self.dma_channels,
211 system_clock,
212 global_state.ok_or(ServoClusterBuilderError::MismatchingGlobalState)?,
213 )
214 }
215 }
216 };
217
218 let pwm_frequency = self.pwm_frequency.unwrap_or(DEFAULT_FREQUENCY);
220 let pwm_period = if let Some((pwm_period, div256)) =
221 PwmCluster::<NUM_SERVOS, P, SM>::calculate_pwm_factors(
222 system_clock.freq(),
223 pwm_frequency,
224 ) {
225 for servo in 0..NUM_SERVOS as u8 {
227 let _ = pwms.set_channel_level(servo, 0, false);
228 let _ = pwms.set_channel_offset(
229 servo,
230 (servo_phases[servo as usize] * pwm_period as f32) as u32,
231 false,
232 );
233 }
234
235 pwms.set_top(pwm_period, true); let div: u16 = (div256 >> 8) as u16;
241 let modulus: u8 = (div256 % 256) as u8;
242 pwms.clock_divisor_fixed_point(div, modulus);
243 pwm_period
244 } else {
245 0
246 };
247
248 Ok(ServoCluster {
249 pwms,
250 pwm_period,
251 pwm_frequency,
252 states,
253 servo_phases,
254 })
255 }
256}
257
258#[derive(Format)]
260pub enum ServoClusterBuilderError {
261 MismatchingGlobalState,
264 MissingPins,
266 MissingCalibrations,
267 #[cfg(feature = "debug_pio")]
269 MissingSideSet,
270}
271
272impl<'a, const NUM_SERVOS: usize, P, SM, Cal, F> ServoCluster<NUM_SERVOS, P, SM, Cal>
273where
274 Cal: Default + CalibrationData + Clone,
275 for<'i> <Cal as CalibrationData>::Iterator<'i>: Iterator<Item = (Point, Point)>,
276 P: PIOExt<PinFunction = F>,
277 F: Function,
278 SM: StateMachineIndex,
279{
280 pub fn builder<C1, C2, const NUM_CHANNELS: usize>(
282 pio: &'a mut PIO<P>,
283 sm: UninitStateMachine<(P, SM)>,
284 dma_channels: (Channel<C1>, Channel<C2>),
285 global_states: &'static mut GlobalStates<NUM_CHANNELS>,
286 ) -> ServoClusterBuilder<'a, Cal, C1, C2, P, SM, F, NUM_SERVOS, NUM_CHANNELS>
287 where
288 C1: ChannelIndex + 'static,
289 C2: ChannelIndex + 'static,
290 {
291 ServoClusterBuilder {
292 pio,
293 sm,
294 dma_channels,
295 global_states,
296 pins: None,
297 #[cfg(feature = "debug_pio")]
298 side_set_pin: None,
299 pwm_frequency: None,
300 calibrations: None,
301 auto_phase: None,
302 }
303 }
304
305 fn create_servo_states(
307 calibrations: [Calibration<Cal>; NUM_SERVOS],
308 auto_phase: bool,
309 ) -> ([ServoState<Cal>; NUM_SERVOS], [f32; NUM_SERVOS]) {
310 (
311 initialize_array_from::<Calibration<Cal>, ServoState<Cal>, NUM_SERVOS>(
312 calibrations,
313 move |calibration| ServoState::with_calibration(calibration),
314 ),
315 initialize_array_by_index::<f32, NUM_SERVOS>(|i| {
316 if auto_phase {
317 i as f32 / NUM_SERVOS as f32
318 } else {
319 0.0
320 }
321 }),
322 )
323 }
324
325 pub fn servos(&self) -> [ServoIdx; NUM_SERVOS] {
326 initialize_array_by_index(|i| ServoIdx(i as u8))
327 }
328
329 pub fn enabled(&self, servo: ServoIdx) -> bool {
331 self.states[servo.0 as usize].enabled()
332 }
333
334 pub fn set_enabled(&mut self, servo: ServoIdx, enable: bool, load: bool) {
337 let state = &mut self.states[servo.0 as usize];
338 if enable {
339 let new_pulse = state.enable_with_return();
340 self.apply_pulse(servo, new_pulse, load);
341 } else {
342 state.disable();
343 }
344 }
345
346 pub fn pulse(&self, servo: ServoIdx) -> Option<f32> {
348 self.states[servo.0 as usize].pulse()
349 }
350
351 pub fn set_pulse(&mut self, servo: ServoIdx, pulse: f32, load: bool) {
354 let new_pulse = self.states[servo.0 as usize].set_pulse_with_return(pulse);
355 if let Some(new_pulse) = new_pulse {
356 self.apply_pulse(servo, new_pulse, load);
357 }
358 }
359
360 pub fn value(&self, servo: ServoIdx) -> f32 {
362 self.states[servo.0 as usize].value()
363 }
364
365 pub fn set_value(&mut self, servo: ServoIdx, value: f32, load: bool) {
368 let new_pulse = self.states[servo.0 as usize].set_value_with_return(value);
369 self.apply_pulse(servo, new_pulse, load);
370 }
371
372 pub fn phase(&self, servo: ServoIdx) -> f32 {
374 self.servo_phases[servo.0 as usize]
375 }
376
377 pub fn set_phase(&mut self, servo: ServoIdx, phase: f32, load: bool) {
380 self.servo_phases[servo.0 as usize] = phase.clamp(0.0, 1.0);
381 let _ = self.pwms.set_channel_offset(
383 servo.0,
384 (self.servo_phases[servo.0 as usize] * self.pwms.top() as f32) as u32,
385 load,
386 );
387 }
388
389 pub fn frequency(&self) -> f32 {
391 self.pwm_frequency
392 }
393
394 pub fn set_frequency(&mut self, system_clock_hz: HertzU32, frequency: f32) {
396 if (servo_state::MIN_FREQUENCY..=servo_state::MAX_FREQUENCY).contains(&frequency) {
397 if let Some((period, div256)) =
398 PwmCluster::<NUM_SERVOS, P, SM>::calculate_pwm_factors(system_clock_hz, frequency)
399 {
400 self.pwm_period = period;
401 self.pwm_frequency = frequency;
402
403 for servo in 0..NUM_SERVOS {
404 if self.states[servo].enabled() {
405 self.apply_pulse(
407 ServoIdx(servo as u8),
408 self.states[servo].pulse().unwrap(),
409 false,
410 );
411 }
412 let _ = self.pwms.set_channel_offset(
414 servo as u8,
415 (self.servo_phases[servo] * self.pwm_period as f32) as u32,
416 false,
417 );
418 }
419
420 self.pwms.set_top(self.pwm_period, true);
421 let div = (div256 >> 8) as u16;
422 let frac = (div256 % 256) as u8;
423 self.pwms.clock_divisor_fixed_point(div, frac);
424 }
425 }
426 }
427
428 pub fn min_value(&self, servo: ServoIdx) -> f32 {
430 self.states[servo.0 as usize].min_value()
431 }
432
433 pub fn mid_value(&self, servo: ServoIdx) -> f32 {
435 self.states[servo.0 as usize].mid_value()
436 }
437
438 pub fn max_value(&self, servo: ServoIdx) -> f32 {
440 self.states[servo.0 as usize].max_value()
441 }
442
443 pub fn to_min(&mut self, servo: ServoIdx, load: bool) {
446 let new_pulse = self.states[servo.0 as usize].to_min_with_return();
447 self.apply_pulse(servo, new_pulse, load);
448 }
449
450 pub fn to_mid(&mut self, servo: ServoIdx, load: bool) {
453 let new_pulse = self.states[servo.0 as usize].to_mid_with_return();
454 self.apply_pulse(servo, new_pulse, load);
455 }
456
457 pub fn to_max(&mut self, servo: ServoIdx, load: bool) {
460 let new_pulse = self.states[servo.0 as usize].to_max_with_return();
461 self.apply_pulse(servo, new_pulse, load);
462 }
463
464 pub fn to_percent(&mut self, servo: ServoIdx, percent: f32, load: bool) {
467 let new_pulse = self.states[servo.0 as usize].to_percent_with_return(percent);
468 self.apply_pulse(servo, new_pulse, load);
469 }
470
471 pub fn calibration(&self, servo: ServoIdx) -> &Calibration<Cal> {
473 self.states[servo.0 as usize].calibration()
474 }
475
476 pub fn calibration_mut(&mut self, servo: ServoIdx) -> &mut Calibration<Cal> {
478 self.states[servo.0 as usize].calibration_mut()
479 }
480
481 pub fn load(&mut self) {
483 self.pwms.load_pwm()
484 }
485
486 fn apply_pulse(&mut self, servo: ServoIdx, pulse: f32, load: bool) {
489 let level = ServoState::<Cal>::pulse_to_level(pulse, self.pwm_period, self.pwm_frequency);
491 let _ = self.pwms.set_channel_level(servo.0, level, load);
492 }
493}