embassy_stm32/can/fd/
config.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
//! Configuration for FDCAN Module
// Note: This file is copied and modified from fdcan crate by Richard Meadows

use core::num::{NonZeroU16, NonZeroU8};

/// Configures the bit timings.
///
/// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter
/// parameters as follows:
///
/// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed).
///   This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1).
/// - *Sample Point*: Should normally be left at the default value of 87.5%.
/// - *SJW*: Should normally be left at the default value of 1.
///
/// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr`
/// parameter to this method.
#[derive(Clone, Copy, Debug)]
pub struct NominalBitTiming {
    /// Value by which the oscillator frequency is divided for generating the bit time quanta. The bit
    /// time is built up from a multiple of this quanta. Valid values are 1 to 512.
    pub prescaler: NonZeroU16,
    /// Valid values are 1 to 128.
    pub seg1: NonZeroU8,
    /// Valid values are 1 to 255.
    pub seg2: NonZeroU8,
    /// Valid values are 1 to 128.
    pub sync_jump_width: NonZeroU8,
}
impl NominalBitTiming {
    #[inline]
    pub(crate) fn nbrp(&self) -> u16 {
        u16::from(self.prescaler) & 0x1FF
    }
    #[inline]
    pub(crate) fn ntseg1(&self) -> u8 {
        u8::from(self.seg1)
    }
    #[inline]
    pub(crate) fn ntseg2(&self) -> u8 {
        u8::from(self.seg2) & 0x7F
    }
    #[inline]
    pub(crate) fn nsjw(&self) -> u8 {
        u8::from(self.sync_jump_width) & 0x7F
    }
}

impl Default for NominalBitTiming {
    #[inline]
    fn default() -> Self {
        // Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a NBTP
        // register value of 0x0600_0A03
        Self {
            prescaler: NonZeroU16::new(1).unwrap(),
            seg1: NonZeroU8::new(11).unwrap(),
            seg2: NonZeroU8::new(4).unwrap(),
            sync_jump_width: NonZeroU8::new(4).unwrap(),
        }
    }
}

/// Configures the data bit timings for the FdCan Variable Bitrates.
/// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS.
#[derive(Clone, Copy, Debug)]
pub struct DataBitTiming {
    /// Tranceiver Delay Compensation
    pub transceiver_delay_compensation: bool,
    ///  The value by which the oscillator frequency is divided to generate the bit time quanta. The bit
    ///  time is built up from a multiple of this quanta. Valid values for the Baud Rate Prescaler are 1
    ///  to 31.
    pub prescaler: NonZeroU16,
    /// Valid values are 1 to 31.
    pub seg1: NonZeroU8,
    /// Valid values are 1 to 15.
    pub seg2: NonZeroU8,
    /// Must always be smaller than DTSEG2, valid values are 1 to 15.
    pub sync_jump_width: NonZeroU8,
}
impl DataBitTiming {
    // #[inline]
    // fn tdc(&self) -> u8 {
    //     let tsd = self.transceiver_delay_compensation as u8;
    //     //TODO: stm32g4 does not export the TDC field
    //     todo!()
    // }
    #[inline]
    pub(crate) fn dbrp(&self) -> u8 {
        (u16::from(self.prescaler) & 0x001F) as u8
    }
    #[inline]
    pub(crate) fn dtseg1(&self) -> u8 {
        u8::from(self.seg1) & 0x1F
    }
    #[inline]
    pub(crate) fn dtseg2(&self) -> u8 {
        u8::from(self.seg2) & 0x0F
    }
    #[inline]
    pub(crate) fn dsjw(&self) -> u8 {
        u8::from(self.sync_jump_width) & 0x0F
    }
}

impl Default for DataBitTiming {
    #[inline]
    fn default() -> Self {
        // Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a DBTP
        // register value of 0x0000_0A33
        Self {
            transceiver_delay_compensation: false,
            prescaler: NonZeroU16::new(1).unwrap(),
            seg1: NonZeroU8::new(11).unwrap(),
            seg2: NonZeroU8::new(4).unwrap(),
            sync_jump_width: NonZeroU8::new(4).unwrap(),
        }
    }
}

/// Configures which modes to use
/// Individual headers can contain a desire to be send via FdCan
/// or use Bit rate switching. But if this general setting does not allow
/// that, only classic CAN is used instead.
#[derive(Clone, Copy, Debug)]
pub enum FrameTransmissionConfig {
    /// Only allow Classic CAN message Frames
    ClassicCanOnly,
    /// Allow (non-brs) FdCAN Message Frames
    AllowFdCan,
    /// Allow FdCAN Message Frames and allow Bit Rate Switching
    AllowFdCanAndBRS,
}

///
#[derive(Clone, Copy, Debug)]
pub enum ClockDivider {
    /// Divide by 1
    _1 = 0b0000,
    /// Divide by 2
    _2 = 0b0001,
    /// Divide by 4
    _4 = 0b0010,
    /// Divide by 6
    _6 = 0b0011,
    /// Divide by 8
    _8 = 0b0100,
    /// Divide by 10
    _10 = 0b0101,
    /// Divide by 12
    _12 = 0b0110,
    /// Divide by 14
    _14 = 0b0111,
    /// Divide by 16
    _16 = 0b1000,
    /// Divide by 18
    _18 = 0b1001,
    /// Divide by 20
    _20 = 0b1010,
    /// Divide by 22
    _22 = 0b1011,
    /// Divide by 24
    _24 = 0b1100,
    /// Divide by 26
    _26 = 0b1101,
    /// Divide by 28
    _28 = 0b1110,
    /// Divide by 30
    _30 = 0b1111,
}

/// Prescaler of the Timestamp counter
#[derive(Clone, Copy, Debug)]
pub enum TimestampPrescaler {
    /// 1
    _1 = 1,
    /// 2
    _2 = 2,
    /// 3
    _3 = 3,
    /// 4
    _4 = 4,
    /// 5
    _5 = 5,
    /// 6
    _6 = 6,
    /// 7
    _7 = 7,
    /// 8
    _8 = 8,
    /// 9
    _9 = 9,
    /// 10
    _10 = 10,
    /// 11
    _11 = 11,
    /// 12
    _12 = 12,
    /// 13
    _13 = 13,
    /// 14
    _14 = 14,
    /// 15
    _15 = 15,
    /// 16
    _16 = 16,
}

/// Selects the source of the Timestamp counter
#[derive(Clone, Copy, Debug)]
pub enum TimestampSource {
    /// The Timestamp counter is disabled
    None,
    /// Using the FdCan input clock as the Timstamp counter's source,
    /// and using a specific prescaler
    Prescaler(TimestampPrescaler),
    /// Using TIM3 as a source
    FromTIM3,
}

/// How to handle frames in the global filter
#[derive(Clone, Copy, Debug)]
pub enum NonMatchingFilter {
    /// Frames will go to Fifo0 when they do no match any specific filter
    IntoRxFifo0 = 0b00,
    /// Frames will go to Fifo1 when they do no match any specific filter
    IntoRxFifo1 = 0b01,
    /// Frames will be rejected when they do not match any specific filter
    Reject = 0b11,
}

/// How to handle frames which do not match a specific filter
#[derive(Clone, Copy, Debug)]
pub struct GlobalFilter {
    /// How to handle non-matching standard frames
    pub handle_standard_frames: NonMatchingFilter,

    /// How to handle non-matching extended frames
    pub handle_extended_frames: NonMatchingFilter,

    /// How to handle remote standard frames
    pub reject_remote_standard_frames: bool,

    /// How to handle remote extended frames
    pub reject_remote_extended_frames: bool,
}
impl GlobalFilter {
    /// Reject all non-matching and remote frames
    pub const fn reject_all() -> Self {
        Self {
            handle_standard_frames: NonMatchingFilter::Reject,
            handle_extended_frames: NonMatchingFilter::Reject,
            reject_remote_standard_frames: true,
            reject_remote_extended_frames: true,
        }
    }

    /// How to handle non-matching standard frames
    pub const fn set_handle_standard_frames(mut self, filter: NonMatchingFilter) -> Self {
        self.handle_standard_frames = filter;
        self
    }
    /// How to handle non-matching exteded frames
    pub const fn set_handle_extended_frames(mut self, filter: NonMatchingFilter) -> Self {
        self.handle_extended_frames = filter;
        self
    }
    /// How to handle remote standard frames
    pub const fn set_reject_remote_standard_frames(mut self, filter: bool) -> Self {
        self.reject_remote_standard_frames = filter;
        self
    }
    /// How to handle remote extended frames
    pub const fn set_reject_remote_extended_frames(mut self, filter: bool) -> Self {
        self.reject_remote_extended_frames = filter;
        self
    }
}
impl Default for GlobalFilter {
    #[inline]
    fn default() -> Self {
        Self {
            handle_standard_frames: NonMatchingFilter::IntoRxFifo0,
            handle_extended_frames: NonMatchingFilter::IntoRxFifo0,
            reject_remote_standard_frames: false,
            reject_remote_extended_frames: false,
        }
    }
}

/// TX buffer operation mode
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum TxBufferMode {
    /// TX FIFO operation - In this mode CAN frames are trasmitted strictly in write order.
    Fifo,
    /// TX priority queue operation - In this mode CAN frames are transmitted according to CAN priority.
    Priority,
}

impl From<TxBufferMode> for crate::pac::can::vals::Tfqm {
    fn from(value: TxBufferMode) -> Self {
        match value {
            TxBufferMode::Priority => Self::QUEUE,
            TxBufferMode::Fifo => Self::FIFO,
        }
    }
}

impl From<crate::pac::can::vals::Tfqm> for TxBufferMode {
    fn from(value: crate::pac::can::vals::Tfqm) -> Self {
        match value {
            crate::pac::can::vals::Tfqm::QUEUE => Self::Priority,
            crate::pac::can::vals::Tfqm::FIFO => Self::Fifo,
        }
    }
}

/// FdCan Config Struct
#[derive(Clone, Copy, Debug)]
pub struct FdCanConfig {
    /// Nominal Bit Timings
    pub nbtr: NominalBitTiming,
    /// (Variable) Data Bit Timings
    pub dbtr: DataBitTiming,
    /// Enables or disables automatic retransmission of messages
    ///
    /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
    /// util it can be sent. Otherwise, it will try only once to send each frame.
    ///
    /// Automatic retransmission is enabled by default.
    pub automatic_retransmit: bool,
    /// Enabled or disables the pausing between transmissions
    ///
    /// This feature looses up burst transmissions coming from a single node and it protects against
    /// "babbling idiot" scenarios where the application program erroneously requests too many
    /// transmissions.
    pub transmit_pause: bool,
    /// Enabled or disables the pausing between transmissions
    ///
    /// This feature looses up burst transmissions coming from a single node and it protects against
    /// "babbling idiot" scenarios where the application program erroneously requests too many
    /// transmissions.
    pub frame_transmit: FrameTransmissionConfig,
    /// Non Isoe Mode
    /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
    /// FD Specification V1.0.
    pub non_iso_mode: bool,
    /// Edge Filtering: Two consecutive dominant tq required to detect an edge for hard synchronization
    pub edge_filtering: bool,
    /// Enables protocol exception handling
    pub protocol_exception_handling: bool,
    /// Sets the general clock divider for this FdCAN instance
    pub clock_divider: ClockDivider,
    /// Sets the timestamp source
    pub timestamp_source: TimestampSource,
    /// Configures the Global Filter
    pub global_filter: GlobalFilter,
    /// TX buffer mode (FIFO or priority queue)
    pub tx_buffer_mode: TxBufferMode,
}

impl FdCanConfig {
    /// Configures the bit timings.
    #[inline]
    pub const fn set_nominal_bit_timing(mut self, btr: NominalBitTiming) -> Self {
        self.nbtr = btr;
        self
    }

    /// Configures the bit timings.
    #[inline]
    pub const fn set_data_bit_timing(mut self, btr: DataBitTiming) -> Self {
        self.dbtr = btr;
        self
    }

    /// Enables or disables automatic retransmission of messages
    ///
    /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
    /// util it can be sent. Otherwise, it will try only once to send each frame.
    ///
    /// Automatic retransmission is enabled by default.
    #[inline]
    pub const fn set_automatic_retransmit(mut self, enabled: bool) -> Self {
        self.automatic_retransmit = enabled;
        self
    }

    /// Enabled or disables the pausing between transmissions
    ///
    /// This feature looses up burst transmissions coming from a single node and it protects against
    /// "babbling idiot" scenarios where the application program erroneously requests too many
    /// transmissions.
    #[inline]
    pub const fn set_transmit_pause(mut self, enabled: bool) -> Self {
        self.transmit_pause = enabled;
        self
    }

    /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
    /// FD Specification V1.0.
    #[inline]
    pub const fn set_non_iso_mode(mut self, enabled: bool) -> Self {
        self.non_iso_mode = enabled;
        self
    }

    /// Two consecutive dominant tq required to detect an edge for hard synchronization
    #[inline]
    pub const fn set_edge_filtering(mut self, enabled: bool) -> Self {
        self.edge_filtering = enabled;
        self
    }

    /// Sets the allowed transmission types for messages.
    #[inline]
    pub const fn set_frame_transmit(mut self, fts: FrameTransmissionConfig) -> Self {
        self.frame_transmit = fts;
        self
    }

    /// Enables protocol exception handling
    #[inline]
    pub const fn set_protocol_exception_handling(mut self, peh: bool) -> Self {
        self.protocol_exception_handling = peh;
        self
    }

    /// Sets the general clock divider for this FdCAN instance
    #[inline]
    pub const fn set_clock_divider(mut self, div: ClockDivider) -> Self {
        self.clock_divider = div;
        self
    }

    /// Sets the timestamp source
    #[inline]
    pub const fn set_timestamp_source(mut self, tss: TimestampSource) -> Self {
        self.timestamp_source = tss;
        self
    }

    /// Sets the global filter settings
    #[inline]
    pub const fn set_global_filter(mut self, filter: GlobalFilter) -> Self {
        self.global_filter = filter;
        self
    }

    /// Sets the TX buffer mode (FIFO or priority queue)
    #[inline]
    pub const fn set_tx_buffer_mode(mut self, txbm: TxBufferMode) -> Self {
        self.tx_buffer_mode = txbm;
        self
    }
}

impl Default for FdCanConfig {
    #[inline]
    fn default() -> Self {
        Self {
            nbtr: NominalBitTiming::default(),
            dbtr: DataBitTiming::default(),
            automatic_retransmit: true,
            transmit_pause: false,
            frame_transmit: FrameTransmissionConfig::ClassicCanOnly,
            non_iso_mode: false,
            edge_filtering: false,
            protocol_exception_handling: true,
            clock_divider: ClockDivider::_1,
            timestamp_source: TimestampSource::None,
            global_filter: GlobalFilter::default(),
            tx_buffer_mode: TxBufferMode::Priority,
        }
    }
}