stm32mp15x之M4使用canfd
- 软件开发
- 2025-09-08 23:45:02

目录 序配置添加注坑参考 序
在使用 stm32mp15x 系列时,M4 有不少的坑,这里简单聊聊使用 canfd 时遇到的一些问题。
配置这里使用 PLL4R 为 100M,用于 CANFD 的时钟
canfd 速率配置成 1M ,5M,其中数据传输速率为 5M。
接收采用 RxFifo0,发送采用 FIFO 模式,发送和接收数据长度都配置成 64字节,tx、rx 深度都配置成 15。
配置 FDCAN2 中断 0 就可以。
配置生成如下:
void MX_FDCAN2_Init(void) { hfdcan2.Instance = FDCAN2; hfdcan2.Init.FrameFormat = FDCAN_FRAME_FD_BRS; hfdcan2.Init.Mode = FDCAN_MODE_NORMAL; hfdcan2.Init.AutoRetransmission = ENABLE; hfdcan2.Init.TransmitPause = DISABLE; hfdcan2.Init.ProtocolException = DISABLE; hfdcan2.Init.NominalPrescaler = 2; hfdcan2.Init.NominalSyncJumpWidth = 16; hfdcan2.Init.NominalTimeSeg1 = 33; hfdcan2.Init.NominalTimeSeg2 = 16; hfdcan2.Init.DataPrescaler = 1; hfdcan2.Init.DataSyncJumpWidth = 4; hfdcan2.Init.DataTimeSeg1 = 15; hfdcan2.Init.DataTimeSeg2 = 4; hfdcan2.Init.MessageRAMOffset = 0; hfdcan2.Init.StdFiltersNbr = 1; hfdcan2.Init.ExtFiltersNbr = 0; hfdcan2.Init.RxFifo0ElmtsNbr = 15; hfdcan2.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_64; hfdcan2.Init.RxFifo1ElmtsNbr = 0; hfdcan2.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_8; hfdcan2.Init.RxBuffersNbr = 0; hfdcan2.Init.RxBufferSize = FDCAN_DATA_BYTES_8; hfdcan2.Init.TxEventsNbr = 0; hfdcan2.Init.TxBuffersNbr = 0; hfdcan2.Init.TxFifoQueueElmtsNbr = 15; hfdcan2.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; hfdcan2.Init.TxElmtSize = FDCAN_DATA_BYTES_64; if (HAL_FDCAN_Init(&hfdcan2) != HAL_OK) { Error_Handler(); } } void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* fdcanHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; if(fdcanHandle->Instance==FDCAN2) { /* USER CODE BEGIN FDCAN2_MspInit 0 */ /* USER CODE END FDCAN2_MspInit 0 */ if(IS_ENGINEERING_BOOT_MODE()) { /** Initializes the peripherals clock */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN; PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL4_R; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /* FDCAN2 clock enable */ __HAL_RCC_FDCAN_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /**FDCAN2 GPIO Configuration PB12 ------> FDCAN2_RX PB13 ------> FDCAN2_TX */ GPIO_InitStruct.Pin = GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN2; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN2; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* FDCAN2 interrupt Init */ HAL_NVIC_SetPriority(FDCAN2_IT0_IRQn, 1, 0); HAL_NVIC_EnableIRQ(FDCAN2_IT0_IRQn); /* USER CODE BEGIN FDCAN2_MspInit 1 */ /* USER CODE END FDCAN2_MspInit 1 */ } } void HAL_FDCAN_MspDeInit(FDCAN_HandleTypeDef* fdcanHandle) { if(fdcanHandle->Instance==FDCAN2) { /* USER CODE BEGIN FDCAN2_MspDeInit 0 */ /* USER CODE END FDCAN2_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_FDCAN_CLK_DISABLE(); /**FDCAN2 GPIO Configuration PB12 ------> FDCAN2_RX PB13 ------> FDCAN2_TX */ HAL_GPIO_DeInit(GPIOB, GPIO_PIN_12|GPIO_PIN_13); /* FDCAN2 interrupt Deinit */ HAL_NVIC_DisableIRQ(FDCAN2_IT0_IRQn); /* USER CODE BEGIN FDCAN2_MspDeInit 1 */ /* USER CODE END FDCAN2_MspDeInit 1 */ } } 添加配置接收过滤与中断:
过滤主要有 4 种, FDCAN_FILTER_RANGE、FDCAN_FILTER_MASK、FDCAN_FILTER_DUAL、FDCAN_FILTER_RANGE_NO_EIDM,这里使用 FDCAN_FILTER_MASK 过滤
FDCAN_FilterTypeDef sFilterConfig; sFilterConfig.IdType = FDCAN_STANDARD_ID; sFilterConfig.FilterIndex = 0; sFilterConfig.FilterType = FDCAN_FILTER_MASK; sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; sFilterConfig.FilterID1 = 0x111; sFilterConfig.FilterID2 = 0x7F0; // mask 为1部分需完全匹配 HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig); HAL_FDCAN_ConfigGlobalFilter(&hfdcan2,FDCAN_REJECT, DISABLE, DISABLE, DISABLE); //设置被滤除掉的消息的处理方式 HAL_FDCAN_ConfigRxFifoOverwrite(&hfdcan2, FDCAN_RX_FIFO0, FDCAN_RX_FIFO_BLOCKING); HAL_FDCAN_ActivateNotification(&hfdcan2,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0); /* set the wartermark of Rx FIFO0 to 1 */ HAL_FDCAN_ConfigFifoWatermark(&hfdcan2, FDCAN_CFG_RX_FIFO0, 1); // /* Enable wartermark interrupts of Rx FIFO0 */ HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_RX_FIFO0_WATERMARK, 0); HAL_FDCAN_Start(&hfdcan2);中断接收
中断接收采用 FDCAN_IT_RX_FIFO0_NEW_MESSAGE 接收,需要同时打开 FDCAN_IT_RX_FIFO0_WATERMARK
struct canfd_frame{ uint16_t can_id; uint16_t dlc; uint8_t data[64]; }; void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) { if(RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE){ struct canfd_frame* buff; HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &rxHeader, buff->data); buff->can_id = rxHeader.Identifier; buff->dlc = rxHeader.DataLength >> 16; } }发送
发送需要需要注意 BitRateSwitch 需要为 FDCAN_BRS_OFF, DataLength 为高 16 位
static FDCAN_TxHeaderTypeDef txHeader = {0}; txHeader.IdType = FDCAN_STANDARD_ID; txHeader.TxFrameType = FDCAN_DATA_FRAME; txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; txHeader.BitRateSwitch = FDCAN_BRS_OFF; txHeader.FDFormat = FDCAN_FD_CAN; txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS; txHeader.MessageMarker = 0; txHeader.Identifier = buff->can_id; txHeader.DataLength = buff->dlc << 16; memcpy(txData, buff->data, sizeof(txData)); while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan2) == 0){ if(hfdcan2.Instance->TXFQS & FDCAN_TXFQS_TFQF){ HAL_FDCAN_DeInit(&hfdcan2); MX_FDCAN2_Init(); //这里重新初始化 can } } HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan2, &txHeader, txData);错误中断
错误中断需要开启,用于错误检测与复位 can 通信,在使用中存在因为 can 产生错误,而后不进入中断问题,此时,需要在错误后,复位 can,让其重新配置。
可以在配置时,打开这些错误中断,错误状态中,检测这3个 FDCAN_IR_EP 、FDCAN_IR_EW 、FDCAN_IR_BO 即可;错误回调中检测 FDCAN_IR_PED,其他的暂未发现问题。
HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_BUS_OFF | FDCAN_IT_ERROR_WARNING | FDCAN_IT_ERROR_PASSIVE | FDCAN_IT_RESERVED_ADDRESS_ACCESS | FDCAN_IT_DATA_PROTOCOL_ERROR | FDCAN_IT_ARB_PROTOCOL_ERROR | FDCAN_IT_ERROR_LOGGING_OVERFLOW | FDCAN_IT_RAM_ACCESS_FAILURE, 0); void HAL_FDCAN_ErrorCallback(FDCAN_HandleTypeDef *hfdcan) { if(hfdcan2.ErrorCode & FDCAN_IR_PED){ resetCan(); } } void HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t ErrorStatusITs) { if(ErrorStatusITs & (FDCAN_IR_EP | FDCAN_IR_EW | FDCAN_IR_BO)){ resetCan(); } } 注 stm32mp15x 的 DLC 和其他的 st 芯片不一样,在高 16位,使用时注意左右移。 #define FDCAN_DLC_BYTES_0 ((uint32_t)0x00000000U) /*!< 0 bytes data field */ #define FDCAN_DLC_BYTES_1 ((uint32_t)0x00010000U) /*!< 1 bytes data field */ #define FDCAN_DLC_BYTES_2 ((uint32_t)0x00020000U) /*!< 2 bytes data field */ #define FDCAN_DLC_BYTES_3 ((uint32_t)0x00030000U) /*!< 3 bytes data field */ #define FDCAN_DLC_BYTES_4 ((uint32_t)0x00040000U) /*!< 4 bytes data field */ #define FDCAN_DLC_BYTES_5 ((uint32_t)0x00050000U) /*!< 5 bytes data field */ #define FDCAN_DLC_BYTES_6 ((uint32_t)0x00060000U) /*!< 6 bytes data field */ #define FDCAN_DLC_BYTES_7 ((uint32_t)0x00070000U) /*!< 7 bytes data field */ #define FDCAN_DLC_BYTES_8 ((uint32_t)0x00080000U) /*!< 8 bytes data field */ #define FDCAN_DLC_BYTES_12 ((uint32_t)0x00090000U) /*!< 12 bytes data field */ #define FDCAN_DLC_BYTES_16 ((uint32_t)0x000A0000U) /*!< 16 bytes data field */ #define FDCAN_DLC_BYTES_20 ((uint32_t)0x000B0000U) /*!< 20 bytes data field */ #define FDCAN_DLC_BYTES_24 ((uint32_t)0x000C0000U) /*!< 24 bytes data field */ #define FDCAN_DLC_BYTES_32 ((uint32_t)0x000D0000U) /*!< 32 bytes data field */ #define FDCAN_DLC_BYTES_48 ((uint32_t)0x000E0000U) /*!< 48 bytes data field */ #define FDCAN_DLC_BYTES_64 ((uint32_t)0x000F0000U) /*!< 64 bytes data field */ 短路 can 芯片的 H 和 L 两个差分脚,会触发FDCAN_TXFQS_TFQF,此时会无法正常发送,需要重新初始化 canfd 坑 在 linux 下用时,需要手动打开 PLL4R __HAL_RCC_PLL4CLKOUT_ENABLE(RCC_PLL4_DIVR); 发送时,BitRateSwitch 需要配置成 FDCAN_BRS_OFF,不然无法发送 txHeader.BitRateSwitch = FDCAN_BRS_OFF; 使用 FDCAN_IT_RX_FIFO0_NEW_MESSAGE 中断时,需要将 FDCAN_IT_RX_FIFO0_WATERMARK 中断也打开,不然只能中断一次在线调试时,需要在板子中运行没有中断的程序,然后再使用调试器,下载有中断的程序,不然程序调试有问题;且只能调试一次,需要重新上下电才能正常,就是说,产生中断后,程序就不能进行中断调试了 参考github /STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H743I-EVAL/Examples/FDCAN/FDCAN_Com_IT/Src/main.c github /STMicroelectronics/STM32CubeMP1/blob/master/Projects/STM32MP157C-EV1/Examples/FDCAN/FDCAN_Loopback/Src/main.c club.rt-thread.org/ask/article/f354701c18db97db.html
stm32mp15x之M4使用canfd由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“stm32mp15x之M4使用canfd”