前言
我们使用前一章节Bootloader中 APP工程 来生成FDCAN的初始化代码 本次先使用CAN2.0即FDCAN中的Classic模式
关于FDCAN和CAN对比可以参考一下表格
| 特性 |
经典CAN (Classic CAN) |
FDCAN (CAN FD) |
| 最大数据传输速率 |
最高1M |
数据段最高可达8 Mbps 甚至更高 |
| 单帧最大数据长度 |
8 字节 |
64 字节 |
| 速率可变 |
不支持,全帧固定速率 |
支持 ,仲裁段低速(保证兼容),数据段高速 |
| 错误校验 (CRC) |
基础,15位 CRC校验 |
增强,最高21位 CRC校验,可靠性更高 |
| 兼容性 |
无法解析FDCAN帧 |
向下兼容 经典CAN,可配置为传统模式与其通信 |
| 典型应用场景 |
低速控制指令(如车窗、简单传感器) |
高速大数据传输(如OTA升级 、自动驾驶数据流 ) |
使用FDCAN可以实现更快速的IAP过程,但是配置更加复杂 所以先使用CAN通信实现基本功能 然后在CAN的基础之上升级为FDCAN 整体框架逻辑是不变的 只有接收数据的部分会做一点相应改动
工程可以直接复制先前APP部分的工程 然后再初始化CAN部分
CAN部分外设配置如下
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_DIV1;
hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC;
hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
hfdcan1.Init.AutoRetransmission = ENABLE;
hfdcan1.Init.TransmitPause = DISABLE;
hfdcan1.Init.ProtocolException = ENABLE;
hfdcan1.Init.NominalPrescaler = 4;
hfdcan1.Init.NominalSyncJumpWidth = 4;
hfdcan1.Init.NominalTimeSeg1 = 19;
hfdcan1.Init.NominalTimeSeg2 = 4;
hfdcan1.Init.DataPrescaler = 4;
hfdcan1.Init.DataSyncJumpWidth = 4;
hfdcan1.Init.DataTimeSeg1 = 19;
hfdcan1.Init.DataTimeSeg2 = 4;
hfdcan1.Init.StdFiltersNbr = 1;
hfdcan1.Init.ExtFiltersNbr = 0;
hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN FDCAN1_Init 2 */
/* USER CODE END FDCAN1_Init 2 */
}
void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* fdcanHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(fdcanHandle->Instance==FDCAN1)
{
/* USER CODE BEGIN FDCAN1_MspInit 0 */
/* USER CODE END FDCAN1_MspInit 0 */
/* FDCAN1 clock enable */
__HAL_RCC_FDCAN_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**FDCAN1 GPIO Configuration
PB8 ------> FDCAN1_RX
PB9 ------> FDCAN1_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* FDCAN1 interrupt Init */
HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn);
/* USER CODE BEGIN FDCAN1_MspInit 1 */
/* USER CODE END FDCAN1_MspInit 1 */
}
}
Nucleo板子CAN引脚配置为PB8和PB9 除此之外还要配置STBY引脚拉低让收发器退出休眠模式
除了外设之外 滤波器也需要配置 不然会出现无法收到消息的情况
HAL_StatusTypeDef BSP_CAN_Init(void)
{
FDCAN_FilterTypeDef sFilterConfig = {0};
MX_FDCAN1_Init();
/* 接收过滤器设置为接收全部标准帧,由 Bootloader/APP 上层按 ID 再过滤。 */
sFilterConfig.IdType = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex = 0;
sFilterConfig.FilterType = FDCAN_FILTER_MASK;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x000U;
sFilterConfig.FilterID2 = 0x000U;
if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
{
return HAL_ERROR;
}
if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
FDCAN_ACCEPT_IN_RX_FIFO0,
FDCAN_REJECT,
FDCAN_REJECT_REMOTE,
FDCAN_REJECT_REMOTE) != HAL_OK)
{
return HAL_ERROR;
}
if (HAL_FDCAN_ActivateNotification(&hfdcan1,
FDCAN_IT_RX_FIFO0_NEW_MESSAGE |
FDCAN_IT_RX_FIFO0_FULL |
FDCAN_IT_RX_FIFO0_MESSAGE_LOST |
FDCAN_IT_ERROR_WARNING |
FDCAN_IT_ERROR_PASSIVE |
FDCAN_IT_BUS_OFF |
FDCAN_IT_ARB_PROTOCOL_ERROR |
FDCAN_IT_DATA_PROTOCOL_ERROR,
0U) != HAL_OK)
{
return HAL_ERROR;
}
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
{
return HAL_ERROR;
}
printf("[CAN] Classic CAN 1Mbps, RX ID=0x%03lX, TX ID=0x%03lX\r\n",
(unsigned long)IAP_CAN_HOST_TO_BOOT_ID,
(unsigned long)IAP_CAN_BOOT_TO_HOST_ID);
return HAL_OK;
}
完成以上部分 就可以在main中添加滤波器初始化代码 然后打开FDCAN外设
为了方便使用库函数 这里我们对FDCAN发送函数进行一次封装
HAL_StatusTypeDef BSP_CAN_Send(uint32_t std_id, const uint8_t *data, uint8_t len)
{
FDCAN_TxHeaderTypeDef txHeader = {0};
uint8_t txData[8] = {0};
if ((std_id > 0x7FFU) || (len > 8U))
{
return HAL_ERROR;
}
if ((len > 0U) && (data == NULL))
{
return HAL_ERROR;
}
if (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) == 0U)
{
return HAL_BUSY;
}
for (uint8_t i = 0U; i < len; i++)
{
txData[i] = data[i];
}
txHeader.Identifier = std_id;
txHeader.IdType = FDCAN_STANDARD_ID;
txHeader.TxFrameType = FDCAN_DATA_FRAME;
txHeader.DataLength = FDCAN_LenToDlc(len);
txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
txHeader.BitRateSwitch = FDCAN_BRS_OFF;
txHeader.FDFormat = FDCAN_CLASSIC_CAN;
txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
txHeader.MessageMarker = 0U;
return HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &txHeader, txData);
}
CAN不同于串口 收发是需要ACK帧来确认消息是否到达的 否则会报总线错误 所以在使用前我们需要接好CAN分析仪来调试
if ((HAL_GetTick() - LedLastTick) >= 500U)
{
LedLastTick = HAL_GetTick();
BSP_LED_Toggle(LED_RED); /* LD2: PE14 */
uint8_t data[8] = {0};
BSP_CAN_Send(0x200,data,8);
}
接下来只需要在MAIN中添加循环发送代码即可正常通信

完成这部分 就可以开始我们IAP的升级流程设计了