ST FDCAN介紹
前言
FDCAN(Flexible Data-Rate CAN)是CAN的升級版。特點包括
- 每幀數據段最大長度由8字節上升到64字節。
- 速度由1Mbps上升到5Mbps,甚至還可以更高。在一個數據幀中仲裁段(ID和ACK)的速率和CAN一樣最高1Mbps,這樣可以保證總線的健壯可靠,但是數據段可以5Mbps甚至更高,一個數據幀中使用不同的波特率,這就是FD(Flexible Data-Rate)的由來。
- 向下兼容CAN
FDCAN格式
- 第一仲裁階段
- 數據階段
- 第二仲裁階段
CAN-FD 與 CAN 2.0 的幀架構比較
在標識符之後,CAN 2.0 和 CAN-FD 有一個不同的操作:
- CAN 2.0 發送 RTR 位明確幀類型:數據幀(RTR 為顯性)或遠程幀(RTR 為隱性)。
- CAN-FD 只支持數據幀,因此總是發送顯性 RRS(預留)。
CAN-FD 與 CAN 2.0 之間的主要差異
STM32CubeMX Setting
這邊可以參考網路上H7的設定選項,使用G4或G0會缺少Messge Ram分配這區塊設定,其他部分雷同
FDCAN傳輸參考程式
這邊可以參考輸出如下
#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 */
}
讀取如下
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");
}
}