做这个网站多少钱网上做广告宣传
FDCAN应用说明
概述
最近用到了FDCAN,下面对FDCAN的一些使用注意事项进行分析。
一些FDCAN的基本概念可以看这些文章。一文搞懂CAN FD总线协议帧格式 - 知乎、STM32H743使用FDCAN+TJA1042T配合CubeMX及HAL库实现仲裁段1M速率,数据段5M速率的FDCAN总线传输记录_stm32h743 can cubemx-CSDN博客
假设作为软件工程师,不想关注具体里面的东西,可以简单理解为每个CAN交互报文都是如下格式。ID和CRC是协议必须带的,Data0-Datan是实际要发送的内容。
对于FDCAN和CAN的差异,从软件侧来看,主要包括如下。由于ID和CRC要占不少东西,而且还有抢占的问题,所以一般软件能用FDCAN就用FDCAN。
CAN | FDCAN | |
---|---|---|
Data最大长度(Bytes) | 8 | 64 |
Data段最大速度 | 1Mbps | 5Mbps(其他段最大还是1Mbps) |
PHY芯片 | 普通的TJA1050就行 | 如果要大于1Mbps,用TJA1042 |
ST芯片时钟配置
在STM32H5上面使用时,里面有2个FDCAN控制器,可独立使用。
但是FDCAN2的时钟配置好像必须一样,不然行为就很奇怪。其他配置没细的尝试,应该主要和红框有关,其他影响不大。
某种情况下,IO有限,只能使用FDCAN2时,分频系数必须配置为DIV1
。
ST芯片FDCAN重置
FDCAN有个比较麻烦的地方,就是其是有ACK机制的,也就是发送的数据包如果没ACK(或者没接PHY转换芯片,其会监控TX和RX信号),数据包会一直在队列中。
但是应用某些情况下需要把FIFO中的数据包都清空。可以先调用HAL_FDCAN_Stop
接口,再调用HAL_FDCAN_Start
接口,即可重置。
void FDCAN_DeConfig(FDCAN_HandleTypeDef *hfdcan)
{if (HAL_FDCAN_Stop(hfdcan) != HAL_OK){while(1);}
}void FDCAN_Config(FDCAN_HandleTypeDef *hfdcan)
{.../* Start the FDCAN module */if (HAL_FDCAN_Start(hfdcan) != HAL_OK){while(1);}
}void fdcan_control_restart(FDCAN_HandleTypeDef *hfdcan)
{FDCAN_DeConfig(hfdcan);FDCAN_Config(hfdcan);
}
FDCAN的TX和RX
需要注意,当有PHY芯片时,如果用逻辑分析仪看MCU的TX和RX信号,会发现TX发送任何数据,RX就会收到与之对应的数据。
这也是为什么MCU可以做到冲突规避的原理。
注意,可以把总线所有设备都拿掉,直接看TX,RX的信号。
FDCAN的ID唯一性
注意,注意,注意
,这里每个设备最好都用一个唯一的ID来总线上交互,不然会丢包。
由于总线上每个设备都可以随时发起数据包请求,当然应用协议上做好限制,交互是响应式(Request+Response)的请求模式的话, 允许所有设备用一个ID进行交互。
因为当设备的ID相同时,假设同时发起来FDCAN请求,那FDCAN的硬件仲裁机制将无法正常工作,这时总线上很可能有多个设备同时工作,有些设备的数据包可能没发送出去,但是硬件却认为数据包已经发送成功了。这个在大规模数据压力测试时出现会恶心死。
FDCAN的拓扑结构
标准的CAN网络要求有一个总线,所有设备都接入到总线中,总线的起点和终点需要放置120欧的电阻。
但是实际项目中基本都不是这种情况,一般都是星形网络,支持节点的热插拔。
在这种网络中,并没有像汽车那种一根长长的总线。总线可能就是一个PCB板,留了很多接入点,设备可以用线缆连入,这个情况下,总线长度可能不到10cm
,支线的长度却有2-5m
的长度,这个情况下,因为不确定什么时候有设备加入,也无法决定哪个子节点需要加120欧电阻。
那这个情况下,就直接所有子节点都不加120欧电阻。通过示波器查看电压,可以看看各个节点的压降都还不错。
此外,既然已经非标了,就别使用太高速的CAN频率,使用250kbps
或者500kbps
即可。
FDCAN测试设备
协议分析怎么能少了仪器,虽然示波器已经能看很多东西了,但是业务问题还是有这种抓包仪器会好分析一些。200-300就可以抓包看了。多通道USB转CAN分析仪调试器CAN盒CANFD总线CANopen J1939OBD ZLG-淘宝网。
个人买了一个2通道的就足够用了。
安装上位机软件可以抓到交互的数据包。
FDCAN总线示波器波形
可以看看大佬写的文章,写得很好了。用示波器排查CAN的各种错误帧(1) - 知乎
最好在每个终端节点旁边都测测其看到的差分电平压降,只要不衰减太厉害,所设计的电路和can网络拓扑就没什么问题。下面借鉴大佬的一个图,上面的相对干净了。
确保上升沿和下降沿迅速切换,别有斜坡(别用太大的电容)。
一个简易FDCAN控制器代码
#include <stdint.h>
#include <string.h>FDCAN_HandleTypeDef hfdcan1;
FDCAN_HandleTypeDef hfdcan2;/*** @brief This function handles FDCAN1 interrupt 0.*/void FDCAN1_IT0_IRQHandler(void){/* USER CODE BEGIN FDCAN1_IT0_IRQn 0 *//* USER CODE END FDCAN1_IT0_IRQn 0 */HAL_FDCAN_IRQHandler(&hfdcan1);/* USER CODE BEGIN FDCAN1_IT0_IRQn 1 *//* USER CODE END FDCAN1_IT0_IRQn 1 */}/*** @brief This function handles FDCAN2 interrupt 0.*/
void FDCAN2_IT0_IRQHandler(void)
{/* USER CODE BEGIN FDCAN2_IT0_IRQn 0 *//* USER CODE END FDCAN2_IT0_IRQn 0 */HAL_FDCAN_IRQHandler(&hfdcan2);/* USER CODE BEGIN FDCAN2_IT0_IRQn 1 *//* USER CODE END FDCAN2_IT0_IRQn 1 */
}/*** @brief FDCAN1 Initialization Function* @param None* @retval None*/static void MX_FDCAN1_Init(void){/* USER CODE BEGIN FDCAN1_Init 0 *//* USER CODE END FDCAN1_Init 0 *//* USER CODE BEGIN FDCAN1_Init 1 *//* USER CODE END FDCAN1_Init 1 */hfdcan1.Instance = FDCAN1;hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV2;hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_NO_BRS;hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;hfdcan1.Init.AutoRetransmission = ENABLE;hfdcan1.Init.TransmitPause = DISABLE;hfdcan1.Init.ProtocolException = DISABLE;hfdcan1.Init.NominalPrescaler = 1;hfdcan1.Init.NominalSyncJumpWidth = 62;hfdcan1.Init.NominalTimeSeg1 = 187;hfdcan1.Init.NominalTimeSeg2 = 62;hfdcan1.Init.DataPrescaler = 2;hfdcan1.Init.DataSyncJumpWidth = 5;hfdcan1.Init.DataTimeSeg1 = 19;hfdcan1.Init.DataTimeSeg2 = 5;hfdcan1.Init.StdFiltersNbr = 1;hfdcan1.Init.ExtFiltersNbr = 0;hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK){while(1);}/* USER CODE BEGIN FDCAN1_Init 2 *//* USER CODE END FDCAN1_Init 2 */}/*** @brief FDCAN2 Initialization Function* @param None* @retval None*/static void MX_FDCAN2_Init(void){/* USER CODE BEGIN FDCAN2_Init 0 *//* USER CODE END FDCAN2_Init 0 *//* USER CODE BEGIN FDCAN2_Init 1 *//* USER CODE END FDCAN2_Init 1 */hfdcan2.Instance = FDCAN2;hfdcan2.Init.ClockDivider = FDCAN_CLOCK_DIV2;hfdcan2.Init.FrameFormat = FDCAN_FRAME_FD_NO_BRS;hfdcan2.Init.Mode = FDCAN_MODE_NORMAL;hfdcan2.Init.AutoRetransmission = ENABLE;hfdcan2.Init.TransmitPause = DISABLE;hfdcan2.Init.ProtocolException = DISABLE;hfdcan2.Init.NominalPrescaler = 1;hfdcan2.Init.NominalSyncJumpWidth = 62;hfdcan2.Init.NominalTimeSeg1 = 187;hfdcan2.Init.NominalTimeSeg2 = 62;hfdcan2.Init.DataPrescaler = 2;hfdcan2.Init.DataSyncJumpWidth = 5;hfdcan2.Init.DataTimeSeg1 = 19;hfdcan2.Init.DataTimeSeg2 = 5;hfdcan2.Init.StdFiltersNbr = 1;hfdcan2.Init.ExtFiltersNbr = 0;hfdcan2.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;if (HAL_FDCAN_Init(&hfdcan2) != HAL_OK){while(1);}/* USER CODE BEGIN FDCAN2_Init 2 *//* USER CODE END FDCAN2_Init 2 */}void FDCAN_DeConfig(FDCAN_HandleTypeDef *hfdcan)
{if (HAL_FDCAN_Stop(hfdcan) != HAL_OK){while(1);}
}void FDCAN_Config(FDCAN_HandleTypeDef *hfdcan)
{FDCAN_FilterTypeDef sFilterConfig;/* Configure Rx filter */sFilterConfig.IdType = FDCAN_STANDARD_ID;sFilterConfig.FilterIndex = 0;sFilterConfig.FilterType = FDCAN_FILTER_RANGE;if(hfdcan->Instance == FDCAN1){sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;// sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1;}else if(hfdcan->Instance == FDCAN2){sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1;}sFilterConfig.FilterID1 = 0x00000000;sFilterConfig.FilterID2 = 0x000007FF;if (HAL_FDCAN_ConfigFilter(hfdcan, &sFilterConfig) != HAL_OK){while(1);}/* Configure global filter:Filter all remote frames with STD and EXT IDReject non matching frames with STD ID and EXT ID */if (HAL_FDCAN_ConfigGlobalFilter(hfdcan, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK){while(1);}/* Activate Rx FIFO 0 new message notification on both FDCAN instances */// if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE |// FDCAN_IT_RAM_ACCESS_FAILURE | FDCAN_IT_ERROR_LOGGING_OVERFLOW | FDCAN_IT_RAM_WATCHDOG | FDCAN_IT_ARB_PROTOCOL_ERROR | FDCAN_IT_DATA_PROTOCOL_ERROR | FDCAN_IT_RESERVED_ADDRESS_ACCESS // | FDCAN_IT_ERROR_PASSIVE | FDCAN_IT_ERROR_WARNING | FDCAN_IT_BUS_OFF// , 0) != HAL_OK)// {// while(1);// }if(hfdcan->Instance == FDCAN1){if (HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO0_FULL, 0) != HAL_OK){while(1);}}else{if (HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_RX_FIFO1_NEW_MESSAGE | FDCAN_IT_RX_FIFO1_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_FULL, 0) != HAL_OK){while(1);}}// if (HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_BUS_OFF, 0) != HAL_OK)// {// while(1);// }/* Configure and enable Tx Delay Compensation, required for BRS mode.TdcOffset default recommended value: DataTimeSeg1 * DataPrescalerTdcFilter default recommended value: 0 */HAL_FDCAN_ConfigTxDelayCompensation(hfdcan, hfdcan->Init.DataPrescaler * hfdcan->Init.DataTimeSeg1, 0);HAL_FDCAN_EnableTxDelayCompensation(hfdcan);/* Start the FDCAN module */if (HAL_FDCAN_Start(hfdcan) != HAL_OK){while(1);}
}// void HAL_FDCAN_ErrorCallback(FDCAN_HandleTypeDef *hfdcan)
// {
// printf("HAL_FDCAN_ErrorCallback, Error Code = 0x%x, State = 0x%x\r\n", hfdcan->ErrorCode, hfdcan->State);
// }// void HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t ErrorStatusITs)
// {
// printf("HAL_FDCAN_ErrorStatusCallback, Error Code = 0x%x, State = 0x%x, ErrorStatusITs = 0x%x\r\n", hfdcan->ErrorCode, hfdcan->State, ErrorStatusITs);
// }int fdcan_control_get_tx_len_by_real_len(uint32_t real_len)
{if(real_len <= 0){return FDCAN_DLC_BYTES_0;}else if(real_len <= 1){return FDCAN_DLC_BYTES_1;}else if(real_len <= 2){return FDCAN_DLC_BYTES_2;}else if(real_len <= 3){return FDCAN_DLC_BYTES_3;}else if(real_len <= 4){return FDCAN_DLC_BYTES_4;}else if(real_len <= 5){return FDCAN_DLC_BYTES_5;}else if(real_len <= 6){return FDCAN_DLC_BYTES_6;}else if(real_len <= 7){return FDCAN_DLC_BYTES_7;}else if(real_len <= 8){return FDCAN_DLC_BYTES_8;}else if(real_len <= 12){return FDCAN_DLC_BYTES_12;}else if(real_len <= 16){return FDCAN_DLC_BYTES_16;}else if(real_len <= 20){return FDCAN_DLC_BYTES_20;}else if(real_len <= 24){return FDCAN_DLC_BYTES_24;}else if(real_len <= 32){return FDCAN_DLC_BYTES_32;}else if(real_len <= 48){return FDCAN_DLC_BYTES_48;}return FDCAN_DLC_BYTES_64;
}void fdcan_control_restart(FDCAN_HandleTypeDef *hfdcan)
{int index = hfdcan->Instance == FDCAN1 ? 0 : 1;EASY_LOG_ERR("fdcan_control_restart, index: %d\r\n", index);// reconfig fdcanFDCAN_DeConfig(hfdcan);FDCAN_Config(hfdcan);
}int fdcan_control_tx_packet(FDCAN_HandleTypeDef *hfdcan, uint16_t id, const uint8_t *data, uint32_t len)
{uint8_t data_buf[64] = {0};int index = hfdcan->Instance == FDCAN1 ? 0 : 1;if(len > 64){len = 64;}memcpy(data_buf, data, len);FDCAN_TxHeaderTypeDef TxHeader;TxHeader.Identifier = id; // CAN IDTxHeader.IdType = FDCAN_STANDARD_ID; // 标准IDTxHeader.TxFrameType = FDCAN_DATA_FRAME;TxHeader.DataLength = fdcan_control_get_tx_len_by_real_len(len); // 发送长度:64byteTxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;TxHeader.BitRateSwitch = FDCAN_BRS_OFF;TxHeader.FDFormat = FDCAN_FD_CAN; // CANFDTxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS; TxHeader.MessageMarker = 0;int ret = 0;ret = HAL_FDCAN_AddMessageToTxFifoQ(hfdcan, &TxHeader, data_buf);if(ret != HAL_OK){return 0;}else{}return len;
}void HAL_FDCAN_TxBufferCompleteCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes)
{EASY_LOG_DBG("Tx complete\r\n");
}__EASY_WEAK__ void fdcan1_control_rx_packet(uint32_t can_id, uint8_t *data, uint32_t len)
{}__EASY_WEAK__ void fdcan2_control_rx_packet(uint32_t can_id, uint8_t *data, uint32_t len)
{}static const uint8_t DLCtoBytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64};
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{FDCAN_RxHeaderTypeDef RxHeader;uint8_t data_buf[64] = {0};uint16_t data_len = 0;if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET){// int work_cnt = 0;/* Retrieve Rx messages from RX FIFO0 *//* Check if FIFO 0 receive at least one message */while (HAL_FDCAN_GetRxFifoFillLevel(hfdcan, FDCAN_RX_FIFO0)){// work_cnt ++;// if(work_cnt > 1)// {// printf("!!!!!!!work_cnt = %d\r\n", work_cnt);// }if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, data_buf) != HAL_OK){while(1);}data_len = DLCtoBytes[RxHeader.DataLength];// printf("Identifier = 0x%x\r\n", RxHeader.Identifier);// printf("DataLength = %d(0x%x)\r\n", data_len, RxHeader.DataLength);// printf("FDFormat = 0x%x\r\n", RxHeader.FDFormat);// printf("BitRateSwitch = %d\r\n", RxHeader.BitRateSwitch);// printf("ErrorStateIndicator = 0x%x\r\n", RxHeader.ErrorStateIndicator);// for(int i = 0; i < data_len; i++)// {// printf("Data[%d] = 0x%x\r\n", i, can1_rxbuf[i]);// }if(hfdcan->Instance == FDCAN1){fdcan1_control_rx_packet(RxHeader.Identifier, data_buf, data_len);}else if(hfdcan->Instance == FDCAN2){fdcan2_control_rx_packet(RxHeader.Identifier, data_buf, data_len);}}/* Retrieve Rx messages from RX FIFO0 */// status = HAL_FDCAN_GetRxMessage(&hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData);// if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, data_buf) != HAL_OK)// {// while(1);// }}if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_MESSAGE_LOST) != RESET){EASY_LOG_DBG("Rx FIFO0 message lost\r\n");}if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_FULL) != RESET){EASY_LOG_DBG("Rx FIFO0 message full\r\n");}
}void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo1ITs)
{FDCAN_RxHeaderTypeDef RxHeader;uint8_t data_buf[64] = {0};uint16_t data_len = 0;if((RxFifo1ITs & FDCAN_IT_RX_FIFO1_NEW_MESSAGE) != RESET){// int work_cnt = 0;/* Retrieve Rx messages from RX FIFO1 *//* Check if FIFO 0 receive at least one message */while (HAL_FDCAN_GetRxFifoFillLevel(hfdcan, FDCAN_RX_FIFO1)){// work_cnt ++;// if(work_cnt > 1)// {// printf("!!!!!!!work_cnt = %d\r\n", work_cnt);// }if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO1, &RxHeader, data_buf) != HAL_OK){while(1);}data_len = DLCtoBytes[RxHeader.DataLength];// printf("Identifier = 0x%x\r\n", RxHeader.Identifier);// printf("DataLength = %d(0x%x)\r\n", data_len, RxHeader.DataLength);// printf("FDFormat = 0x%x\r\n", RxHeader.FDFormat);// printf("BitRateSwitch = %d\r\n", RxHeader.BitRateSwitch);// printf("ErrorStateIndicator = 0x%x\r\n", RxHeader.ErrorStateIndicator);// for(int i = 0; i < data_len; i++)// {// printf("Data[%d] = 0x%x\r\n", i, can1_rxbuf[i]);// }if(hfdcan->Instance == FDCAN1){fdcan1_control_rx_packet(RxHeader.Identifier, data_buf, data_len);}else if(hfdcan->Instance == FDCAN2){fdcan2_control_rx_packet(RxHeader.Identifier, data_buf, data_len);}}/* Retrieve Rx messages from RX FIFO1 */// status = HAL_FDCAN_GetRxMessage(&hfdcan, FDCAN_RX_FIFO1, &RxHeader, RxData);// if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO1, &RxHeader, data_buf) != HAL_OK)// {// while(1);// }}if((RxFifo1ITs & FDCAN_IT_RX_FIFO1_MESSAGE_LOST) != RESET){EASY_LOG_DBG("Rx FIFO1 message lost\r\n");}if((RxFifo1ITs & FDCAN_IT_RX_FIFO1_FULL) != RESET){EASY_LOG_DBG("Rx FIFO1 message full\r\n");}
}int fdcan_control_init(void)
{EASY_LOG_DBG("fdcan_control_init\n");MX_FDCAN1_Init();MX_FDCAN2_Init();return 0;
}