1.FDCAN
FDCAN为CAN的升级版,它拥有更高的速度由1Mbps上升到5Mbps,每帧数据段最大长度由8字节上升到64字节,同时最重要的是它向下兼容CAN。不过在FDCAN配置普通CAN通信与大家熟知的STMF1、F4等配置CAN不太一样。因此在此记录一下,在STM32CubeMX下配置FDCAN作为普通模式的CAN使用并通信。直接进入正题。
2.STM32CubeMX配置
基于STM32G431CBT6芯片,具有170MHz的高频,也有FDCAN接口。首先常规配置时钟,达到最高170MHz。
然后打开FDCAN配置窗口,配置中断和时间报段。选择1/8/8原因是170MHz高频下170MHz/1/(8+8+1),可以达到1Mbps速率。
主要配置这些部分,Data Sync Jump Width、Data Time Seg1和Data Time Seg2默认不需要配置,因为这是FDCAN特有的功能,我们只使用传统CAN模式。同时记得在NVIC Settings上勾选上中断配置。
配置完成就生成程序框架,下面我贴上CAN的代码,需要注意生成的代码只是框架,目前FDCAN还不能正常作为CAN使用。需要加入过滤器代码以及CAN发送接收等部分代码才能正常使用
#include "fdcan.h"
/* USER CODE BEGIN 0 */
FDCAN_RxHeaderTypeDef fdcan1_RxHeader;
FDCAN_TxHeaderTypeDef fdcan1_TxHeader;
FDCAN_HandleTypeDef hfdcan1;
/* USER CODE END 0 */
FDCAN_HandleTypeDef hfdcan1;
/* FDCAN1 init function */
void MX_FDCAN1_Init(void)
{
/* USER CODE BEGIN FDCAN1_Init 0 */
FDCAN_FilterTypeDef FDCAN1_RXFilter;
/* 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 = DISABLE;
hfdcan1.Init.TransmitPause = DISABLE;
hfdcan1.Init.ProtocolException = DISABLE;
hfdcan1.Init.NominalPrescaler = 10;
hfdcan1.Init.NominalSyncJumpWidth = 1;
hfdcan1.Init.NominalTimeSeg1 = 8;
hfdcan1.Init.NominalTimeSeg2 = 8;
hfdcan1.Init.DataPrescaler = 1;
hfdcan1.Init.DataSyncJumpWidth = 1;
hfdcan1.Init.DataTimeSeg1 = 1;
hfdcan1.Init.DataTimeSeg2 = 1;
hfdcan1.Init.StdFiltersNbr = 0;
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 */
FDCAN1_RXFilter.IdType=FDCAN_STANDARD_ID; //标准ID
FDCAN1_RXFilter.FilterIndex=0; //滤波器索引
FDCAN1_RXFilter.FilterType=FDCAN_FILTER_RANGE; //滤波器类型
FDCAN1_RXFilter.FilterConfig=FDCAN_FILTER_TO_RXFIFO0; //过滤器0关联到FIFO0
FDCAN1_RXFilter.FilterID1=0x0000; //32位ID
FDCAN1_RXFilter.FilterID2=0x0000; //如果FDCAN配置为传统模式的话,这里是32位掩码
if(HAL_FDCAN_ConfigFilter(&hfdcan1,&FDCAN1_RXFilter)!=HAL_OK) //滤波器初始化
{
Error_Handler();
}
HAL_FDCAN_Start(&hfdcan1); //开启FDCAN
HAL_FDCAN_ActivateNotification(&hfdcan1,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);
/* USER CODE END FDCAN1_Init 2 */
}
void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* fdcanHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
if(fdcanHandle->Instance==FDCAN1)
{
/* USER CODE BEGIN FDCAN1_MspInit 0 */
/* USER CODE END FDCAN1_MspInit 0 */
/** Initializes the peripherals clocks
*/
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
/* FDCAN1 clock enable */
__HAL_RCC_FDCAN_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**FDCAN1 GPIO Configuration
PA11 ------> FDCAN1_RX
PA12 ------> FDCAN1_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* FDCAN1 interrupt Init */
HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn);
/* USER CODE BEGIN FDCAN1_MspInit 1 */
/* USER CODE END FDCAN1_MspInit 1 */
}
}
void HAL_FDCAN_MspDeInit(FDCAN_HandleTypeDef* fdcanHandle)
{
if(fdcanHandle->Instance==FDCAN1)
{
/* USER CODE BEGIN FDCAN1_MspDeInit 0 */
/* USER CODE END FDCAN1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_FDCAN_CLK_DISABLE();
/**FDCAN1 GPIO Configuration
PA11 ------> FDCAN1_RX
PA12 ------> FDCAN1_TX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);
/* FDCAN1 interrupt Deinit */
HAL_NVIC_DisableIRQ(FDCAN1_IT0_IRQn);
/* USER CODE BEGIN FDCAN1_MspDeInit 1 */
/* USER CODE END FDCAN1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8),可设置为FDCAN_DLC_BYTES_2~FDCAN_DLC_BYTES_8
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
uint8_t FDCAN1_Send_Msg(uint8_t* msg)
{
fdcan1_TxHeader.Identifier = 0x001;
fdcan1_TxHeader.IdType = FDCAN_STANDARD_ID;
fdcan1_TxHeader.TxFrameType = FDCAN_DATA_FRAME;
fdcan1_TxHeader.DataLength = FDCAN_DLC_BYTES_8;
fdcan1_TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
fdcan1_TxHeader.BitRateSwitch = FDCAN_BRS_OFF;
fdcan1_TxHeader.FDFormat = FDCAN_CLASSIC_CAN;
fdcan1_TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
fdcan1_TxHeader.MessageMarker = 0x52; //由于网上借鉴该函数,我也不太明白为什么是0x52,不过我测试改成0好像也没问题
if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1,&fdcan1_TxHeader,msg)!=HAL_OK) return 1;//发送
return 0;
}
//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
//其他,接收的数据长度;
uint8_t FDCAN1_Receive_Msg(uint8_t *buf, uint16_t *Identifier,uint16_t *len)
{
if(HAL_FDCAN_GetRxMessage(&hfdcan1,FDCAN_RX_FIFO0,&fdcan1_RxHeader,buf)!=HAL_OK)return 0;//接收数据
*Identifier = fdcan1_RxHeader.Identifier;
*len=fdcan1_RxHeader.DataLength>>16;
return fdcan1_RxHeader.DataLength>>16;
}
/* USER CODE END 1 */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file fdcan.h
* @brief This file contains all the function prototypes for
* the fdcan.c file
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __FDCAN_H__
#define __FDCAN_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern FDCAN_HandleTypeDef hfdcan1;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_FDCAN1_Init(void);
/* USER CODE BEGIN Prototypes */
uint8_t FDCAN1_Send_Msg(uint8_t* msg);
uint8_t FDCAN1_Receive_Msg(uint8_t *buf, uint16_t *Identifier,uint16_t *len);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __FDCAN_H__ */
从以上代码可以看出FDCAN部分要增加过滤器配置代码,CubeMX并不会帮你自动生成这部分代码,这些发送接收函数都是网上借鉴的,其实都大差不差。需要注意如果想选定只能收到特定ID的信息,那么FDCAN上过滤器的FilterID1和FilterID2要进行更改,而且我印象单单更改这两个值还不可以,还需要配置其他REJET东西,关于这点可以自行网上了解。可以参考这篇文章:(19条消息) STM32H7 FDCAN id过滤问题_fdcan_filter_mask_swheryoo的博客-CSDN博客
如果想通过FDCAN中断处理,可以在stm32g4xx_it.c文件中找到FDCAN1_IT0_IRQHandler该函数,在里面进行自己想要的中断处理。
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 */
}
自此,FDCAN就可以作为传统的CAN进行通信了,亲测有效。