1use veloxity_core::board::BoardIo;
37use veloxity_core::errors;
38use veloxity_core::math::FlightFloat;
39use veloxity_core::packets::ParamPacket;
40use veloxity_core::params::Params;
41use veloxity_core::sensors::SensorBus;
42
43use embassy_time::{Delay, Duration, Instant};
44use stm_32::cortex_m::prelude::_embedded_hal_blocking_delay_DelayMs;
45#[cfg(not(feature = "scope-timing-pins"))]
46use stm_32::cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
47use stm_32::peripherals;
48use stm_32::peripherals::pwm::PixRacerProServoMonstrosity;
49use stm_32::*;
50
51#[cfg(feature = "runtime-diagnostics")]
52use core::sync::atomic::{AtomicU32, Ordering};
53
54include!("../../../platforms/stm_32/stm32h7x3_common.rs");
55
56const PARAM_STORAGE_TIMEOUT: Duration = Duration::from_secs(2);
57
58#[cfg(feature = "runtime-diagnostics")]
59#[unsafe(no_mangle)]
60pub static VELOXITY_DIAG_IMU_CONSUME: AtomicU32 = AtomicU32::new(0);
61#[cfg(feature = "runtime-diagnostics")]
62#[unsafe(no_mangle)]
63pub static VELOXITY_DIAG_IMU_CONSUME_ERROR: AtomicU32 = AtomicU32::new(0);
64#[cfg(feature = "runtime-diagnostics")]
65#[unsafe(no_mangle)]
66pub static VELOXITY_DIAG_IMU_CONSUME_AGE_SUM_US: AtomicU32 = AtomicU32::new(0);
67#[cfg(feature = "runtime-diagnostics")]
68#[unsafe(no_mangle)]
69pub static VELOXITY_DIAG_IMU_CONSUME_AGE_MAX_US: AtomicU32 = AtomicU32::new(0);
70
71#[cfg(feature = "runtime-diagnostics")]
72mod runtime_sensor_diagnostics {
73 use core::sync::atomic::{AtomicU32, Ordering};
74
75 use veloxity_core::{errors::SensorError, math::FlightFloat, sensors::SensorBus};
76
77 macro_rules! consume_counters {
78 ($count:ident, $errors:ident, $age_sum:ident, $age_max:ident) => {
79 #[unsafe(no_mangle)]
80 pub static $count: AtomicU32 = AtomicU32::new(0);
81 #[unsafe(no_mangle)]
82 pub static $errors: AtomicU32 = AtomicU32::new(0);
83 #[unsafe(no_mangle)]
84 pub static $age_sum: AtomicU32 = AtomicU32::new(0);
85 #[unsafe(no_mangle)]
86 pub static $age_max: AtomicU32 = AtomicU32::new(0);
87 };
88 }
89
90 consume_counters!(
91 VELOXITY_DIAG_MAG_CONSUME,
92 VELOXITY_DIAG_MAG_CONSUME_ERROR,
93 VELOXITY_DIAG_MAG_CONSUME_AGE_SUM_US,
94 VELOXITY_DIAG_MAG_CONSUME_AGE_MAX_US
95 );
96 consume_counters!(
97 VELOXITY_DIAG_BARO_CONSUME,
98 VELOXITY_DIAG_BARO_CONSUME_ERROR,
99 VELOXITY_DIAG_BARO_CONSUME_AGE_SUM_US,
100 VELOXITY_DIAG_BARO_CONSUME_AGE_MAX_US
101 );
102 consume_counters!(
103 VELOXITY_DIAG_PITOT_CONSUME,
104 VELOXITY_DIAG_PITOT_CONSUME_ERROR,
105 VELOXITY_DIAG_PITOT_CONSUME_AGE_SUM_US,
106 VELOXITY_DIAG_PITOT_CONSUME_AGE_MAX_US
107 );
108 consume_counters!(
109 VELOXITY_DIAG_RANGE_CONSUME,
110 VELOXITY_DIAG_RANGE_CONSUME_ERROR,
111 VELOXITY_DIAG_RANGE_CONSUME_AGE_SUM_US,
112 VELOXITY_DIAG_RANGE_CONSUME_AGE_MAX_US
113 );
114 consume_counters!(
115 VELOXITY_DIAG_GNSS_CONSUME,
116 VELOXITY_DIAG_GNSS_CONSUME_ERROR,
117 VELOXITY_DIAG_GNSS_CONSUME_AGE_SUM_US,
118 VELOXITY_DIAG_GNSS_CONSUME_AGE_MAX_US
119 );
120 consume_counters!(
121 VELOXITY_DIAG_BATTERY_CONSUME,
122 VELOXITY_DIAG_BATTERY_CONSUME_ERROR,
123 VELOXITY_DIAG_BATTERY_CONSUME_AGE_SUM_US,
124 VELOXITY_DIAG_BATTERY_CONSUME_AGE_MAX_US
125 );
126 consume_counters!(
127 VELOXITY_DIAG_RC_CONSUME,
128 VELOXITY_DIAG_RC_CONSUME_ERROR,
129 VELOXITY_DIAG_RC_CONSUME_AGE_SUM_US,
130 VELOXITY_DIAG_RC_CONSUME_AGE_MAX_US
131 );
132
133 pub fn record_bus<R: FlightFloat>(sensors: &SensorBus<R>, now_us: u64) {
134 macro_rules! record {
135 ($field:ident, $count:ident, $errors:ident, $age_sum:ident, $age_max:ident) => {
136 if let Some(result) = &sensors.$field {
137 $count.fetch_add(1, Ordering::Relaxed);
138 match result {
139 Ok(packet) => {
140 let age = now_us
141 .saturating_sub(packet.header.timestamp)
142 .min(u32::MAX as u64) as u32;
143 $age_sum.fetch_add(age, Ordering::Relaxed);
144 $age_max.fetch_max(age, Ordering::Relaxed);
145 }
146 Err(SensorError::GenericSensorError(_)) => {
147 $errors.fetch_add(1, Ordering::Relaxed);
148 }
149 }
150 }
151 };
152 }
153 record!(
154 mag,
155 VELOXITY_DIAG_MAG_CONSUME,
156 VELOXITY_DIAG_MAG_CONSUME_ERROR,
157 VELOXITY_DIAG_MAG_CONSUME_AGE_SUM_US,
158 VELOXITY_DIAG_MAG_CONSUME_AGE_MAX_US
159 );
160 record!(
161 baro,
162 VELOXITY_DIAG_BARO_CONSUME,
163 VELOXITY_DIAG_BARO_CONSUME_ERROR,
164 VELOXITY_DIAG_BARO_CONSUME_AGE_SUM_US,
165 VELOXITY_DIAG_BARO_CONSUME_AGE_MAX_US
166 );
167 record!(
168 pitot,
169 VELOXITY_DIAG_PITOT_CONSUME,
170 VELOXITY_DIAG_PITOT_CONSUME_ERROR,
171 VELOXITY_DIAG_PITOT_CONSUME_AGE_SUM_US,
172 VELOXITY_DIAG_PITOT_CONSUME_AGE_MAX_US
173 );
174 record!(
175 range,
176 VELOXITY_DIAG_RANGE_CONSUME,
177 VELOXITY_DIAG_RANGE_CONSUME_ERROR,
178 VELOXITY_DIAG_RANGE_CONSUME_AGE_SUM_US,
179 VELOXITY_DIAG_RANGE_CONSUME_AGE_MAX_US
180 );
181 record!(
182 gnss,
183 VELOXITY_DIAG_GNSS_CONSUME,
184 VELOXITY_DIAG_GNSS_CONSUME_ERROR,
185 VELOXITY_DIAG_GNSS_CONSUME_AGE_SUM_US,
186 VELOXITY_DIAG_GNSS_CONSUME_AGE_MAX_US
187 );
188 record!(
189 battery,
190 VELOXITY_DIAG_BATTERY_CONSUME,
191 VELOXITY_DIAG_BATTERY_CONSUME_ERROR,
192 VELOXITY_DIAG_BATTERY_CONSUME_AGE_SUM_US,
193 VELOXITY_DIAG_BATTERY_CONSUME_AGE_MAX_US
194 );
195 record!(
196 rc,
197 VELOXITY_DIAG_RC_CONSUME,
198 VELOXITY_DIAG_RC_CONSUME_ERROR,
199 VELOXITY_DIAG_RC_CONSUME_AGE_SUM_US,
200 VELOXITY_DIAG_RC_CONSUME_AGE_MAX_US
201 );
202 }
203}
204
205fn wait_for_sd_response(request_id: u64) -> Option<ParamPacket> {
206 let start = Instant::now();
207 while start.elapsed() < PARAM_STORAGE_TIMEOUT {
208 match peripherals::sd_card::SD_READ_SIGNAL.try_take() {
209 Some(Ok(packet)) if packet.header.timestamp == request_id => return Some(packet),
210 Some(Ok(_)) => {}
211 Some(Err(_)) => return None,
212 None => {}
213 }
214 core::hint::spin_loop();
215 }
216 None
217}
218
219#[cfg(feature = "sensor-poll-diagnostics")]
220mod sensor_poll_diagnostics {
221 use core::sync::atomic::{AtomicU32, Ordering};
222
223 use veloxity_core::{errors::SensorError, math::FlightFloat};
224
225 #[unsafe(no_mangle)]
226 pub static VELOXITY_PIXRACER_DIAG_IMU: AtomicU32 = AtomicU32::new(0);
227 #[unsafe(no_mangle)]
228 pub static VELOXITY_PIXRACER_DIAG_IMU_ERR: AtomicU32 = AtomicU32::new(0);
229 #[unsafe(no_mangle)]
230 pub static VELOXITY_PIXRACER_DIAG_MAG: AtomicU32 = AtomicU32::new(0);
231 #[unsafe(no_mangle)]
232 pub static VELOXITY_PIXRACER_DIAG_MAG_ERR: AtomicU32 = AtomicU32::new(0);
233 #[unsafe(no_mangle)]
234 pub static VELOXITY_PIXRACER_DIAG_BARO: AtomicU32 = AtomicU32::new(0);
235 #[unsafe(no_mangle)]
236 pub static VELOXITY_PIXRACER_DIAG_BARO_ERR: AtomicU32 = AtomicU32::new(0);
237 #[unsafe(no_mangle)]
238 pub static VELOXITY_PIXRACER_DIAG_PITOT: AtomicU32 = AtomicU32::new(0);
239 #[unsafe(no_mangle)]
240 pub static VELOXITY_PIXRACER_DIAG_PITOT_ERR: AtomicU32 = AtomicU32::new(0);
241 #[unsafe(no_mangle)]
242 pub static VELOXITY_PIXRACER_DIAG_RANGE: AtomicU32 = AtomicU32::new(0);
243 #[unsafe(no_mangle)]
244 pub static VELOXITY_PIXRACER_DIAG_RANGE_ERR: AtomicU32 = AtomicU32::new(0);
245 #[unsafe(no_mangle)]
246 pub static VELOXITY_PIXRACER_DIAG_GNSS: AtomicU32 = AtomicU32::new(0);
247 #[unsafe(no_mangle)]
248 pub static VELOXITY_PIXRACER_DIAG_GNSS_ERR: AtomicU32 = AtomicU32::new(0);
249 #[unsafe(no_mangle)]
250 pub static VELOXITY_PIXRACER_DIAG_RC: AtomicU32 = AtomicU32::new(0);
251 #[unsafe(no_mangle)]
252 pub static VELOXITY_PIXRACER_DIAG_RC_ERR: AtomicU32 = AtomicU32::new(0);
253
254 pub fn record_bus<R: FlightFloat>(sensors: &veloxity_core::sensors::SensorBus<R>) {
255 record(
256 &sensors.imu,
257 &VELOXITY_PIXRACER_DIAG_IMU,
258 &VELOXITY_PIXRACER_DIAG_IMU_ERR,
259 );
260 record(
261 &sensors.mag,
262 &VELOXITY_PIXRACER_DIAG_MAG,
263 &VELOXITY_PIXRACER_DIAG_MAG_ERR,
264 );
265 record(
266 &sensors.baro,
267 &VELOXITY_PIXRACER_DIAG_BARO,
268 &VELOXITY_PIXRACER_DIAG_BARO_ERR,
269 );
270 record(
271 &sensors.pitot,
272 &VELOXITY_PIXRACER_DIAG_PITOT,
273 &VELOXITY_PIXRACER_DIAG_PITOT_ERR,
274 );
275 record(
276 &sensors.range,
277 &VELOXITY_PIXRACER_DIAG_RANGE,
278 &VELOXITY_PIXRACER_DIAG_RANGE_ERR,
279 );
280 record(
281 &sensors.gnss,
282 &VELOXITY_PIXRACER_DIAG_GNSS,
283 &VELOXITY_PIXRACER_DIAG_GNSS_ERR,
284 );
285 record(
286 &sensors.rc,
287 &VELOXITY_PIXRACER_DIAG_RC,
288 &VELOXITY_PIXRACER_DIAG_RC_ERR,
289 );
290 }
291
292 fn record<T>(sample: &Option<Result<T, SensorError>>, count: &AtomicU32, errors: &AtomicU32) {
293 if let Some(result) = sample {
294 count.fetch_add(1, Ordering::Relaxed);
295 if result.is_err() {
296 errors.fetch_add(1, Ordering::Relaxed);
297 }
298 }
299 }
300}
301
302fn spawn_task<S: Send>(
303 spawner: &embassy_executor::SendSpawner,
304 token: Result<embassy_executor::SpawnToken<S>, embassy_executor::SpawnError>,
305) {
306 spawner.spawn(token.expect("failed to allocate Embassy task"));
307}
308
309pub struct Board {
310 _probe: [Output<'static>; 3],
311 pub start_time: embassy_time::Instant,
312 test_pin_1: Output<'static>,
313 test_pin_2: Output<'static>,
314 pending_reset_to_bootloader: Option<bool>,
315 sd_request_id: u64,
316 #[cfg(feature = "sensor-poll-diagnostics")]
317 last_sbus_diag_ms: u32,
318 #[cfg(feature = "sensor-poll-diagnostics")]
319 sbus_rc_drains: u32,
320 #[cfg(feature = "sensor-poll-diagnostics")]
321 sbus_last_rc_status: u32,
322 #[cfg(feature = "sensor-poll-diagnostics")]
323 sbus_last_rc_lol: bool,
324}
325
326impl BoardIo for Board {
327 fn set_test_pin_1(&mut self, high: bool) {
328 if high {
329 self.test_pin_1.set_high();
330 } else {
331 self.test_pin_1.set_low();
332 }
333 }
334
335 fn set_test_pin_2(&mut self, high: bool) {
336 if high {
337 self.test_pin_2.set_high();
338 } else {
339 self.test_pin_2.set_low();
340 }
341 }
342
343 fn update_sensor_bus<R: FlightFloat>(&mut self, sensors: &mut SensorBus<R>) {
344 sensors.clear();
345 sensors.imu = peripherals::bmi08x::IMU_SIGNAL
346 .try_take()
347 .map(|result| result.map(|packet| packet.cast()));
348 sensors.mag = peripherals::ist8308::MAG_SIGNAL.try_take();
349 sensors.baro = peripherals::dps310::BARO_SIGNAL.try_take();
350 sensors.pitot = peripherals::ms4525::PITOT_SIGNAL.try_take();
351 sensors.range = peripherals::llv3hp::RANGE_SIGNAL.try_take();
352 sensors.gnss = peripherals::ublox::GNSS_SIGNAL.try_take();
353 sensors.battery = crate::battery::BATTERY_SIGNAL.try_take();
354 sensors.rc = peripherals::sbus::RC_CHANNEL.try_receive().ok();
355 #[cfg(feature = "runtime-diagnostics")]
356 runtime_sensor_diagnostics::record_bus(sensors, self.clock_micros());
357 #[cfg(feature = "sensor-poll-diagnostics")]
358 self.record_sbus_rc_drain(&sensors.rc);
359 #[cfg(feature = "sensor-poll-diagnostics")]
360 sensor_poll_diagnostics::record_bus(sensors);
361 #[cfg(feature = "sensor-poll-diagnostics")]
362 self.log_sbus_diagnostics_if_due();
363 #[cfg(not(feature = "scope-timing-pins"))]
364 if sensors.imu.is_some() {
365 let mut delay = Delay;
366 self.set_test_pin_1(true);
367 delay.delay_us(1u32);
368 self.set_test_pin_1(false);
369 }
370 }
371
372 fn imu_pending(&self) -> bool {
373 peripherals::bmi08x::IMU_SIGNAL.signaled()
374 }
375
376 fn update_imu_sensor<R: FlightFloat>(&mut self, sensors: &mut SensorBus<R>) {
377 sensors.clear();
378 sensors.imu = peripherals::bmi08x::IMU_SIGNAL
379 .try_take()
380 .map(|result| result.map(|packet| packet.cast()));
381 #[cfg(feature = "runtime-diagnostics")]
382 if sensors.imu.is_some() {
383 VELOXITY_DIAG_IMU_CONSUME.fetch_add(1, Ordering::Relaxed);
384 match &sensors.imu {
385 Some(Ok(packet)) => {
386 let age = self
387 .clock_micros()
388 .saturating_sub(packet.header.timestamp)
389 .min(u32::MAX as u64) as u32;
390 VELOXITY_DIAG_IMU_CONSUME_AGE_SUM_US.fetch_add(age, Ordering::Relaxed);
391 VELOXITY_DIAG_IMU_CONSUME_AGE_MAX_US.fetch_max(age, Ordering::Relaxed);
392 }
393 Some(Err(_)) => {
394 VELOXITY_DIAG_IMU_CONSUME_ERROR.fetch_add(1, Ordering::Relaxed);
395 }
396 None => {}
397 }
398 }
399 #[cfg(feature = "sensor-poll-diagnostics")]
400 sensor_poll_diagnostics::record_bus(sensors);
401 #[cfg(not(feature = "scope-timing-pins"))]
402 if sensors.imu.is_some() {
403 let mut delay = Delay;
404 self.set_test_pin_1(true);
405 delay.delay_us(1u32);
406 self.set_test_pin_1(false);
407 }
408 }
409
410 fn update_service_sensor_bus<R: FlightFloat>(&mut self, sensors: &mut SensorBus<R>) {
411 sensors.clear();
412 sensors.mag = peripherals::ist8308::MAG_SIGNAL.try_take();
413 sensors.baro = peripherals::dps310::BARO_SIGNAL.try_take();
414 sensors.pitot = peripherals::ms4525::PITOT_SIGNAL.try_take();
415 sensors.range = peripherals::llv3hp::RANGE_SIGNAL.try_take();
416 sensors.gnss = peripherals::ublox::GNSS_SIGNAL.try_take();
417 sensors.battery = crate::battery::BATTERY_SIGNAL.try_take();
418 sensors.rc = peripherals::sbus::RC_CHANNEL.try_receive().ok();
419 #[cfg(feature = "runtime-diagnostics")]
420 runtime_sensor_diagnostics::record_bus(sensors, self.clock_micros());
421 #[cfg(feature = "sensor-poll-diagnostics")]
422 self.record_sbus_rc_drain(&sensors.rc);
423 #[cfg(feature = "sensor-poll-diagnostics")]
424 sensor_poll_diagnostics::record_bus(sensors);
425 #[cfg(feature = "sensor-poll-diagnostics")]
426 self.log_sbus_diagnostics_if_due();
427 }
428
429 fn serial_rx_read(&mut self, buf: &mut [u8]) -> Option<Result<usize, errors::TelemError>> {
430 #[cfg(feature = "usb-vcp-serial")]
431 match peripherals::vcp::VCP_RX.try_read(buf) {
432 Ok(n) => {
433 return Some(Ok(n));
434 }
435 Err(embassy_sync::pipe::TryReadError::Empty) => {
436 return Some(Ok(0));
439 }
440 }
441
442 #[cfg(not(feature = "usb-vcp-serial"))]
443 match peripherals::telem::TELEM_RX.try_read(buf) {
444 Ok(n) => {
445 return Some(Ok(n));
446 }
447 Err(embassy_sync::pipe::TryReadError::Empty) => {
448 return Some(Ok(0));
451 }
452 }
453 }
454
455 fn configure_battery_monitor(&mut self, voltage_multiplier: f32, current_multiplier: f32) {
456 crate::battery::configure_multipliers(voltage_multiplier, current_multiplier);
457 }
458 fn serial_tx_write(&mut self, bytes: &[u8]) -> Option<Result<usize, errors::TelemError>> {
459 #[cfg(feature = "usb-vcp-serial")]
460 {
461 #[cfg(feature = "runtime-diagnostics")]
462 let message_id = (bytes.len() > 5 && bytes[0] == 0xFE).then_some(bytes[5]);
463 #[cfg(feature = "runtime-diagnostics")]
464 peripherals::vcp::record_frame_attempt(message_id);
465 let mut n = 0;
466 let len = bytes.len();
467 loop {
468 match peripherals::vcp::VCP_TX.try_write(&bytes[n..len]) {
469 Ok(wrote) => {
470 if wrote == (len - n) {
471 break;
472 } else {
473 n += wrote;
474 }
475 }
476 Err(_) => {
477 #[cfg(feature = "runtime-diagnostics")]
478 peripherals::vcp::record_frame_rejected(message_id, n != 0);
479 return Some(Err(errors::TelemError::GenericTelemError(
480 "Error Writing USB VCP Packet!",
481 )));
482 }
483 }
484 }
485 #[cfg(feature = "runtime-diagnostics")]
486 peripherals::vcp::record_frame_enqueued(message_id);
487 return Some(Ok(len));
488 }
489
490 #[cfg(not(feature = "usb-vcp-serial"))]
491 {
492 let mut n = 0;
493 let len = bytes.len();
494 loop {
495 match peripherals::telem::TELEM_TX.try_write(&bytes[n..len]) {
496 Ok(wrote) => {
497 if wrote == (len - n) {
498 break;
499 } else {
500 n += wrote;
501 }
502 }
503 Err(_) => {
504 return Some(Err(errors::TelemError::GenericTelemError(
505 "Error Writing Telem Packet!",
506 )));
507 }
508 }
509 }
510 Some(Ok(len))
511 }
512 }
513
514 fn clock_millis(&self) -> u32 {
515 self.start_time.elapsed().as_millis() as u32
516 }
517
518 fn clock_micros(&self) -> u64 {
519 self.start_time.elapsed().as_micros() as u64
520 }
521
522 fn read_params(&mut self, params: &mut Params) -> bool {
523 let request_id = self.next_sd_request_id();
524 peripherals::sd_card::SD_READ_SIGNAL.reset();
525 peripherals::sd_card::SD_READ_REQUEST_SIGNAL.signal(request_id);
526
527 let Some(packet) = wait_for_sd_response(request_id) else {
528 return false;
529 };
530 let Some(stored) = peripherals::sd_card::decode_params(&packet) else {
531 return false;
532 };
533
534 *params = stored;
535 true
536 }
537
538 fn write_params(&mut self, params: &Params) -> bool {
539 let Some(mut packet) = peripherals::sd_card::encode_params(params) else {
540 return false;
541 };
542 let request_id = self.next_sd_request_id();
543 packet.header.timestamp = request_id;
544
545 peripherals::sd_card::SD_READ_SIGNAL.reset();
546 peripherals::sd_card::SD_WRITE_SIGNAL.signal(Ok(packet));
547
548 matches!(
549 wait_for_sd_response(request_id),
550 Some(packet) if packet.header.status == 1
551 )
552 }
553
554 fn reboot(&mut self) -> bool {
555 self.pending_reset_to_bootloader = Some(false);
556 true
557 }
558
559 fn reboot_to_bootloader(&mut self) -> bool {
560 self.pending_reset_to_bootloader = Some(true);
561 true
562 }
563
564 fn run_deferred_board_actions(&mut self) {
565 if self.pending_reset_to_bootloader.take().is_some() {
566 let mut delay = Delay;
567 delay.delay_ms(20u32);
568 stm_32::cortex_m::peripheral::SCB::sys_reset();
569 }
570 }
571}
572
573impl Board {
574 fn next_sd_request_id(&mut self) -> u64 {
575 self.sd_request_id = self.sd_request_id.wrapping_add(1);
576 self.sd_request_id
577 }
578
579 pub fn new() -> (Board, PixRacerProServoMonstrosity) {
580 let p: EMBASSY_Peripherals = embassy_stm32::init(clock_config(24));
581
582 let start_time = embassy_time::Instant::now();
583
584 let battery_monitor = crate::battery::PixracerBatteryMonitor::new(
585 p.ADC1, p.ADC3, p.PA2, p.PA3, p.DMA2_CH0, p.DMA2_CH1,
586 );
587
588 let mut spi1_config: embassy_stm32::spi::Config = spi::Config::default();
590 spi1_config.frequency = mhz(16); spi1_config.mode = spi::MODE_3;
592 spi1_config.bit_order = spi::BitOrder::MsbFirst;
593 spi1_config.miso_pull = embassy_stm32::gpio::Pull::Up;
594 let spi1 = spi::Spi::new(
595 p.SPI1,
596 p.PA5,
597 p.PA7,
598 p.PA6,
599 p.DMA1_CH0,
600 p.DMA1_CH1,
601 BoardIrqs,
602 spi1_config,
603 );
604 let spi1_bus = Mutex::new(spi1);
605 let _spi1_bus = SPI1_BUS.init(spi1_bus);
606
607 let mut spi2_config: embassy_stm32::spi::Config = spi::Config::default();
609 spi2_config.frequency = mhz(16); spi2_config.mode = spi::MODE_3;
611 spi2_config.bit_order = spi::BitOrder::MsbFirst;
612 spi2_config.miso_pull = embassy_stm32::gpio::Pull::Up;
613 let spi2 = spi::Spi::new(
614 p.SPI2,
615 p.PB10, p.PB15, p.PB14, p.DMA1_CH2,
619 p.DMA1_CH3,
620 BoardIrqs,
621 spi2_config,
622 );
623 let spi2_bus = Mutex::new(spi2);
624 let spi2_bus = SPI2_BUS.init(spi2_bus);
625
626 let nss2 = Output::new(p.PD7, Level::High, Speed::Low);
628 let drdy2 = ExtiInput::new(p.PD15, p.EXTI15, Pull::Down, BoardIrqs);
630 let dps_dev = SpiDevice::new(spi2_bus, nss2);
631 let dps_sensor = peripherals::dps310::Dps310Sensor {
632 dev: dps_dev,
633 drdy: drdy2,
634 three_wire: false,
635 };
636
637 let mut i2c_config = i2c::Config::default();
639 i2c_config.scl_pullup = true;
640 i2c_config.sda_pullup = true;
641 i2c_config.frequency = Hertz(100_000);
642 let i2c1 = i2c::I2c::new(
643 p.I2C1, p.PB8, p.PB9, p.DMA2_CH2, p.DMA2_CH3, BoardIrqs, i2c_config,
644 );
645 let i2c1_bus = Mutex::new(i2c1);
646 let i2c1_bus = I2C1_BUS.init(i2c1_bus);
647
648 let ist8303_sensor = peripherals::ist8308::Ist8308Sensor {
650 dev: I2cDevice::new(i2c1_bus),
651 };
652
653 let ms4525_sensor = peripherals::ms4525::Ms4525Sensor {
655 dev: I2cDevice::new(i2c1_bus),
656 };
657
658 let llv3hp_sensor = peripherals::llv3hp::Llv3hpSensor {
659 dev: I2cDevice::new(i2c1_bus),
660 };
661
662 let mut uart3config = usart::Config::default();
664 uart3config.rx_pull = Pull::Up;
665 uart3config.baudrate = 921600;
666 let uart3 = Uart::new(
667 p.USART3,
668 p.PD9,
669 p.PD8,
670 p.DMA2_CH4,
671 p.DMA2_CH5,
672 BoardIrqs,
673 uart3config,
674 )
675 .unwrap();
676 let (uart3_tx, uart3_rx) = uart3.split();
677
678 let telem3_rx = peripherals::telem::TelemRx {
679 uart_rx: uart3_rx,
680 byte_processor: stm_32::peripherals::telem::BasicProcessor {},
681 };
682
683 let telem3_tx = peripherals::telem::TelemTx { uart_tx: uart3_tx };
684
685 static EP_BUF_CELL: StaticCell<[u8; 256]> = StaticCell::new();
687 let mut config = embassy_stm32::usb::Config::default();
688 config.vbus_detection = true;
689 let driver = Driver::new_fs(
690 p.USB_OTG_FS,
691 Irqs,
692 p.PA12,
693 p.PA11,
694 EP_BUF_CELL.init([0u8; 256]),
695 config,
696 );
697 let vcp = peripherals::vcp::Vcp {
698 driver,
699 byte_processor: stm_32::peripherals::vcp::BasicProcessor {},
700 };
701
702 let mut uart4config = usart::Config::default();
704 uart4config.baudrate = 9600u32;
705 uart4config.rx_pull = Pull::Up;
706 let uart4 = Uart::new(
707 p.UART4,
708 p.PA1,
709 p.PA0,
710 p.DMA2_CH6,
711 p.DMA2_CH7,
712 BoardIrqs,
713 uart4config,
714 )
715 .unwrap();
716
717 let ublox_sensor = peripherals::ublox::UbloxSensor {
719 uart: uart4,
720 protocol: peripherals::ublox::Protocol::M8,
721 baudrate: peripherals::ublox::Bitrate::Baud230400,
722 nav_period_ms: 100u16,
723 };
724 let drdy_pps = ExtiInput::new(p.PG12, p.EXTI12, Pull::Down, BoardIrqs);
725 let pps_sensor = peripherals::pps::PpsSensor { pps: drdy_pps };
726
727 let mut uart6config = usart::Config::default();
730 uart6config.baudrate = 100000u32;
731 uart6config.parity = usart::Parity::ParityEven;
732 uart6config.stop_bits = usart::StopBits::STOP2;
733 uart6config.invert_rx = true;
734 uart6config.invert_tx = true;
735 uart6config.data_bits = usart::DataBits::DataBits8;
736
737 let usart6 = Uart::new(
738 p.USART6,
739 p.PC7,
740 p.PC6,
741 p.DMA1_CH4,
742 p.DMA1_CH5,
743 BoardIrqs,
744 uart6config,
745 )
746 .unwrap();
747 let (_uart6_tx, uart6_rx) = usart6.split();
748 let sbus_rx = peripherals::sbus::SbusRC { uart: uart6_rx };
749
750 let sdmmc1 = sdmmc::Sdmmc::new_4bit(
752 p.SDMMC1,
753 BoardIrqs,
754 p.PC12,
755 p.PD2,
756 p.PC8,
757 p.PC9,
758 p.PC10,
759 p.PC11,
760 Default::default(),
761 );
762
763 let mut spi5_config: embassy_stm32::spi::Config = spi::Config::default();
765 spi5_config.frequency = mhz(2); spi5_config.mode = spi::MODE_3;
767 spi5_config.bit_order = spi::BitOrder::MsbFirst;
768 spi5_config.miso_pull = embassy_stm32::gpio::Pull::Up;
769 let spi5 = spi::Spi::new(
770 p.SPI5,
771 p.PF7, p.PF9, p.PF8, p.DMA1_CH6, p.DMA1_CH7, BoardIrqs,
777 spi5_config,
778 );
779 let spi5_ = Mutex::new(spi5);
780 let spi5_bus = SPI5_BUS.init(spi5_);
781
782 let nss_bmi08x_a = Output::new(p.PF6, Level::High, Speed::Low); let drdy_bmi08x_a = ExtiInput::new(p.PF1, p.EXTI1, Pull::Down, BoardIrqs); let nss_bmi08x_g = Output::new(p.PF10, Level::High, Speed::Low); let drdy_bmi08x_g = ExtiInput::new(p.PF3, p.EXTI3, Pull::Down, BoardIrqs); let bmi08x_dev_a = SpiDevice::new(spi5_bus, nss_bmi08x_a);
788 let bmi08x_dev_g = SpiDevice::new(spi5_bus, nss_bmi08x_g);
789 let jumper: Output<'static> = Output::new(p.PF2, Level::High, Speed::Low); let bmi08x_sensor = peripherals::bmi08x::Bmi08xSensor {
791 dev_a: bmi08x_dev_a,
792 dev_g: bmi08x_dev_g,
793 drdy_a: drdy_bmi08x_a,
794 drdy_g: drdy_bmi08x_g,
795 jumper: jumper,
796 range_a: peripherals::bmi08x::AccelRange::Bmi085(
797 peripherals::bmi08x::AccelRange085::Max16g,
798 ),
799 range_g: peripherals::bmi08x::GyroRange::Max500dps,
800 sample_rate: peripherals::bmi08x::SampleRate::Odr400Hz,
801 board_axis_signs: [1.0, 1.0, 1.0],
805 };
806
807 let usd_detect = embassy_stm32::gpio::Input::new(p.PG3, Pull::None); let usd_card = peripherals::sd_card::SdCard {
810 sdmmc: sdmmc1,
811 detect: usd_detect,
812 };
813
814 interrupt::SAI1.set_priority(Priority::P0);
816 let spawner1 = P1_EXECUTOR.start(interrupt::SAI1);
817 spawn_task(&spawner1, peripherals::bmi08x::task(bmi08x_sensor));
818
819 interrupt::SAI2.set_priority(Priority::P2);
821 let spawner2 = P2_EXECUTOR.start(interrupt::SAI2);
822
823 spawn_task(&spawner2, peripherals::vcp::task(vcp));
825 spawn_task(&spawner2, peripherals::telem::task_rx(telem3_rx));
826
827 interrupt::SAI3.set_priority(Priority::P3);
829 let spawner3 = P3_EXECUTOR.start(interrupt::SAI3);
830 spawn_task(&spawner3, peripherals::ist8308::task(ist8303_sensor));
831 spawn_task(&spawner3, peripherals::ms4525::task(ms4525_sensor));
832 spawn_task(&spawner3, peripherals::dps310::task(dps_sensor));
833 spawn_task(&spawner3, peripherals::ublox::task(ublox_sensor));
834 spawn_task(&spawner3, peripherals::pps::task(pps_sensor));
835 spawn_task(&spawner3, peripherals::sbus::task(sbus_rx));
836 spawn_task(&spawner3, peripherals::llv3hp::task(llv3hp_sensor));
837 spawn_task(&spawner3, crate::battery::task(battery_monitor));
838
839 interrupt::SAI4.set_priority(Priority::P4);
841 let spawner4 = P4_EXECUTOR.start(interrupt::SAI4);
842 spawn_task(&spawner4, peripherals::telem::task_tx(telem3_tx));
843 spawn_task(&spawner4, peripherals::sd_card::task(usd_card));
844
845 let tim1_ch1_pin = PwmPin::<_, embassy_stm32::timer::Ch1>::new(p.PE9, OutputType::PushPull);
849 let tim1_ch2_pin =
850 PwmPin::<_, embassy_stm32::timer::Ch2>::new(p.PE11, OutputType::PushPull);
851 let tim1_ch3_pin =
852 PwmPin::<_, embassy_stm32::timer::Ch3>::new(p.PE13, OutputType::PushPull);
853 let tim1_ch4_pin =
854 PwmPin::<_, embassy_stm32::timer::Ch4>::new(p.PE14, OutputType::PushPull);
855 let tim4_ch2_pin =
856 PwmPin::<_, embassy_stm32::timer::Ch2>::new(p.PD13, OutputType::PushPull);
857 let tim4_ch3_pin =
858 PwmPin::<_, embassy_stm32::timer::Ch3>::new(p.PD14, OutputType::PushPull);
859 let tim8_ch1_pin = PwmPin::<_, embassy_stm32::timer::Ch1>::new(p.PI5, OutputType::PushPull);
860 let tim8_ch2_pin = PwmPin::<_, embassy_stm32::timer::Ch2>::new(p.PI6, OutputType::PushPull);
861
862 let timer1 = SimplePwm::new(
863 p.TIM1,
864 Some(tim1_ch1_pin),
865 Some(tim1_ch2_pin),
866 Some(tim1_ch3_pin),
867 Some(tim1_ch4_pin),
868 Hertz::hz(400),
869 Default::default(),
870 );
871 let timer4 = SimplePwm::new(
872 p.TIM4,
873 None,
874 Some(tim4_ch2_pin),
875 Some(tim4_ch3_pin),
876 None,
877 Hertz::hz(400),
878 Default::default(),
879 );
880 let timer8 = SimplePwm::new(
881 p.TIM8,
882 Some(tim8_ch1_pin),
883 Some(tim8_ch2_pin),
884 None,
885 None,
886 Hertz::hz(400),
887 Default::default(),
888 );
889
890 let timer1 = peripherals::pwm::TimerEnum::TIM1(timer1);
891 let timer4 = peripherals::pwm::TimerEnum::TIM4(timer4);
892 let timer8 = peripherals::pwm::TimerEnum::TIM8(timer8);
893
894 let timers: [peripherals::pwm::TimerEnum; 3] = [timer1, timer4, timer8];
895
896 let servos = peripherals::pwm::PixRacerProServoMonstrosity::with_timer_kinds_and_dma(
897 timers,
898 [
899 (0, peripherals::pwm::TimerChannel::Ch4), (0, peripherals::pwm::TimerChannel::Ch3), (0, peripherals::pwm::TimerChannel::Ch2), (0, peripherals::pwm::TimerChannel::Ch1), (1, peripherals::pwm::TimerChannel::Ch2), (1, peripherals::pwm::TimerChannel::Ch3), (2, peripherals::pwm::TimerChannel::Ch1), (2, peripherals::pwm::TimerChannel::Ch2), ],
908 [
909 peripherals::pwm::PwmTimerBlockKind::StandardOnly,
910 peripherals::pwm::PwmTimerBlockKind::StandardOnly,
911 peripherals::pwm::PwmTimerBlockKind::StandardOnly,
912 ],
913 [None, None, None],
914 );
915
916 #[cfg_attr(feature = "scope-timing-pins", allow(unused_mut))]
918 let mut test_pin_1 = Output::new(p.PD11, Level::Low, Speed::VeryHigh);
919 #[cfg(not(feature = "scope-timing-pins"))]
920 test_pin_1.set_high();
921 #[cfg_attr(feature = "scope-timing-pins", allow(unused_mut))]
922 let mut test_pin_2 = Output::new(p.PD12, Level::Low, Speed::VeryHigh);
923 #[cfg(not(feature = "scope-timing-pins"))]
924 test_pin_2.set_high();
925
926 let probe = [
928 Output::new(p.PG13, Level::Low, Speed::Low),
929 Output::new(p.PG9, Level::Low, Speed::Low),
930 Output::new(p.PG14, Level::Low, Speed::Low),
931 ];
933 (
934 Board {
935 _probe: probe,
936 start_time,
937 test_pin_1,
938 test_pin_2,
939 pending_reset_to_bootloader: None,
940 sd_request_id: 0,
941 #[cfg(feature = "sensor-poll-diagnostics")]
942 last_sbus_diag_ms: 0,
943 #[cfg(feature = "sensor-poll-diagnostics")]
944 sbus_rc_drains: 0,
945 #[cfg(feature = "sensor-poll-diagnostics")]
946 sbus_last_rc_status: 0,
947 #[cfg(feature = "sensor-poll-diagnostics")]
948 sbus_last_rc_lol: false,
949 },
950 servos,
951 )
952 }
953
954 #[cfg(feature = "sensor-poll-diagnostics")]
955 fn record_sbus_rc_drain(
956 &mut self,
957 rc: &Option<Result<veloxity_core::packets::RcPacket, errors::SensorError>>,
958 ) {
959 if let Some(result) = rc {
960 self.sbus_rc_drains = self.sbus_rc_drains.wrapping_add(1);
961 if let Ok(packet) = result {
962 self.sbus_last_rc_status = packet.header.status as u32;
963 self.sbus_last_rc_lol = packet.lol;
964 }
965 }
966 }
967
968 #[cfg(feature = "sensor-poll-diagnostics")]
969 fn log_sbus_diagnostics_if_due(&mut self) {
970 let now_ms = self.clock_millis();
971 if now_ms.wrapping_sub(self.last_sbus_diag_ms) < 1_000 {
972 return;
973 }
974 self.last_sbus_diag_ms = now_ms;
975
976 let sbus = peripherals::sbus::diagnostics();
977 veloxity_core::log_info!(
978 "SBUS rx{} e{} sz{} n{} v{}",
979 sbus.read_ok,
980 sbus.read_err,
981 sbus.last_read_size,
982 sbus.size_25,
983 sbus.valid_frame
984 );
985 veloxity_core::log_info!(
986 "SBUS bh{} bf{} sig{} to{} dr{}",
987 sbus.bad_header,
988 sbus.bad_footer,
989 sbus.signal,
990 sbus.timeout,
991 self.sbus_rc_drains
992 );
993 veloxity_core::log_info!(
994 "SBUS st{} rst{} lol{}",
995 sbus.last_status,
996 self.sbus_last_rc_status,
997 self.sbus_last_rc_lol as u8
998 );
999 }
1000}