ST FDCAN Introduction
Preface
FDCAN (Flexible Data-Rate CAN) is an upgraded version of CAN. Its features include:
- The maximum length of the data segment per frame has increased from 8 bytes to 64 bytes.
- The speed has increased from 1 Mbps to 5 Mbps, and even higher speeds are possible. In a data frame, the arbitration segment (ID and ACK) maintains the same maximum speed as CAN, which is 1 Mbps. This ensures robustness and reliability of the bus. However, the data segment can operate at 5 Mbps or even higher. Different baud rates can be used within a single data frame, hence the name “Flexible Data-Rate” (FD).
- It is backward compatible with CAN.
FDCAN Format
- a first arbitration phase
- a data phase
- a second arbitration phase
Frame architecture comparison between CAN-FD and CAN 2.0
After identifier, CAN 2.0 and CAN-FD have a different action:
- CAN 2.0 sends an RTR bit to precise the type of frame: data frame (RTR is dominant) or remote frame(RTR is recessive).
- CAN-FD sends always a dominant RRS (reserved) as it only supports data frames.
Main differences between CAN-FD and CAN 2.0
STM32CubeMX Setting
You can refer to the H7 configuration options available online. When using G4 or G0, there will be a lack of configuration options for Message RAM allocation. The rest of the settings are similar.
FDCAN sample code
you could refer as below for transmit data
#include <string.h>
FDCAN_TxHeaderTypeDef TxHeader1;
uint8_t TxData1[64];
typedef struct {
uint8_t flag;
uint8_t index;
FDCAN_TxHeaderTypeDef TxHeader[16];
uint8_t TxData[64*16];
} FDCAN_SendFailTypeDef;
FDCAN_SendFailTypeDef fdcan1_send_fail = {0};
void fdcan1_transmit(uint32_t can_id, uint32_t DataLength, uint8_t tx_data[])
{
TxHeader1.Identifier = can_id;
TxHeader1.IdType = FDCAN_EXTENDED_ID;
if(can_id < 0x800) { //exactly not right
TxHeader1.IdType = FDCAN_STANDARD_ID;
}
TxHeader1.TxFrameType = FDCAN_DATA_FRAME;
TxHeader1.DataLength = DataLength;
TxHeader1.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader1.BitRateSwitch = FDCAN_BRS_ON;
TxHeader1.FDFormat = FDCAN_FD_CAN;
TxHeader1.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
TxHeader1.MessageMarker = 0; //Tx Event FIFO Use
if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader1, tx_data) != HAL_OK) {
memcpy(&fdcan1_send_fail.TxHeader[fdcan1_send_fail.index], &TxHeader1, sizeof(FDCAN_TxHeaderTypeDef));
memcpy(&fdcan1_send_fail.TxData[64*fdcan1_send_fail.index], tx_data, can_dlc2len(DataLength));
fdcan1_send_fail.index = (fdcan1_send_fail.index + 1) & 0x0F; //0~15
fdcan1_send_fail.flag = 1;
}
}
int main(void)
{
...
/* USER CODE BEGIN 2 */
fdcan1_config();
fdcan2_config();
for(uint8_t i = 0; i < 64; i++) {
TxData1[i] = i;
TxData2[i] = i;
}
HAL_TIM_Base_Start_IT(&htim6);
uint32_t count = 0;
uint32_t cnt_100us = 0;
uint32_t cnt_500us = 0;
uint32_t i = 0;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(tim6_flag && count < 1000) {
tim6_flag = 0;
TxData1[0] = count >> 8 & 0xFF;
TxData1[1] = count & 0xFF;
++cnt_100us;
cnt_500us = cnt_100us / 5;
if(cnt_500us && (cnt_100us%5==0) ) {
switch(cnt_500us) {
case 1: fdcan1_transmit(0x108, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x101, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x102, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x103, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x104, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x105, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x106, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x107, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x12345678, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x12345671, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x12345672, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x12345673, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x12345674, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x12345675, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x12345676, FDCAN_DLC_BYTES_64, TxData1);
fdcan1_transmit(0x12345677, FDCAN_DLC_BYTES_64, TxData1);
break;
case 17: /* next send */ break;
case 18: break;
case 19: break;
case 20: ++count; cnt_100us = 0; break; //10ms
}
} else { //fail retransmission once
if(fdcan1_send_fail.flag && cnt_500us > 16) { //can't conflict with normal send
for(i = 0; i < fdcan1_send_fail.index; i++) {
fdcan1_transmit(fdcan1_send_fail.TxHeader[i].Identifier,
fdcan1_send_fail.TxHeader[i].DataLength,
&fdcan1_send_fail.TxData[64*i]);
}
fdcan1_send_fail.index = 0;
fdcan1_send_fail.flag = 0; //maybe send 4 times or more
}
if(fdcan2_send_fail.flag && cnt_500us > 16) {
for(i = 0; i < fdcan2_send_fail.index; i++) {
fdcan2_transmit(fdcan2_send_fail.TxHeader[i].Identifier,
fdcan2_send_fail.TxHeader[i].DataLength,
&fdcan2_send_fail.TxData[64*i]);
}
fdcan2_send_fail.index = 0;
fdcan2_send_fail.flag = 0;
}
}
}
}
/* USER CODE END 3 */
}
receive
FDCAN_RxHeaderTypeDef RxHeader1;
uint8_t RxData1[64];
uint8_t RxData2[64];
uint8_t dlc2len[]={0,1,2,3,4,5,6,7,8,12,16,20,24,32,48,64};
uint8_t can_dlc2len(uint32_t RxHeader_DataLength)
{
return dlc2len[RxHeader_DataLength>>16];
}
uint8_t cnt = 0;
uint8_t brs[] = {'-', 'B'};
uint8_t esi[] = {'-', 'E'};
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != 0) {
//memset(&RxHeader1, 0, sizeof(FDCAN_RxHeaderTypeDef)); //if use, lose frame
HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader1, RxData1);
if (hfdcan->Instance == FDCAN1) {
printf("fdcan1, ");
} else if (hfdcan->Instance == FDCAN2) {
printf("fdcan2, ");
} else {
}
printf("0x%8X, %02d, %c, %c:",RxHeader1.Identifier,
can_dlc2len(RxHeader1.DataLength),
brs[RxHeader1.BitRateSwitch>>20 & 0x1],
esi[RxHeader1.ErrorStateIndicator>>31 & 0x1]);
for(cnt = 0; cnt < can_dlc2len(RxHeader1.DataLength); cnt++) {
printf(" %02X", RxData1[cnt]);
}
printf("\n\r");
}
}