Skip to content

Commit ff287d0

Browse files
iabdalkaderdpgeorge
authored andcommitted
stm32/pyb_can: Enable CAN FD frame support and BRS.
- Enable CAN FD frame support and BRS. - Optimize the message RAM usage per FDCAN instance. - Document the usage and different sections of the Message RAM.
1 parent 8baf05a commit ff287d0

File tree

2 files changed

+151
-80
lines changed

2 files changed

+151
-80
lines changed

ports/stm32/fdcan.c

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,19 @@
5656
#define FDCAN_IT_GROUP_RX_FIFO1 (FDCAN_ILS_RF1NL | FDCAN_ILS_RF1FL | FDCAN_ILS_RF1LL)
5757
#endif
5858

59+
// The dedicated Message RAM should be 2560 words, but the way it's defined in stm32h7xx_hal_fdcan.c
60+
// as (SRAMCAN_BASE + FDCAN_MESSAGE_RAM_SIZE - 0x4U) limits the usable number of words to 2559 words.
61+
#define FDCAN_MESSAGE_RAM_SIZE (2560 - 1)
62+
5963
// also defined in <PROC>_hal_fdcan.c, but not able to declare extern and reach the variable
60-
static const uint8_t DLCtoBytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64};
64+
const uint8_t DLCtoBytes[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64};
6165

6266
bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) {
6367
(void)auto_restart;
6468

6569
FDCAN_InitTypeDef *init = &can_obj->can.Init;
66-
init->FrameFormat = FDCAN_FRAME_CLASSIC;
70+
// Configure FDCAN with FD frame and BRS support.
71+
init->FrameFormat = FDCAN_FRAME_FD_BRS;
6772
init->Mode = mode;
6873

6974
init->NominalPrescaler = prescaler; // tq = NominalPrescaler x (1/fdcan_ker_ck)
@@ -81,46 +86,58 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
8186
init->DataSyncJumpWidth = 1;
8287
init->DataTimeSeg1 = 1;
8388
init->DataTimeSeg2 = 1;
84-
#endif
85-
86-
#if defined(STM32H7)
87-
// variable used to specify RAM address in HAL, only for H7, G4 uses defined offset address in HAL
88-
// The Message RAM is shared between CAN1 and CAN2. Setting the offset to half
89-
// the Message RAM for the second CAN and using half the resources for each CAN.
89+
init->StdFiltersNbr = 28; // /2 ? if FDCAN2 is used !!?
90+
init->ExtFiltersNbr = 0; // Not used
91+
init->TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
92+
#elif defined(STM32H7)
93+
// The dedicated FDCAN RAM is 2560 32-bit words and shared between the FDCAN instances.
94+
// To support 2 FDCAN instances simultaneously, the Message RAM is divided in half by
95+
// setting the second FDCAN memory offset to half the RAM size. With this configuration,
96+
// the maximum words per FDCAN instance is 1280 32-bit words.
9097
if (can_obj->can_id == PYB_CAN_1) {
9198
init->MessageRAMOffset = 0;
9299
} else {
93-
init->MessageRAMOffset = 2560 / 2;
100+
init->MessageRAMOffset = FDCAN_MESSAGE_RAM_SIZE / 2;
94101
}
95-
#endif
96-
97-
#if defined(STM32G4)
98-
99-
init->StdFiltersNbr = 28; // /2 ? if FDCAN2 is used !!?
100-
init->ExtFiltersNbr = 0; // Not used
101-
102-
#elif defined(STM32H7)
103-
104-
init->StdFiltersNbr = 64; // 128 / 2
105-
init->ExtFiltersNbr = 0; // Not used
106-
107-
init->TxEventsNbr = 16; // 32 / 2
108-
init->RxBuffersNbr = 32; // 64 / 2
109-
init->TxBuffersNbr = 16; // 32 / 2
110-
111-
init->RxFifo0ElmtsNbr = 64; // 128 / 2
112-
init->RxFifo0ElmtSize = FDCAN_DATA_BYTES_8;
113-
114-
init->RxFifo1ElmtsNbr = 64; // 128 / 2
115-
init->RxFifo1ElmtSize = FDCAN_DATA_BYTES_8;
116-
117-
init->TxFifoQueueElmtsNbr = 16; // Tx fifo elements
118-
init->TxElmtSize = FDCAN_DATA_BYTES_8;
102+
// An element stored in the Message RAM contains an identifier, DLC, control bits, the
103+
// data field and the specific transmission or reception bits field for control.
104+
// The following code configures the different Message RAM sections per FDCAN instance.
105+
106+
// The RAM filtering section is configured for 64 x 1 word elements for 11-bit standard
107+
// identifiers, and 31 x 2 words elements for 29-bit extended identifiers.
108+
// The total number of words reserved for the filtering per FDCAN instance is 126 words.
109+
init->StdFiltersNbr = 64;
110+
// Note extended identifiers are Not used in pyb_can.c and Not handled correctly.
111+
// Disable the extended identifiers filters for now until this is fixed properly.
112+
init->ExtFiltersNbr = 0 /*31*/;
113+
114+
// The Tx event FIFO is used to store the message ID and the timestamp of successfully
115+
// transmitted elements. The Tx event FIFO can store a maximum of 32 (2 words) elements.
116+
// NOTE: Events are stored in Tx event FIFO only if tx_msg.TxEventFifoControl is enabled.
117+
init->TxEventsNbr = 0;
118+
119+
// Transmission section is configured in FIFO mode operation, with no dedicated Tx buffers.
120+
// The Tx FIFO can store a maximum of 32 elements (or 576 words), each element is 18 words
121+
// long (to support a maximum of 64 bytes data field):
122+
// 2 words header + 16 words data field (to support up to 64 bytes of data).
123+
// The total number of words reserved for the Tx FIFO per FDCAN instance is 288 words.
124+
init->TxBuffersNbr = 0;
125+
init->TxFifoQueueElmtsNbr = 16;
126+
init->TxElmtSize = FDCAN_DATA_BYTES_64;
127+
init->TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
119128

129+
// Reception section is configured to use Rx FIFO 0 and Rx FIFO1, with no dedicated Rx buffers.
130+
// Each Rx FIFO can store a maximum of 64 elements (1152 words), each element is 18 words
131+
// long (to support a maximum of 64 bytes data field):
132+
// 2 words header + 16 words data field (to support up to 64 bytes of data).
133+
// The total number of words reserved for the Rx FIFOs per FDCAN instance is 864 words.
134+
init->RxBuffersNbr = 0;
135+
init->RxFifo0ElmtsNbr = 24;
136+
init->RxFifo0ElmtSize = FDCAN_DATA_BYTES_64;
137+
init->RxFifo1ElmtsNbr = 24;
138+
init->RxFifo1ElmtSize = FDCAN_DATA_BYTES_64;
120139
#endif
121140

122-
init->TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
123-
124141
FDCAN_GlobalTypeDef *CANx = NULL;
125142
const pin_obj_t *pins[2];
126143

@@ -159,7 +176,10 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
159176

160177
// init CANx
161178
can_obj->can.Instance = CANx;
162-
HAL_FDCAN_Init(&can_obj->can);
179+
// catch bad configuration errors.
180+
if (HAL_FDCAN_Init(&can_obj->can) != HAL_OK) {
181+
return false;
182+
}
163183

164184
// Disable acceptance of non-matching frames (enabled by default)
165185
HAL_FDCAN_ConfigGlobalFilter(&can_obj->can, FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);
@@ -168,7 +188,7 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
168188
HAL_FDCAN_Start(&can_obj->can);
169189

170190
// Reset all filters
171-
for (int f = 0; f < 64; ++f) {
191+
for (int f = 0; f < init->StdFiltersNbr; ++f) {
172192
can_clearfilter(can_obj, f, 0);
173193
}
174194

@@ -299,10 +319,12 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr,
299319
hdr->FDFormat = *address & FDCAN_ELEMENT_MASK_FDF;
300320
hdr->FilterIndex = (*address & FDCAN_ELEMENT_MASK_FIDX) >> 24;
301321
hdr->IsFilterMatchingFrame = (*address++ & FDCAN_ELEMENT_MASK_ANMF) >> 31;
322+
// Convert DLC to Bytes.
323+
hdr->DataLength = DLCtoBytes[hdr->DataLength];
302324

303325
// Copy data
304326
uint8_t *pdata = (uint8_t *)address;
305-
for (uint32_t i = 0; i < DLCtoBytes[hdr->DataLength]; ++i) {
327+
for (uint32_t i = 0; i < hdr->DataLength; ++i) {
306328
*data++ = *pdata++;
307329
}
308330

ports/stm32/pyb_can.c

Lines changed: 91 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#if MICROPY_HW_ENABLE_FDCAN
4343

4444
#define CAN_MAX_FILTER (64)
45+
#define CAN_MAX_DATA_FRAME (64)
4546

4647
#define CAN_FIFO0 FDCAN_RX_FIFO0
4748
#define CAN_FIFO1 FDCAN_RX_FIFO1
@@ -89,10 +90,11 @@
8990

9091
// Both banks start at 0
9192
STATIC uint8_t can2_start_bank = 0;
92-
93+
extern const uint8_t DLCtoBytes[16];
9394
#else
9495

9596
#define CAN_MAX_FILTER (28)
97+
#define CAN_MAX_DATA_FRAME (8)
9698

9799
#define CAN_DEFAULT_PRESCALER (100)
98100
#define CAN_DEFAULT_SJW (1)
@@ -180,19 +182,50 @@ STATIC uint32_t pyb_can_get_source_freq() {
180182
return can_kern_clk;
181183
}
182184

185+
STATIC void pyb_can_get_bit_timing(mp_uint_t baudrate, mp_uint_t sample_point,
186+
mp_int_t *bs1_out, mp_int_t *bs2_out, mp_int_t *prescaler_out) {
187+
uint32_t can_kern_clk = pyb_can_get_source_freq();
188+
189+
// The following max values work on all MCUs for classical CAN.
190+
for (int brp = 1; brp < 512; brp++) {
191+
for (int bs1 = 1; bs1 < 16; bs1++) {
192+
for (int bs2 = 1; bs2 < 8; bs2++) {
193+
if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) &&
194+
((sample_point * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) {
195+
*bs1_out = bs1;
196+
*bs2_out = bs2;
197+
*prescaler_out = brp;
198+
return;
199+
}
200+
}
201+
}
202+
}
203+
204+
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("couldn't match baudrate and sample point"));
205+
}
206+
183207
// init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8)
184208
STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
185-
enum { ARG_mode, ARG_extframe, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart, ARG_baudrate, ARG_sample_point };
209+
enum { ARG_mode, ARG_extframe, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart, ARG_baudrate, ARG_sample_point,
210+
ARG_brs_prescaler, ARG_brs_sjw, ARG_brs_bs1, ARG_brs_bs2, ARG_brs_baudrate, ARG_brs_sample_point };
186211
static const mp_arg_t allowed_args[] = {
187-
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_MODE_NORMAL} },
188-
{ MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} },
189-
{ MP_QSTR_prescaler, MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} },
190-
{ MP_QSTR_sjw, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_SJW} },
191-
{ MP_QSTR_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} },
192-
{ MP_QSTR_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} },
193-
{ MP_QSTR_auto_restart, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
194-
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
195-
{ MP_QSTR_sample_point, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 75} }, // 75% sampling point
212+
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_MODE_NORMAL} },
213+
{ MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} },
214+
{ MP_QSTR_prescaler, MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} },
215+
{ MP_QSTR_sjw, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_SJW} },
216+
{ MP_QSTR_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} },
217+
{ MP_QSTR_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} },
218+
{ MP_QSTR_auto_restart, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
219+
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
220+
{ MP_QSTR_sample_point, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 75} }, // 75% sampling point
221+
#if MICROPY_HW_ENABLE_FDCAN
222+
{ MP_QSTR_brs_prescaler, MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} },
223+
{ MP_QSTR_brs_sjw, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_SJW} },
224+
{ MP_QSTR_brs_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} },
225+
{ MP_QSTR_brs_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} },
226+
{ MP_QSTR_brs_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
227+
{ MP_QSTR_brs_sample_point, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }
228+
#endif
196229
};
197230

198231
// parse args
@@ -206,34 +239,30 @@ STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp
206239

207240
// Calculate CAN bit timing from baudrate if provided
208241
if (args[ARG_baudrate].u_int != 0) {
209-
uint32_t baudrate = args[ARG_baudrate].u_int;
210-
uint32_t sampoint = args[ARG_sample_point].u_int;
211-
uint32_t can_kern_clk = pyb_can_get_source_freq();
212-
bool timing_found = false;
213-
214-
// The following max values work on all MCUs for classical CAN.
215-
for (int brp = 1; brp < 512 && !timing_found; brp++) {
216-
for (int bs1 = 1; bs1 < 16 && !timing_found; bs1++) {
217-
for (int bs2 = 1; bs2 < 8 && !timing_found; bs2++) {
218-
if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) &&
219-
((sampoint * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) {
220-
args[ARG_bs1].u_int = bs1;
221-
args[ARG_bs2].u_int = bs2;
222-
args[ARG_prescaler].u_int = brp;
223-
timing_found = true;
224-
}
225-
}
226-
}
227-
}
228-
if (!timing_found) {
229-
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("couldn't match baudrate and sample point"));
230-
}
242+
pyb_can_get_bit_timing(args[ARG_baudrate].u_int, args[ARG_sample_point].u_int,
243+
&args[ARG_bs1].u_int, &args[ARG_bs2].u_int, &args[ARG_prescaler].u_int);
231244
}
232245

233-
// init CAN (if it fails, it's because the port doesn't exist)
246+
#if MICROPY_HW_ENABLE_FDCAN
247+
// If no sample point is provided for data bit timing, use the nominal sample point.
248+
if (args[ARG_brs_sample_point].u_int == 0) {
249+
args[ARG_brs_sample_point].u_int = args[ARG_sample_point].u_int;
250+
}
251+
// Calculate BRS CAN bit timing from baudrate if provided
252+
if (args[ARG_brs_baudrate].u_int != 0) {
253+
pyb_can_get_bit_timing(args[ARG_brs_baudrate].u_int, args[ARG_brs_sample_point].u_int,
254+
&args[ARG_brs_bs1].u_int, &args[ARG_brs_bs2].u_int, &args[ARG_brs_prescaler].u_int);
255+
}
256+
// Set BRS bit timings.
257+
self->can.Init.DataPrescaler = args[ARG_brs_prescaler].u_int;
258+
self->can.Init.DataSyncJumpWidth = args[ARG_brs_sjw].u_int;
259+
self->can.Init.DataTimeSeg1 = args[ARG_bs1].u_int; // DataTimeSeg1 = Propagation_segment + Phase_segment_1
260+
self->can.Init.DataTimeSeg2 = args[ARG_bs2].u_int;
261+
#endif
262+
234263
if (!can_init(self, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int,
235264
args[ARG_bs1].u_int, args[ARG_bs2].u_int, args[ARG_auto_restart].u_bool)) {
236-
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("CAN(%d) doesn't exist"), self->can_id);
265+
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("CAN(%d) init failure"), self->can_id);
237266
}
238267

239268
return mp_const_none;
@@ -450,12 +479,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_any_obj, pyb_can_any);
450479

451480
// send(send, addr, *, timeout=5000)
452481
STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
453-
enum { ARG_data, ARG_id, ARG_timeout, ARG_rtr };
482+
enum { ARG_data, ARG_id, ARG_timeout, ARG_rtr, ARG_fdf, ARG_brs };
454483
static const mp_arg_t allowed_args[] = {
455484
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
456485
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
457486
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
458487
{ MP_QSTR_rtr, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
488+
#if MICROPY_HW_ENABLE_FDCAN
489+
{ MP_QSTR_fdf, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
490+
{ MP_QSTR_brs, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
491+
#endif
459492
};
460493

461494
// parse args
@@ -468,21 +501,20 @@ STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
468501
uint8_t data[1];
469502
pyb_buf_get_for_send(args[ARG_data].u_obj, &bufinfo, data);
470503

471-
if (bufinfo.len > 8) {
504+
if (bufinfo.len > CAN_MAX_DATA_FRAME) {
472505
mp_raise_ValueError(MP_ERROR_TEXT("CAN data field too long"));
473506
}
474507

475508
// send the data
476509
CanTxMsgTypeDef tx_msg;
477510

478511
#if MICROPY_HW_ENABLE_FDCAN
479-
uint8_t tx_data[8];
512+
uint8_t tx_data[CAN_MAX_DATA_FRAME];
513+
memset(tx_data, 0, sizeof(tx_data));
514+
480515
tx_msg.MessageMarker = 0;
481516
tx_msg.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
482-
tx_msg.BitRateSwitch = FDCAN_BRS_OFF;
483-
tx_msg.FDFormat = FDCAN_CLASSIC_CAN;
484517
tx_msg.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
485-
tx_msg.DataLength = (bufinfo.len << 16); // TODO DLC for len > 8
486518

487519
if (self->extframe) {
488520
tx_msg.Identifier = args[ARG_id].u_int & 0x1FFFFFFF;
@@ -496,6 +528,23 @@ STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
496528
} else {
497529
tx_msg.TxFrameType = FDCAN_REMOTE_FRAME;
498530
}
531+
if (args[ARG_fdf].u_bool == false) {
532+
tx_msg.FDFormat = FDCAN_CLASSIC_CAN;
533+
} else {
534+
tx_msg.FDFormat = FDCAN_FD_CAN;
535+
}
536+
if (args[ARG_brs].u_bool == false) {
537+
tx_msg.BitRateSwitch = FDCAN_BRS_OFF;
538+
} else {
539+
tx_msg.BitRateSwitch = FDCAN_BRS_ON;
540+
}
541+
// Roundup DataLength to next DLC size and encode to DLC.
542+
for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(DLCtoBytes); i++) {
543+
if (bufinfo.len <= DLCtoBytes[i]) {
544+
tx_msg.DataLength = (i << 16);
545+
break;
546+
}
547+
}
499548
#else
500549
tx_msg.DLC = bufinfo.len;
501550
uint8_t *tx_data = tx_msg.Data; // Data is uint32_t but holds only 1 byte
@@ -565,7 +614,7 @@ STATIC mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
565614
// receive the data
566615
CanRxMsgTypeDef rx_msg;
567616
#if MICROPY_HW_ENABLE_FDCAN
568-
uint8_t rx_data[8];
617+
uint8_t rx_data[CAN_MAX_DATA_FRAME];
569618
#else
570619
uint8_t *rx_data = rx_msg.Data;
571620
#endif

0 commit comments

Comments
 (0)