1、RT1060-CAN FD功能简介
这里使用RT1060系列的1064芯片进行开发,测试板是官方提供的开发板;RT1060系列支持3路CAN功能,CAN1和CAN2只能最为普通的CAN外设,支持CAN2.0,而CAN3支持CAN-FD功能;CAN-FD功能这里就不做详细介绍,百度上很多该方面资料,核心点是普通CAN只能传输8字节数据,而CAN-FD最大支持传输64字节数据。
注意事项:使用CANFD功能的时候,用于测试的CAN工具需要支持CANFD功能。
2、各类宏定义和全局变量
#define EXAMPLE_CAN CAN3 //使用的CAN外设 - CAN3
#define RX_MESSAGE_BUFFER_NUM (10) //接收邮箱
#define TX_MESSAGE_BUFFER_NUM (9) //发送邮箱
#define EXAMPLE_CAN_CLK_SOURCE (kFLEXCAN_ClkSrc1) //CAN时钟
/* 选择60M时钟除以USB1 PLL (480mhz)作为flexcan主时钟源 */
#define FLEXCAN_CLOCK_SOURCE_SELECT (0U)
/* Clock divider for master flexcan clock source */
#define FLEXCAN_CLOCK_SOURCE_DIVIDER (2U)
#define DLC (15)
#define BYTES_IN_MB kFLEXCAN_64BperMB //每个消息区选择64字节数据
#define EXAMPLE_CAN_CLK_FREQ ((CLOCK_GetFreq(kCLOCK_Usb1PllClk) / 8) / (FLEXCAN_CLOCK_SOURCE_DIVIDER + 1U)) //时钟频率
flexcan_handle_t flexcanHandle;
volatile bool txComplete = false; //发送完成标志
volatile bool rxComplete = false; //接收完成标志
volatile bool wakenUp = false; //唤醒标识
flexcan_mb_transfer_t txXfer, rxXfer; //邮箱
flexcan_fd_frame_t frame;
uint32_t txIdentifier; //收发ID标识符
uint32_t rxIdentifier;
uint16_t t_Count = 0; //用于测试
3、CAN FD IO初始化
static void can2_gpio_config(void)
{
//设置GPIO的时钟
CLOCK_EnableClock(kCLOCK_Iomuxc);
//配置IO的复用模式 设置为CAN3的TX和RX功能
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_14_FLEXCAN3_TX, 1U);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_15_FLEXCAN3_RX, 1U);
//配置GPIO功能
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_14_FLEXCAN3_TX, 0x10B0U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_15_FLEXCAN3_RX, 0x10B0U);
}
4、CAN FD功能配置
void can2_function_config(void)
{
flexcan_config_t flexcanConfig; //CAN配置
flexcan_rx_mb_config_t mbConfig; //接收邮箱配置
flexcan_timing_config_t timing_config; //时间配置
/* FLEXCAN时钟配置 */
CLOCK_SetMux(kCLOCK_CanMux, FLEXCAN_CLOCK_SOURCE_SELECT);
CLOCK_SetDiv(kCLOCK_CanDiv, FLEXCAN_CLOCK_SOURCE_DIVIDER);
/* 设置发送和接收的帧ID */
txIdentifier = 0x321;
rxIdentifier = 0x123;
/* Get FlexCAN module default Configuration. */
/*
* flexcanConfig.clkSrc = kFLEXCAN_ClkSrc0;
* flexcanConfig.bitRate = 1000000U;
* flexcanConfig.bitRateFD = 2000000U;
* flexcanConfig.maxMbNum = 16;
* flexcanConfig.enableLoopBack = false;
* flexcanConfig.enableSelfWakeup = false;
* flexcanConfig.enableIndividMask = false;
* flexcanConfig.disableSelfReception = false;
* flexcanConfig.enableListenOnlyMode = false;
* flexcanConfig.enableDoze = false;
*/
FLEXCAN_GetDefaultConfig(&flexcanConfig);
flexcanConfig.clkSrc = EXAMPLE_CAN_CLK_SOURCE;
flexcanConfig.bitRate = 500000U; //这里使用的是**bitRate**,bit位的波特率
memset(&timing_config, 0, sizeof(flexcan_timing_config_t));
if (FLEXCAN_FDCalculateImprovedTimingValues(EXAMPLE_CAN, flexcanConfig.bitRate, flexcanConfig.bitRateFD,
EXAMPLE_CAN_CLK_FREQ, &timing_config))
{
/* 更新修改后的定时配置 */
memcpy(&(flexcanConfig.timingConfig), &timing_config, sizeof(flexcan_timing_config_t));
}
FLEXCAN_FDInit(EXAMPLE_CAN, &flexcanConfig, EXAMPLE_CAN_CLK_FREQ, BYTES_IN_MB, true);
/* 创建CAN句柄 并建立回调函数 */
FLEXCAN_TransferCreateHandle(EXAMPLE_CAN, &flexcanHandle, flexcan_callback, NULL);
/* 设置接收屏蔽机制 后面来仔细研究一下该屏蔽机制 */
FLEXCAN_SetRxMbGlobalMask(EXAMPLE_CAN, FLEXCAN_RX_MB_STD_MASK(rxIdentifier, 0, 0));
/* 设置接收消息缓存 */
mbConfig.format = kFLEXCAN_FrameFormatStandard; //标准帧
mbConfig.type = kFLEXCAN_FrameTypeData; //数据帧
mbConfig.id = FLEXCAN_ID_STD(rxIdentifier); //设置帧ID
FLEXCAN_SetFDRxMbConfig(EXAMPLE_CAN, RX_MESSAGE_BUFFER_NUM, &mbConfig, true);
/* 设置发送消息缓存 */
FLEXCAN_SetFDTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);
}
流程与基础CAN功能类似,在波特率配置的时候使用的是bitRate
,这里需要注意以下;
CANFD采用了双波特率形式:标准波特率(也称仲裁域波特率)和数据域波特率,所以帧结构中不同段采用的波特率也不同。
配置的接收是轮询接收,中断接收后面再测试一下,配合官方SDK的例程探索,难度不大。
这里还配置了一个回调函数,用于判断接收和发送是否完成。
5、CAN FD收发
//发送数据
void can2_send_data(void)
{
frame.id = FLEXCAN_ID_STD(txIdentifier);
frame.format = (uint8_t)kFLEXCAN_FrameFormatStandard;
frame.type = (uint8_t)kFLEXCAN_FrameTypeData;
frame.length = (uint8_t)DLC;
frame.brs = 1U; //灵活的数据率格式与比特率开关
frame.edl = 1U; //灵活的数据速率格式扩展数据长度
frame.dataWord[0] = 0x12345678;
frame.dataWord[2] = 0x12345678;
frame.dataWord[15] = t_Count;
txXfer.mbIdx = (uint8_t)TX_MESSAGE_BUFFER_NUM; //用于传输消息的消息缓冲区的索引。
txXfer.framefd = &frame;
(void)FLEXCAN_TransferFDSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);
//FLEXCAN_WriteFDTxMb(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, &frame);
}
/*!
* @brief FlexCAN Call Back function
*/
static FLEXCAN_CALLBACK(flexcan_callback)
{
switch (status)
{
case kStatus_FLEXCAN_RxIdle:
if (RX_MESSAGE_BUFFER_NUM == result)
{
rxComplete = true;
}
break;
case kStatus_FLEXCAN_TxIdle:
if (TX_MESSAGE_BUFFER_NUM == result)
{
txComplete = true;
}
break;
case kStatus_FLEXCAN_WakeUp:
wakenUp = true;
break;
default:
break;
}
}
//轮询接收
void can2_receive_data(void)
{
rxXfer.mbIdx = (uint8_t)RX_MESSAGE_BUFFER_NUM;
rxXfer.framefd = &frame;
//无阻塞接收 利用回调函数进行接收数据
(void)FLEXCAN_TransferFDReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);
if (rxComplete == true)
{
t_Count++;
rxComplete = false;
}
}
这里的接收和发送有一个联动的功能,就是CAN FD接收到正确的帧ID后,会将t_Count++;
,然后再发送的地方frame.dataWord[15] = t_Count;
这样就可以检测这个CAN FD的发送后接收是否都正确。
另外需要注意的是,使用CAN FD一定需要将flexcan_fd_frame_t 中的brs 、edl
置1;
6、涉及的函数及标志位解释
6.1 FLEXCAN_FDCalculateImprovedTimingValues()函数
根据CAN FD的特定比特率计算改进的定时值。
该函数用于根据给定的标称相位比特率和数据相位比特率计算CAN FD定时值。计算时间值将在CBT/ENCBT和FDCBT/EDCBT寄存器中设置。计算基于CiA 1301 v1.0.0文档的建议。
参数bitRate
:CAN FD标称相位速度,单位为bps,由用户定义。
参数bitRateFD
:CAN FD数据相位速度(以bps为单位)由用户定义。等于bitRate表示禁用比特率切换。
参数sourceClock_Hz
:时钟源频率,单位为Hz。
参数pTimingConfig
:指向FlexCAN定时配置结构的指针。
6.2 flexcan_timing_config_t 结构体
FlexCAN协议定时特性的配置结构。
typedef struct _flexcan_timing_config
{
uint16_t preDivider; /*!< 经典CAN或CAN FD标称相位比特率预分频器。 */
uint8_t rJumpwidth; /*!< 经典CAN或CAN FD标称相位重同步跳变宽度。 */
uint8_t phaseSeg1; /*!< 经典CAN或CAN FD标称相位段1。 */
uint8_t phaseSeg2; /*!< 经典CAN或CAN FD标称相位段2。 */
uint8_t propSeg; /*!< 经典CAN或CAN FD标称相位传播段。 */
#if (defined(FSL_FEATURE_FLEXCAN_HAS_FLEXIBLE_DATA_RATE) && FSL_FEATURE_FLEXCAN_HAS_FLEXIBLE_DATA_RATE)
uint16_t fpreDivider; /*!< CAN FD数据相位比特率预分频器。*/
uint8_t frJumpwidth; /*!< CAN FD数据相位重同步跳变宽度。 */
uint8_t fphaseSeg1; /*!< CAN FD数据相位相位段1 */
uint8_t fphaseSeg2; /*!< CAN FD数据相位相位段2 */
uint8_t fpropSeg; /*!< CAN FD数据相位传播段。*/
#endif
} flexcan_timing_config_t;
使用CAN FD时,仲裁域波特率对应:preDivider、rJumpwidth、phaseSeg1、phaseSeg2、propSeg
,数据域波特率对应fpreDivider、frJumpwidth、fphaseSeg1、fphaseSeg2、fpropSeg
。