你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【stm32U3测评】使用FDCAN进行IAP升级----【2】FDCAN的Classic模式通信

[复制链接]
短笛君 发布时间:2026-6-21 22:33

前言

我们使用前一章节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中添加循环发送代码即可正常通信

image.png

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

收藏 评论1 发布时间:2026-6-21 22:33

举报

1个回答
STM1024 回答时间:昨天 22:25

2026-06-23_223019.jpg

巧了,我也正在做一个IAP的设计,估计完成一半了

包括上位机+Bootloader Firmware

不过,以我拖延症的尿性,估计要到这个周末了。

如果打磨完善,那就不知道是啥时候了。

所属标签

相似分享

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版