1use core::sync::atomic::AtomicBool;
21
22use arbitrary_int::{prelude::*, u11, u15, u2, u3, u4, u7};
23use embedded_can::Frame;
24use ll::CanChannelLowLevel;
25use regs::{BaseId, BufferState, Control, MmioCan, TimingConfig};
26use vorago_shared_hal::enable_nvic_interrupt;
27
28use crate::{clock::Clocks, enable_peripheral_clock, time::Hertz, PeripheralSelect};
29use libm::roundf;
30
31pub mod frame;
32pub use frame::*;
33
34pub mod asynch;
35pub mod ll;
36pub mod regs;
37
38pub const PRESCALER_MIN: u8 = 2;
39pub const PRESCALER_MAX: u8 = 128;
40pub const TSEG1_MIN: u8 = 1;
42pub const TSEG1_MAX: u8 = 16;
43pub const TSEG2_MAX: u8 = 8;
44pub const SJW_MAX: u8 = 4;
46
47pub const MIN_SAMPLE_POINT: f32 = 0.5;
48pub const MAX_BITRATE_DEVIATION: f32 = 0.005;
49
50static CHANNELS_TAKEN: [AtomicBool; 2] = [AtomicBool::new(false), AtomicBool::new(false)];
51
52#[derive(Debug, PartialEq, Eq, Clone, Copy)]
53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
54pub enum CanId {
55 Can0 = 0,
56 Can1 = 1,
57}
58
59impl CanId {
60 #[inline]
66 pub const unsafe fn steal_regs(&self) -> regs::MmioCan<'static> {
67 match self {
68 CanId::Can0 => unsafe { regs::Can::new_mmio_fixed_0() },
69 CanId::Can1 => unsafe { regs::Can::new_mmio_fixed_1() },
70 }
71 }
72
73 #[inline]
74 pub const fn irq_id(&self) -> va416xx::Interrupt {
75 match self {
76 CanId::Can0 => va416xx::Interrupt::CAN0,
77 CanId::Can1 => va416xx::Interrupt::CAN1,
78 }
79 }
80}
81
82pub const fn calculate_sample_point(tseg1: u8, tseg2: u8) -> f32 {
84 let tseg1_val = tseg1 as f32;
85 (tseg1_val + 1.0) / (1.0 + tseg1_val + tseg2 as f32)
86}
87
88#[derive(Debug, Clone, Copy)]
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90pub struct ClockConfig {
91 prescaler: u8,
92 tseg1: u8,
93 tseg2: u8,
94 sjw: u8,
95}
96
97impl ClockConfig {
98 pub fn new(prescaler: u8, tseg1: u8, tseg2: u8, sjw: u8) -> Result<Self, ClockConfigError> {
112 if !(PRESCALER_MIN..=PRESCALER_MAX).contains(&prescaler.value()) {
113 return Err(ClockConfigError::CanNotFindPrescaler);
114 }
115 if tseg1 == 0 || tseg2 == 0 {
116 return Err(ClockConfigError::TsegIsZero);
117 }
118 if tseg1 > TSEG1_MAX {
119 return Err(ClockConfigError::InvalidTseg1);
120 }
121 if tseg2 > TSEG2_MAX {
122 return Err(ClockConfigError::InvalidTseg2);
123 }
124 let smaller_tseg = core::cmp::min(tseg1.value(), tseg2.value());
125 if sjw.value() > smaller_tseg || sjw > SJW_MAX {
126 return Err(InvalidSjwError(sjw).into());
127 }
128 let sample_point = calculate_sample_point(tseg1, tseg2);
129 if sample_point < MIN_SAMPLE_POINT {
130 return Err(InvalidSamplePointError { sample_point }.into());
131 }
132 Ok(Self {
133 prescaler,
134 tseg1,
135 tseg2,
136 sjw,
137 })
138 }
139
140 pub fn from_bitrate_and_segments(
147 clocks: &Clocks,
148 bitrate: Hertz,
149 tseg1: u8,
150 tseg2: u8,
151 sjw: u8,
152 ) -> Result<ClockConfig, ClockConfigError> {
153 if bitrate.raw() == 0 {
154 return Err(ClockConfigError::BitrateIsZero);
155 }
156 let nominal_bit_time = 1 + tseg1 as u32 + tseg2 as u32;
157 let prescaler =
158 roundf(clocks.apb1().raw() as f32 / (bitrate.raw() as f32 * nominal_bit_time as f32))
159 as u32;
160 if !(PRESCALER_MIN as u32..=PRESCALER_MAX as u32).contains(&prescaler) {
161 return Err(ClockConfigError::CanNotFindPrescaler);
162 }
163
164 let actual_bitrate = (clocks.apb1().raw() as f32) / (prescaler * nominal_bit_time) as f32;
165 let bitrate_deviation = calculate_bitrate_deviation(actual_bitrate, bitrate);
166 if bitrate_deviation > MAX_BITRATE_DEVIATION {
167 return Err(ClockConfigError::BitrateErrorTooLarge);
168 }
169 Self::new(prescaler as u8, tseg1, tseg2, sjw)
171 }
172
173 #[inline]
174 pub fn sjw_reg_value(&self) -> u2 {
175 u2::new(self.sjw.value() - 1)
176 }
177
178 #[inline]
179 pub fn tseg1_reg_value(&self) -> u4 {
180 u4::new(self.tseg1.value() - 1)
181 }
182
183 #[inline]
184 pub fn tseg2_reg_value(&self) -> u3 {
185 u3::new(self.tseg2.value() - 1)
186 }
187
188 #[inline]
189 pub fn prescaler_reg_value(&self) -> u7 {
190 u7::new(self.prescaler.value() - 2)
191 }
192}
193
194#[cfg(feature = "alloc")]
214pub fn calculate_all_viable_clock_configs(
215 apb1_clock: Hertz,
216 bitrate: Hertz,
217 sample_point: f32,
218) -> Result<alloc::vec::Vec<(ClockConfig, f32)>, InvalidSamplePointError> {
219 if sample_point < 0.5 || sample_point > 1.0 {
220 return Err(InvalidSamplePointError { sample_point });
221 }
222 let mut configs = alloc::vec::Vec::new();
223 for prescaler in PRESCALER_MIN..PRESCALER_MAX {
224 let nom_bit_time = calculate_nominal_bit_time(apb1_clock, bitrate, prescaler);
225 if nom_bit_time < 8 {
227 break;
228 }
229 let actual_bitrate = calculate_actual_bitrate(apb1_clock, prescaler, nom_bit_time);
230 let bitrate_deviation = calculate_bitrate_deviation(actual_bitrate, bitrate);
231 if bitrate_deviation > 0.05 {
232 continue;
233 }
234 let tseg1 = roundf(sample_point * nom_bit_time as f32) as u32 - 1;
235 if tseg1 > TSEG1_MAX as u32 || tseg1 < TSEG1_MIN as u32 {
236 continue;
237 }
238 let tseg1 = core::cmp::min(tseg1, nom_bit_time - 2) as u8;
240 let tseg2 = nom_bit_time - tseg1 as u32 - 1;
241 if tseg2 > TSEG2_MAX as u32 {
242 continue;
243 }
244 let tseg2 = tseg2 as u8;
245 let sjw = core::cmp::min(tseg2, 4) as u8;
246 let sample_point_actual = roundf(calculate_sample_point(tseg1, tseg2) * 100.0) as u32;
248 let sample_point = roundf(sample_point * 100.0) as u32;
249 let deviation = (sample_point_actual as i32 - sample_point as i32).abs();
250 if deviation > 5 {
251 continue;
252 }
253 configs.push((
254 ClockConfig {
255 prescaler,
256 tseg1,
257 tseg2,
258 sjw,
259 },
260 bitrate_deviation,
261 ));
262 }
263 Ok(configs)
264}
265
266#[inline]
267pub const fn calculate_nominal_bit_time(
268 apb1_clock: Hertz,
269 target_bitrate: Hertz,
270 prescaler: u8,
271) -> u32 {
272 apb1_clock.raw() / (target_bitrate.raw() * prescaler as u32)
273}
274
275#[inline]
276pub const fn calculate_actual_bitrate(apb1_clock: Hertz, prescaler: u8, nom_bit_time: u32) -> f32 {
277 apb1_clock.raw() as f32 / (prescaler as u32 * nom_bit_time) as f32
278}
279
280#[inline]
281pub const fn calculate_bitrate_deviation(actual_bitrate: f32, target_bitrate: Hertz) -> f32 {
282 (actual_bitrate - target_bitrate.raw() as f32).abs() / target_bitrate.raw() as f32
283}
284
285pub trait CanInstance {
286 const ID: CanId;
287 const IRQ: va416xx::Interrupt;
288 const PERIPH_SEL: PeripheralSelect;
289}
290
291impl CanInstance for va416xx::Can0 {
292 const ID: CanId = CanId::Can0;
293 const IRQ: va416xx::Interrupt = va416xx::Interrupt::CAN0;
294 const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
295}
296
297impl CanInstance for va416xx::Can1 {
298 const ID: CanId = CanId::Can1;
299 const IRQ: va416xx::Interrupt = va416xx::Interrupt::CAN1;
300 const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
301}
302
303#[derive(Debug, thiserror::Error)]
304#[cfg_attr(feature = "defmt", derive(defmt::Format))]
305#[error("invalid buffer index {0}")]
306pub struct InvalidBufferIndexError(usize);
307
308#[derive(Debug, thiserror::Error)]
309#[cfg_attr(feature = "defmt", derive(defmt::Format))]
310#[error("sjw must be less than or equal to the smaller tseg value")]
311pub struct InvalidSjwError(u8);
312
313#[derive(Debug, thiserror::Error)]
314#[error("invalid sample point {sample_point}")]
315#[cfg_attr(feature = "defmt", derive(defmt::Format))]
316pub struct InvalidSamplePointError {
317 sample_point: f32,
319}
320
321#[derive(Debug, thiserror::Error)]
322#[cfg_attr(feature = "defmt", derive(defmt::Format))]
323pub enum ClockConfigError {
324 #[error("invalid sjw: {0}")]
325 InvalidSjw(#[from] InvalidSjwError),
326 #[error("TSEG is zero which is not allowed")]
327 TsegIsZero,
328 #[error("TSEG1 is larger than 16")]
329 InvalidTseg1,
330 #[error("TSEG1 is larger than 8")]
331 InvalidTseg2,
332 #[error("invalid sample point: {0}")]
333 InvalidSamplePoint(#[from] InvalidSamplePointError),
334 #[error("bitrate is zero")]
335 BitrateIsZero,
336 #[error("bitrate error larger than +-0.5 %")]
337 BitrateErrorTooLarge,
338 #[error("maximum or minimum allowed prescaler is not sufficient for target bitrate clock")]
339 CanNotFindPrescaler,
340}
341
342pub struct Can {
344 regs: regs::MmioCan<'static>,
345 id: CanId,
346}
347
348impl Can {
349 pub fn new<CanI: CanInstance>(_can: CanI, clk_config: ClockConfig) -> Self {
350 enable_peripheral_clock(CanI::PERIPH_SEL);
351 let id = CanI::ID;
352 let mut regs = if id == CanId::Can0 {
353 unsafe { regs::Can::new_mmio_fixed_0() }
354 } else {
355 unsafe { regs::Can::new_mmio_fixed_1() }
356 };
357 regs.write_control(Control::new_with_raw_value(0));
359 for i in 0..15 {
360 regs.cmbs(i).unwrap().reset();
361 }
362 regs.write_timing(
363 TimingConfig::builder()
364 .with_tseg2(clk_config.tseg2_reg_value())
365 .with_tseg1(clk_config.tseg1_reg_value())
366 .with_sync_jump_width(clk_config.sjw_reg_value())
367 .with_prescaler(clk_config.prescaler_reg_value())
368 .build(),
369 );
370 Self { regs, id }
371 }
372
373 pub fn set_global_mask_for_exact_id_match(&mut self) {
377 self.regs
378 .write_gmskx(regs::ExtendedId::new_with_raw_value(0));
379 self.regs.write_gmskb(BaseId::new_with_raw_value(0));
380 }
381
382 pub fn take_channels(&self) -> Option<CanChannels> {
384 if CHANNELS_TAKEN[self.id() as usize].swap(true, core::sync::atomic::Ordering::SeqCst) {
385 return None;
386 }
387 Some(CanChannels::new(self.id))
388 }
389
390 pub fn set_global_mask_for_exact_id_match_with_rtr_masked(&mut self) {
397 self.regs.write_gmskx(
398 regs::ExtendedId::builder()
399 .with_mask_14_0(u15::new(0))
400 .with_xrtr(true)
401 .build(),
402 );
403 self.regs.write_gmskb(
404 BaseId::builder()
405 .with_mask_28_18(u11::new(0))
406 .with_rtr_or_srr(true)
407 .with_ide(false)
408 .with_mask_17_15(u3::new(0))
409 .build(),
410 );
411 }
412
413 #[inline]
417 pub fn set_base_mask_for_exact_id_match(&mut self) {
418 self.regs
419 .write_bmskx(regs::ExtendedId::new_with_raw_value(0));
420 self.regs.write_bmskb(BaseId::new_with_raw_value(0));
421 }
422
423 #[inline]
426 pub fn set_base_mask_for_all_match(&mut self) {
427 self.regs
428 .write_bmskx(regs::ExtendedId::new_with_raw_value(0xffff));
429 self.regs.write_bmskb(BaseId::new_with_raw_value(0xffff));
430 }
431
432 #[inline]
433 pub fn regs(&mut self) -> &mut MmioCan<'static> {
434 &mut self.regs
435 }
436
437 #[inline]
439 pub fn clear_interrupts(&mut self) {
440 self.regs
441 .write_iclr(regs::InterruptClear::new_with_raw_value(0xFFFF_FFFF));
442 }
443
444 #[inline]
449 pub fn enable_nvic_interrupt(&mut self) {
450 unsafe {
451 enable_nvic_interrupt(self.id().irq_id());
452 }
453 }
454
455 #[inline]
456 pub fn read_error_counters(&self) -> regs::ErrorCounter {
457 self.regs.read_error_counter()
458 }
459
460 #[inline]
461 pub fn read_error_diagnostics(&self) -> regs::DiagnosticRegister {
462 self.regs.read_diag()
463 }
464
465 #[inline]
466 pub fn id(&self) -> CanId {
467 self.id
468 }
469
470 #[inline]
471 pub fn write_ctrl_reg(&mut self, ctrl: Control) {
472 self.regs.write_control(ctrl);
473 }
474
475 #[inline]
476 pub fn modify_control<F>(&mut self, f: F)
477 where
478 F: FnOnce(Control) -> Control,
479 {
480 self.regs.modify_control(f);
481 }
482
483 #[inline]
484 pub fn set_bufflock(&mut self, enable: bool) {
485 self.regs.modify_control(|mut ctrl| {
486 ctrl.set_bufflock(enable);
487 ctrl
488 });
489 }
490
491 #[inline]
492 pub fn enable(&mut self) {
493 self.regs.modify_control(|mut ctrl| {
494 ctrl.set_enable(true);
495 ctrl
496 });
497 }
498}
499
500#[derive(Debug, PartialEq, Eq, Clone, Copy)]
501#[cfg_attr(feature = "defmt", derive(defmt::Format))]
502pub enum TxState {
503 Idle,
504 TransmittingDataFrame,
505 TransmittingRemoteFrame,
506 AwaitingRemoteFrameReply,
507}
508
509#[derive(Debug)]
510#[cfg_attr(feature = "defmt", derive(defmt::Format))]
511pub enum InvalidTxState {
512 State(TxState),
513 BufferState(BufferState),
514}
515
516impl From<TxState> for InvalidTxState {
517 fn from(state: TxState) -> Self {
518 InvalidTxState::State(state)
519 }
520}
521
522impl From<BufferState> for InvalidTxState {
523 fn from(state: BufferState) -> Self {
524 InvalidTxState::BufferState(state)
525 }
526}
527
528#[derive(Debug, thiserror::Error)]
529#[error("invalid tx state {0:?}")]
530#[cfg_attr(feature = "defmt", derive(defmt::Format))]
531pub struct InvalidTxStateError(pub InvalidTxState);
532
533#[derive(Debug, PartialEq, Eq, Clone, Copy)]
534#[cfg_attr(feature = "defmt", derive(defmt::Format))]
535pub enum RxState {
536 Idle,
537 Receiving,
538}
539
540#[derive(Debug, thiserror::Error)]
541#[error("invalid rx state {0:?}")]
542#[cfg_attr(feature = "defmt", derive(defmt::Format))]
543pub struct InvalidRxStateError(pub RxState);
544
545#[derive(Debug)]
547pub struct CanTx {
548 ll: CanChannelLowLevel,
549 mode: TxState,
550}
551
552impl CanTx {
553 pub fn new(mut ll: CanChannelLowLevel, tx_priority: Option<u4>) -> Self {
554 ll.reset();
555 ll.configure_for_transmission(tx_priority);
556 Self {
557 ll,
558 mode: TxState::Idle,
559 }
560 }
561
562 #[inline]
563 pub fn into_rx_channel(self) -> CanRx {
564 CanRx::new(self.ll)
565 }
566
567 pub fn transmit_frame(&mut self, frame: CanFrame) -> Result<(), InvalidTxStateError> {
575 if self.mode == TxState::AwaitingRemoteFrameReply {
576 self.ll.configure_for_transmission(None);
577 self.mode = TxState::Idle;
578 }
579 if self.mode != TxState::Idle {
580 return Err(InvalidTxStateError(self.mode.into()));
581 }
582 if !frame.is_remote_frame() {
583 self.mode = TxState::TransmittingDataFrame;
584 } else {
585 self.mode = TxState::TransmittingRemoteFrame;
586 }
587 if let Ok(state) = self.ll.read_state() {
588 if state != BufferState::TxNotActive {
589 return Err(InvalidTxStateError(state.into()));
590 }
591 }
592 self.ll.transmit_frame_unchecked(frame);
593 Ok(())
594 }
595
596 pub fn transfer_done(&mut self) -> nb::Result<(), InvalidTxStateError> {
600 if self.mode != TxState::TransmittingDataFrame {
601 return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
602 }
603 let status = self.ll.read_state();
604 if status.is_err() {
605 return Err(nb::Error::WouldBlock);
606 }
607 let status = status.unwrap();
608 if status == BufferState::TxNotActive {
609 self.mode = TxState::Idle;
610 return Ok(());
611 }
612 Err(nb::Error::WouldBlock)
613 }
614
615 pub fn remote_transfer_done(&mut self) -> nb::Result<CanRx, InvalidTxStateError> {
627 if self.mode != TxState::TransmittingRemoteFrame {
628 return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
629 }
630 let status = self.ll.read_state();
631 if status.is_err() {
632 return Err(nb::Error::WouldBlock);
633 }
634 let status = status.unwrap();
635 if status == BufferState::RxReady {
636 self.mode = TxState::AwaitingRemoteFrameReply;
637 return Ok(CanRx {
638 ll: unsafe { self.ll.clone() },
639 mode: RxState::Receiving,
640 });
641 }
642 Err(nb::Error::WouldBlock)
643 }
644
645 pub fn remote_transfer_done_with_tx_reconfig(&mut self) -> nb::Result<(), InvalidTxStateError> {
652 if self.mode != TxState::TransmittingRemoteFrame {
653 return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
654 }
655 let status = self.ll.read_state();
656 if status.is_err() {
657 return Err(nb::Error::WouldBlock);
658 }
659 let status = status.unwrap();
660 if status == BufferState::RxReady {
661 self.ll.write_state(BufferState::TxNotActive);
662 self.mode = TxState::Idle;
663 return Ok(());
664 }
665 Err(nb::Error::WouldBlock)
666 }
667
668 pub fn reset(&mut self) {
669 self.ll.reset();
670 self.mode = TxState::Idle;
671 }
672}
673
674pub struct CanRx {
676 ll: CanChannelLowLevel,
677 mode: RxState,
678}
679
680impl CanRx {
681 pub fn new(mut ll: CanChannelLowLevel) -> Self {
682 ll.reset();
683 Self {
684 ll,
685 mode: RxState::Idle,
686 }
687 }
688
689 #[inline]
690 pub fn into_tx_channel(self, tx_priority: Option<u4>) -> CanTx {
691 CanTx::new(self.ll, tx_priority)
692 }
693
694 #[inline]
695 pub fn enable_interrupt(&mut self, enable_translation: bool) {
696 self.ll.enable_interrupt(enable_translation);
697 }
698
699 pub fn configure_for_reception_with_standard_id(
700 &mut self,
701 standard_id: embedded_can::StandardId,
702 set_rtr: bool,
703 ) {
704 self.ll.set_standard_id(standard_id, set_rtr);
705 self.configure_for_reception();
706 }
707
708 pub fn configure_for_reception_with_extended_id(
709 &mut self,
710 extended_id: embedded_can::ExtendedId,
711 set_rtr: bool,
712 ) {
713 self.ll.set_extended_id(extended_id, set_rtr);
714 self.configure_for_reception();
715 }
716
717 pub fn configure_for_reception(&mut self) {
718 self.ll.configure_for_reception();
719 self.mode = RxState::Receiving;
720 }
721
722 #[inline]
723 pub fn frame_available(&self) -> bool {
724 self.ll
725 .read_state()
726 .is_ok_and(|state| state == BufferState::RxFull || state == BufferState::RxOverrun)
727 }
728
729 pub fn receive(
731 &mut self,
732 reconfigure_for_reception: bool,
733 ) -> nb::Result<CanFrame, InvalidRxStateError> {
734 if self.mode != RxState::Receiving {
735 return Err(nb::Error::Other(InvalidRxStateError(self.mode)));
736 }
737 let status = self.ll.read_state();
738 if status.is_err() {
739 return Err(nb::Error::WouldBlock);
740 }
741 let status = status.unwrap();
742 if status == BufferState::RxFull || status == BufferState::RxOverrun {
743 self.mode = RxState::Idle;
744 if reconfigure_for_reception {
745 self.ll.write_state(BufferState::RxReady);
746 }
747 return Ok(self.ll.read_frame_unchecked());
748 }
749 Err(nb::Error::WouldBlock)
750 }
751}
752
753pub struct CanChannels {
754 id: CanId,
755 channels: [Option<CanChannelLowLevel>; 15],
756}
757
758impl CanChannels {
759 const fn new(id: CanId) -> Self {
760 unsafe {
762 Self {
763 id,
764 channels: [
765 Some(CanChannelLowLevel::steal_unchecked(id, 0)),
766 Some(CanChannelLowLevel::steal_unchecked(id, 1)),
767 Some(CanChannelLowLevel::steal_unchecked(id, 2)),
768 Some(CanChannelLowLevel::steal_unchecked(id, 3)),
769 Some(CanChannelLowLevel::steal_unchecked(id, 4)),
770 Some(CanChannelLowLevel::steal_unchecked(id, 5)),
771 Some(CanChannelLowLevel::steal_unchecked(id, 6)),
772 Some(CanChannelLowLevel::steal_unchecked(id, 7)),
773 Some(CanChannelLowLevel::steal_unchecked(id, 8)),
774 Some(CanChannelLowLevel::steal_unchecked(id, 9)),
775 Some(CanChannelLowLevel::steal_unchecked(id, 10)),
776 Some(CanChannelLowLevel::steal_unchecked(id, 11)),
777 Some(CanChannelLowLevel::steal_unchecked(id, 12)),
778 Some(CanChannelLowLevel::steal_unchecked(id, 13)),
779 Some(CanChannelLowLevel::steal_unchecked(id, 14)),
780 ],
781 }
782 }
783 }
784
785 pub const fn can_id(&self) -> CanId {
786 self.id
787 }
788
789 pub fn take(&mut self, idx: usize) -> Option<CanChannelLowLevel> {
791 if idx > 14 {
792 return None;
793 }
794 self.channels[idx].take()
795 }
796
797 pub fn give(&mut self, idx: usize, channel: CanChannelLowLevel) {
798 if idx > 14 {
799 panic!("invalid buffer index for CAN channel");
800 }
801 self.channels[idx] = Some(channel);
802 }
803}
804
805#[cfg(test)]
806mod tests {
807 #[cfg(feature = "alloc")]
808 use std::println;
809
810 #[cfg(feature = "alloc")]
811 #[test]
812 pub fn test_clock_calculator_example_1() {
813 let configs = super::calculate_all_viable_clock_configs(
814 crate::time::Hertz::from_raw(50_000_000),
815 crate::time::Hertz::from_raw(25_000),
816 0.75,
817 )
818 .expect("clock calculation failed");
819 assert_eq!(configs[0].prescaler, 84);
821 assert_eq!(configs[0].tseg1, 16);
822 assert_eq!(configs[0].tseg2, 6);
823 assert_eq!(configs[0].sjw, 4);
824 let sample_cfg = configs
826 .iter()
827 .find(|c| c.prescaler == 100)
828 .expect("clock config not found");
829 assert_eq!(sample_cfg.tseg1, 14);
832 assert_eq!(sample_cfg.tseg2, 5);
833 }
834}