1 | //! Configuration for FDCAN Module |
2 | // Note: This file is copied and modified from fdcan crate by Richard Meadows |
3 | |
4 | use core::num::{NonZeroU16, NonZeroU8}; |
5 | |
6 | /// Configures the bit timings. |
7 | /// |
8 | /// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter |
9 | /// parameters as follows: |
10 | /// |
11 | /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). |
12 | /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). |
13 | /// - *Sample Point*: Should normally be left at the default value of 87.5%. |
14 | /// - *SJW*: Should normally be left at the default value of 1. |
15 | /// |
16 | /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` |
17 | /// parameter to this method. |
18 | #[derive (Clone, Copy, Debug)] |
19 | pub struct NominalBitTiming { |
20 | /// Value by which the oscillator frequency is divided for generating the bit time quanta. The bit |
21 | /// time is built up from a multiple of this quanta. Valid values are 1 to 512. |
22 | pub prescaler: NonZeroU16, |
23 | /// Valid values are 1 to 128. |
24 | pub seg1: NonZeroU8, |
25 | /// Valid values are 1 to 255. |
26 | pub seg2: NonZeroU8, |
27 | /// Valid values are 1 to 128. |
28 | pub sync_jump_width: NonZeroU8, |
29 | } |
30 | impl NominalBitTiming { |
31 | #[inline ] |
32 | pub(crate) fn nbrp(&self) -> u16 { |
33 | u16::from(self.prescaler) & 0x1FF |
34 | } |
35 | #[inline ] |
36 | pub(crate) fn ntseg1(&self) -> u8 { |
37 | u8::from(self.seg1) |
38 | } |
39 | #[inline ] |
40 | pub(crate) fn ntseg2(&self) -> u8 { |
41 | u8::from(self.seg2) & 0x7F |
42 | } |
43 | #[inline ] |
44 | pub(crate) fn nsjw(&self) -> u8 { |
45 | u8::from(self.sync_jump_width) & 0x7F |
46 | } |
47 | } |
48 | |
49 | impl Default for NominalBitTiming { |
50 | #[inline ] |
51 | fn default() -> Self { |
52 | // Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a NBTP |
53 | // register value of 0x0600_0A03 |
54 | Self { |
55 | prescaler: NonZeroU16::new(1).unwrap(), |
56 | seg1: NonZeroU8::new(11).unwrap(), |
57 | seg2: NonZeroU8::new(4).unwrap(), |
58 | sync_jump_width: NonZeroU8::new(4).unwrap(), |
59 | } |
60 | } |
61 | } |
62 | |
63 | /// Configures the data bit timings for the FdCan Variable Bitrates. |
64 | /// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS. |
65 | #[derive (Clone, Copy, Debug)] |
66 | pub struct DataBitTiming { |
67 | /// Tranceiver Delay Compensation |
68 | pub transceiver_delay_compensation: bool, |
69 | /// The value by which the oscillator frequency is divided to generate the bit time quanta. The bit |
70 | /// time is built up from a multiple of this quanta. Valid values for the Baud Rate Prescaler are 1 |
71 | /// to 31. |
72 | pub prescaler: NonZeroU16, |
73 | /// Valid values are 1 to 31. |
74 | pub seg1: NonZeroU8, |
75 | /// Valid values are 1 to 15. |
76 | pub seg2: NonZeroU8, |
77 | /// Must always be smaller than DTSEG2, valid values are 1 to 15. |
78 | pub sync_jump_width: NonZeroU8, |
79 | } |
80 | impl DataBitTiming { |
81 | // #[inline] |
82 | // fn tdc(&self) -> u8 { |
83 | // let tsd = self.transceiver_delay_compensation as u8; |
84 | // //TODO: stm32g4 does not export the TDC field |
85 | // todo!() |
86 | // } |
87 | #[inline ] |
88 | pub(crate) fn dbrp(&self) -> u8 { |
89 | (u16::from(self.prescaler) & 0x001F) as u8 |
90 | } |
91 | #[inline ] |
92 | pub(crate) fn dtseg1(&self) -> u8 { |
93 | u8::from(self.seg1) & 0x1F |
94 | } |
95 | #[inline ] |
96 | pub(crate) fn dtseg2(&self) -> u8 { |
97 | u8::from(self.seg2) & 0x0F |
98 | } |
99 | #[inline ] |
100 | pub(crate) fn dsjw(&self) -> u8 { |
101 | u8::from(self.sync_jump_width) & 0x0F |
102 | } |
103 | } |
104 | |
105 | impl Default for DataBitTiming { |
106 | #[inline ] |
107 | fn default() -> Self { |
108 | // Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a DBTP |
109 | // register value of 0x0000_0A33 |
110 | Self { |
111 | transceiver_delay_compensation: false, |
112 | prescaler: NonZeroU16::new(1).unwrap(), |
113 | seg1: NonZeroU8::new(11).unwrap(), |
114 | seg2: NonZeroU8::new(4).unwrap(), |
115 | sync_jump_width: NonZeroU8::new(4).unwrap(), |
116 | } |
117 | } |
118 | } |
119 | |
120 | /// Configures which modes to use |
121 | /// Individual headers can contain a desire to be send via FdCan |
122 | /// or use Bit rate switching. But if this general setting does not allow |
123 | /// that, only classic CAN is used instead. |
124 | #[derive (Clone, Copy, Debug)] |
125 | pub enum FrameTransmissionConfig { |
126 | /// Only allow Classic CAN message Frames |
127 | ClassicCanOnly, |
128 | /// Allow (non-brs) FdCAN Message Frames |
129 | AllowFdCan, |
130 | /// Allow FdCAN Message Frames and allow Bit Rate Switching |
131 | AllowFdCanAndBRS, |
132 | } |
133 | |
134 | /// |
135 | #[derive (Clone, Copy, Debug)] |
136 | pub enum ClockDivider { |
137 | /// Divide by 1 |
138 | _1 = 0b0000, |
139 | /// Divide by 2 |
140 | _2 = 0b0001, |
141 | /// Divide by 4 |
142 | _4 = 0b0010, |
143 | /// Divide by 6 |
144 | _6 = 0b0011, |
145 | /// Divide by 8 |
146 | _8 = 0b0100, |
147 | /// Divide by 10 |
148 | _10 = 0b0101, |
149 | /// Divide by 12 |
150 | _12 = 0b0110, |
151 | /// Divide by 14 |
152 | _14 = 0b0111, |
153 | /// Divide by 16 |
154 | _16 = 0b1000, |
155 | /// Divide by 18 |
156 | _18 = 0b1001, |
157 | /// Divide by 20 |
158 | _20 = 0b1010, |
159 | /// Divide by 22 |
160 | _22 = 0b1011, |
161 | /// Divide by 24 |
162 | _24 = 0b1100, |
163 | /// Divide by 26 |
164 | _26 = 0b1101, |
165 | /// Divide by 28 |
166 | _28 = 0b1110, |
167 | /// Divide by 30 |
168 | _30 = 0b1111, |
169 | } |
170 | |
171 | /// Prescaler of the Timestamp counter |
172 | #[derive (Clone, Copy, Debug)] |
173 | pub enum TimestampPrescaler { |
174 | /// 1 |
175 | _1 = 1, |
176 | /// 2 |
177 | _2 = 2, |
178 | /// 3 |
179 | _3 = 3, |
180 | /// 4 |
181 | _4 = 4, |
182 | /// 5 |
183 | _5 = 5, |
184 | /// 6 |
185 | _6 = 6, |
186 | /// 7 |
187 | _7 = 7, |
188 | /// 8 |
189 | _8 = 8, |
190 | /// 9 |
191 | _9 = 9, |
192 | /// 10 |
193 | _10 = 10, |
194 | /// 11 |
195 | _11 = 11, |
196 | /// 12 |
197 | _12 = 12, |
198 | /// 13 |
199 | _13 = 13, |
200 | /// 14 |
201 | _14 = 14, |
202 | /// 15 |
203 | _15 = 15, |
204 | /// 16 |
205 | _16 = 16, |
206 | } |
207 | |
208 | /// Selects the source of the Timestamp counter |
209 | #[derive (Clone, Copy, Debug)] |
210 | pub enum TimestampSource { |
211 | /// The Timestamp counter is disabled |
212 | None, |
213 | /// Using the FdCan input clock as the Timstamp counter's source, |
214 | /// and using a specific prescaler |
215 | Prescaler(TimestampPrescaler), |
216 | /// Using TIM3 as a source |
217 | FromTIM3, |
218 | } |
219 | |
220 | /// How to handle frames in the global filter |
221 | #[derive (Clone, Copy, Debug)] |
222 | pub enum NonMatchingFilter { |
223 | /// Frames will go to Fifo0 when they do no match any specific filter |
224 | IntoRxFifo0 = 0b00, |
225 | /// Frames will go to Fifo1 when they do no match any specific filter |
226 | IntoRxFifo1 = 0b01, |
227 | /// Frames will be rejected when they do not match any specific filter |
228 | Reject = 0b11, |
229 | } |
230 | |
231 | /// How to handle frames which do not match a specific filter |
232 | #[derive (Clone, Copy, Debug)] |
233 | pub struct GlobalFilter { |
234 | /// How to handle non-matching standard frames |
235 | pub handle_standard_frames: NonMatchingFilter, |
236 | |
237 | /// How to handle non-matching extended frames |
238 | pub handle_extended_frames: NonMatchingFilter, |
239 | |
240 | /// How to handle remote standard frames |
241 | pub reject_remote_standard_frames: bool, |
242 | |
243 | /// How to handle remote extended frames |
244 | pub reject_remote_extended_frames: bool, |
245 | } |
246 | impl GlobalFilter { |
247 | /// Reject all non-matching and remote frames |
248 | pub const fn reject_all() -> Self { |
249 | Self { |
250 | handle_standard_frames: NonMatchingFilter::Reject, |
251 | handle_extended_frames: NonMatchingFilter::Reject, |
252 | reject_remote_standard_frames: true, |
253 | reject_remote_extended_frames: true, |
254 | } |
255 | } |
256 | |
257 | /// How to handle non-matching standard frames |
258 | pub const fn set_handle_standard_frames(mut self, filter: NonMatchingFilter) -> Self { |
259 | self.handle_standard_frames = filter; |
260 | self |
261 | } |
262 | /// How to handle non-matching exteded frames |
263 | pub const fn set_handle_extended_frames(mut self, filter: NonMatchingFilter) -> Self { |
264 | self.handle_extended_frames = filter; |
265 | self |
266 | } |
267 | /// How to handle remote standard frames |
268 | pub const fn set_reject_remote_standard_frames(mut self, filter: bool) -> Self { |
269 | self.reject_remote_standard_frames = filter; |
270 | self |
271 | } |
272 | /// How to handle remote extended frames |
273 | pub const fn set_reject_remote_extended_frames(mut self, filter: bool) -> Self { |
274 | self.reject_remote_extended_frames = filter; |
275 | self |
276 | } |
277 | } |
278 | impl Default for GlobalFilter { |
279 | #[inline ] |
280 | fn default() -> Self { |
281 | Self { |
282 | handle_standard_frames: NonMatchingFilter::IntoRxFifo0, |
283 | handle_extended_frames: NonMatchingFilter::IntoRxFifo0, |
284 | reject_remote_standard_frames: false, |
285 | reject_remote_extended_frames: false, |
286 | } |
287 | } |
288 | } |
289 | |
290 | /// TX buffer operation mode |
291 | #[derive (Clone, Copy, PartialEq, Eq, Debug)] |
292 | pub enum TxBufferMode { |
293 | /// TX FIFO operation - In this mode CAN frames are trasmitted strictly in write order. |
294 | Fifo, |
295 | /// TX priority queue operation - In this mode CAN frames are transmitted according to CAN priority. |
296 | Priority, |
297 | } |
298 | |
299 | impl From<TxBufferMode> for crate::pac::can::vals::Tfqm { |
300 | fn from(value: TxBufferMode) -> Self { |
301 | match value { |
302 | TxBufferMode::Priority => Self::QUEUE, |
303 | TxBufferMode::Fifo => Self::FIFO, |
304 | } |
305 | } |
306 | } |
307 | |
308 | impl From<crate::pac::can::vals::Tfqm> for TxBufferMode { |
309 | fn from(value: crate::pac::can::vals::Tfqm) -> Self { |
310 | match value { |
311 | crate::pac::can::vals::Tfqm::QUEUE => Self::Priority, |
312 | crate::pac::can::vals::Tfqm::FIFO => Self::Fifo, |
313 | } |
314 | } |
315 | } |
316 | |
317 | /// FdCan Config Struct |
318 | #[derive (Clone, Copy, Debug)] |
319 | pub struct FdCanConfig { |
320 | /// Nominal Bit Timings |
321 | pub nbtr: NominalBitTiming, |
322 | /// (Variable) Data Bit Timings |
323 | pub dbtr: DataBitTiming, |
324 | /// Enables or disables automatic retransmission of messages |
325 | /// |
326 | /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame |
327 | /// util it can be sent. Otherwise, it will try only once to send each frame. |
328 | /// |
329 | /// Automatic retransmission is enabled by default. |
330 | pub automatic_retransmit: bool, |
331 | /// Enabled or disables the pausing between transmissions |
332 | /// |
333 | /// This feature looses up burst transmissions coming from a single node and it protects against |
334 | /// "babbling idiot" scenarios where the application program erroneously requests too many |
335 | /// transmissions. |
336 | pub transmit_pause: bool, |
337 | /// Enabled or disables the pausing between transmissions |
338 | /// |
339 | /// This feature looses up burst transmissions coming from a single node and it protects against |
340 | /// "babbling idiot" scenarios where the application program erroneously requests too many |
341 | /// transmissions. |
342 | pub frame_transmit: FrameTransmissionConfig, |
343 | /// Non Isoe Mode |
344 | /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN |
345 | /// FD Specification V1.0. |
346 | pub non_iso_mode: bool, |
347 | /// Edge Filtering: Two consecutive dominant tq required to detect an edge for hard synchronization |
348 | pub edge_filtering: bool, |
349 | /// Enables protocol exception handling |
350 | pub protocol_exception_handling: bool, |
351 | /// Sets the general clock divider for this FdCAN instance |
352 | pub clock_divider: ClockDivider, |
353 | /// Sets the timestamp source |
354 | pub timestamp_source: TimestampSource, |
355 | /// Configures the Global Filter |
356 | pub global_filter: GlobalFilter, |
357 | /// TX buffer mode (FIFO or priority queue) |
358 | pub tx_buffer_mode: TxBufferMode, |
359 | } |
360 | |
361 | impl FdCanConfig { |
362 | /// Configures the bit timings. |
363 | #[inline ] |
364 | pub const fn set_nominal_bit_timing(mut self, btr: NominalBitTiming) -> Self { |
365 | self.nbtr = btr; |
366 | self |
367 | } |
368 | |
369 | /// Configures the bit timings. |
370 | #[inline ] |
371 | pub const fn set_data_bit_timing(mut self, btr: DataBitTiming) -> Self { |
372 | self.dbtr = btr; |
373 | self |
374 | } |
375 | |
376 | /// Enables or disables automatic retransmission of messages |
377 | /// |
378 | /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame |
379 | /// util it can be sent. Otherwise, it will try only once to send each frame. |
380 | /// |
381 | /// Automatic retransmission is enabled by default. |
382 | #[inline ] |
383 | pub const fn set_automatic_retransmit(mut self, enabled: bool) -> Self { |
384 | self.automatic_retransmit = enabled; |
385 | self |
386 | } |
387 | |
388 | /// Enabled or disables the pausing between transmissions |
389 | /// |
390 | /// This feature looses up burst transmissions coming from a single node and it protects against |
391 | /// "babbling idiot" scenarios where the application program erroneously requests too many |
392 | /// transmissions. |
393 | #[inline ] |
394 | pub const fn set_transmit_pause(mut self, enabled: bool) -> Self { |
395 | self.transmit_pause = enabled; |
396 | self |
397 | } |
398 | |
399 | /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN |
400 | /// FD Specification V1.0. |
401 | #[inline ] |
402 | pub const fn set_non_iso_mode(mut self, enabled: bool) -> Self { |
403 | self.non_iso_mode = enabled; |
404 | self |
405 | } |
406 | |
407 | /// Two consecutive dominant tq required to detect an edge for hard synchronization |
408 | #[inline ] |
409 | pub const fn set_edge_filtering(mut self, enabled: bool) -> Self { |
410 | self.edge_filtering = enabled; |
411 | self |
412 | } |
413 | |
414 | /// Sets the allowed transmission types for messages. |
415 | #[inline ] |
416 | pub const fn set_frame_transmit(mut self, fts: FrameTransmissionConfig) -> Self { |
417 | self.frame_transmit = fts; |
418 | self |
419 | } |
420 | |
421 | /// Enables protocol exception handling |
422 | #[inline ] |
423 | pub const fn set_protocol_exception_handling(mut self, peh: bool) -> Self { |
424 | self.protocol_exception_handling = peh; |
425 | self |
426 | } |
427 | |
428 | /// Sets the general clock divider for this FdCAN instance |
429 | #[inline ] |
430 | pub const fn set_clock_divider(mut self, div: ClockDivider) -> Self { |
431 | self.clock_divider = div; |
432 | self |
433 | } |
434 | |
435 | /// Sets the timestamp source |
436 | #[inline ] |
437 | pub const fn set_timestamp_source(mut self, tss: TimestampSource) -> Self { |
438 | self.timestamp_source = tss; |
439 | self |
440 | } |
441 | |
442 | /// Sets the global filter settings |
443 | #[inline ] |
444 | pub const fn set_global_filter(mut self, filter: GlobalFilter) -> Self { |
445 | self.global_filter = filter; |
446 | self |
447 | } |
448 | |
449 | /// Sets the TX buffer mode (FIFO or priority queue) |
450 | #[inline ] |
451 | pub const fn set_tx_buffer_mode(mut self, txbm: TxBufferMode) -> Self { |
452 | self.tx_buffer_mode = txbm; |
453 | self |
454 | } |
455 | } |
456 | |
457 | impl Default for FdCanConfig { |
458 | #[inline ] |
459 | fn default() -> Self { |
460 | Self { |
461 | nbtr: NominalBitTiming::default(), |
462 | dbtr: DataBitTiming::default(), |
463 | automatic_retransmit: true, |
464 | transmit_pause: false, |
465 | frame_transmit: FrameTransmissionConfig::ClassicCanOnly, |
466 | non_iso_mode: false, |
467 | edge_filtering: false, |
468 | protocol_exception_handling: true, |
469 | clock_divider: ClockDivider::_1, |
470 | timestamp_source: TimestampSource::None, |
471 | global_filter: GlobalFilter::default(), |
472 | tx_buffer_mode: TxBufferMode::Priority, |
473 | } |
474 | } |
475 | } |
476 | |