【S32K3 RTD LLD篇3 】K344 PIT CANFD 收发
小编最近学S32K344比较上头,微醺状态,感觉有点上瘾的那种,所以利用周末,总结下自己学习并动手做的一些东西,不见得好,就当一个笔记。
前面刚刚表达了下对于RTD400自带CAN demo的不满,并且也写了一个外部loopback 标准CAN的代码和文章:
【S32K3 RTD LLD篇2】K344 FlexCAN 外部loopback
这里,准备升级下,上CANFD,enhanced Rx FIFO接收,并且编写了个小功能,并且分享一些小编在实际中遇到的坑点,其实学习了一段时间,感觉这个汽车的芯片,单纯从技术上讲并不算难,就是技术内容点比较多,需要时间一点点去走一遭。
代码功能:开机PIT以2ms间隔定时触发并发送CANFD 64字节的数据帧10帧,每次定时到发送CAN数据的时候,同时也会翻转一个外部引脚,便于示波器查看,然后停止发送,并且进入接收状态,可以接收两种类型的标准ID帧,以及一种类型的扩展ID帧,收到之后果断回环出来。这里在收到ID回环的时候,同时也会去翻转板载的绿色小灯,如果接收到的ID帧并不是滤波表里面的,就不会接收数据并且翻转小灯。测试代码基于S32DS3.5, RTD4.0.0, 板子基于官方的S32K3X4EVB-T172,并且借助PEAK的PCAN-USB Pro去做测试。
同样声明下,此处篇幅有限,并不展开去讲整个FlexCAN的模块信息,因为看过很多的芯片参考手册,我认为这个汽车的芯片参考手册还是很值得夸赞的,写的清晰易懂,唯一要求就是不要偷懒,自己好好看,好好动手做。废话不多说,直接进入构建代码主题。
一,代码实现与配置
代码工程可以重新导入一个RTD4.0.0的can代码做,也可以直接基于之前做的外部loopbcak的代码去做。如果直接导入RTD的can demo,就得把上一篇文章里面提到的坑自己修复下。
1.1 引脚配置
除了添加的PTA6,PTA7用于CAN0_RX, CAN0_TX之外,添加了两个外部引脚,分别是PTA12,PTA30. PTA12用于标志2ms的定时翻转发送CANFD数据,PTA30是板载的绿色小灯引脚。
1.2 时钟配置
这里提到时钟配置,主要原因是需要使用告诉的CANFD,所以把CAN0的时钟源给提高到80MHZ,还有就是要注意下PIT使用的时钟源是什么,频率是多少,时钟配置如下:
1.3 外设配置
外设这里又添加了些模块,比如Pit, Siul2_Dio,然后flexCAN模块需要重新配置。
先说简单的Pit,就是一个定时器,配置如下:
实际上这个duration并没有真正使用,具体的定时时间是直接使用代码去控制的。
Siul2_Port在之前外部loop的基础上,又加了两个外部引脚:
对于这个MSCR,不知道号的,可以直接去S32K344RM附件的表格里面去查找。以CAN的PTA6,PTA7为例:
关于Siul2_Dio,模块加了,啥也不用配,默认就行。
到了最关键的部分,FlexCAN,配置如下:
到目前为止,所有的配置就完成了。IntCtrl_lp配不配置无所谓,因为会在代码里面调用API配置。
点击update code,重新生成代码。
1.4 代码添加
直接上整个main的代码:
/*!
** Copyright 2019 NXP
** @file main.c
** @brief
** Main module.
** This module contains user's application code.
*/
/*!
** @addtogroup main_module main module documentation
** @{
*/
/* MODULE main */
/* Including necessary configuration files. */
#include "Mcal.h"
#include "Clock_Ip.h"
#include "FlexCAN_Ip.h"
#include "IntCtrl_Ip.h"
#include "Siul2_Port_Ip.h"
#include "Siul2_Dio_Ip.h"
#include "Pit_Ip.h"
#define MSG_ID 20u
#define RX_MB_IDX 1U
#define TX_MB_IDX 4U
volatile int exit_code = 0;
/* User includes */
uint8 dummyData[8] = {1,2,3,4,5,6,8};
/* PIT instance used - 0 */
#define PIT_INST_0 0U
/* PIT Channel used - 0 */
#define CH_0 0U
//#define PIT_PERIOD 400000//10ms
#define PIT_PERIOD 80000 //2ms
//#define PIT_PERIOD 8000 //20us
#define LED_PIN 14u
#define LED_PORT PTA_H_HALF
#define TEST_PIN 12u
#define TEST_PORT PTA_L_HALF
static volatile uint8 toggleLed = 0U;
static volatile uint8 canfdrxflag = 0U;
/* User includes */
uint8 tx_loop[64] = {0X01, 0X02, 0X03, 0X04, 0X05, 0X06, 0X07, 0X08, 0X09, 0X0A, 0X0B, 0X0C, 0X0D, 0X0E, 0X0F, 0X10,
0X11, 0X12, 0X13, 0X14, 0X15, 0X16, 0X17, 0X18, 0X19, 0X1A, 0X1B, 0X1C, 0X1D, 0X1E, 0X1F, 0X20,
0X21, 0X22, 0X23, 0X24, 0X25, 0X26, 0X27, 0X28, 0X29, 0X2A, 0X2B, 0X2C, 0X2D, 0X2E, 0X2F, 0X30,
0X31, 0X32, 0X33, 0X34, 0X35, 0X36, 0X37, 0X38, 0X39, 0X3A, 0X3B, 0X3C, 0X3D, 0X3E, 0X3F, 0X40
};
uint8 tx_cnt = 0;
Flexcan_Ip_DataInfoType rx_info = {
.msg_id_type = FLEXCAN_MSG_ID_STD,
.data_length = 8u,
.is_polling = TRUE,
.is_remote = FALSE
};
Flexcan_Ip_DataInfoType tx_info = {
.msg_id_type = FLEXCAN_MSG_ID_STD,
.data_length = 64,
.is_polling = TRUE,
.is_remote = FALSE,
.fd_padding = 0,
.fd_enable = TRUE,
.enable_brs = TRUE,
};
const Flexcan_Ip_EnhancedIdTableType CAN0_EnhanceFIFO_IdFilterTable[3] =
{
/* Enhanced FIFO filter table element 0-2 */
{
.filterType = FLEXCAN_IP_ENHANCED_RX_FIFO_ONE_ID_FILTER, // Filter element with filter + mask scheme
.isExtendedFrame = true,
.id2 = 0xABCD, // EXT ID filter
.id1 = 0x1FFFFFFF , // EXT ID mask
.rtr2 = false, // RTR filter
.rtr1 = true, // RTR mask
},
{
.filterType = FLEXCAN_IP_ENHANCED_RX_FIFO_ONE_ID_FILTER,
.isExtendedFrame = false,
.id2 = 0x123, // STD ID filter
.id1 = 0x7FF , // STD ID mask
.rtr2 = false, // RTR filter
.rtr1 = true, // RTR mask
},
{
.filterType = FLEXCAN_IP_ENHANCED_RX_FIFO_ONE_ID_FILTER,
.isExtendedFrame = false,
.id2 = 0x456, // STD ID filter
.id1 = 0x7FF , // STD ID mask
.rtr2 = false, // RTR filter
.rtr1 = true, // RTR mask
}
};
Flexcan_Ip_MsgBuffType rxData, rxFifoData;
/*!
\brief The main function for the project.
\details The startup initialization sequence is the following:
* - startup asm routine
* - main()
*/
extern void CAN0_ORED_0_31_MB_IRQHandler(void);
void PIT_timer(void)
{
toggleLed = 1;
}
void flexcan0_Callback(uint8 instance, Flexcan_Ip_EventType eventType, uint32 buffIdx,
const Flexcan_Ip_StateType *flexcanState)
{
(void)flexcanState;
(void)instance;
switch(eventType)
{
case FLEXCAN_EVENT_RX_COMPLETE:
dummyData[0]++;
if(buffIdx==2) // MB2 received, std id
{
tx_info.msg_id_type = FLEXCAN_MSG_ID_STD; // send std ID message
FlexCAN_Ip_Send(INST_FLEXCAN_0, TX_MB_IDX, &tx_info, MSG_ID, (uint8 *)&dummyData);
FlexCAN_Ip_Receive(INST_FLEXCAN_0, 2, &rxData, false);
}
else if(buffIdx==1) // MB1 received, ext id
{
tx_info.msg_id_type = FLEXCAN_MSG_ID_EXT; // send ext ID message
FlexCAN_Ip_Send(INST_FLEXCAN_0, TX_MB_IDX, &tx_info, MSG_ID, (uint8 *)&dummyData);
FlexCAN_Ip_Receive(INST_FLEXCAN_0, 1, &rxData, false);
}
else {};
break;
case FLEXCAN_EVENT_RXFIFO_COMPLETE:
break;
case FLEXCAN_EVENT_TX_COMPLETE:
break;
case FLEXCAN_EVENT_ENHANCED_RXFIFO_COMPLETE:
canfdrxflag = 1;
// read rxFifoData[5]
//FlexCAN_Ip_RxFifo(INST_FLEXCAN_0, &rxFifoData);
break;
case FLEXCAN_EVENT_ENHANCED_RXFIFO_WATERMARK:
break;
default:
break;
}
}
int main(void)
{
/* Write your code here */
Clock_Ip_Init(&Clock_Ip_aClockConfig[0]);
Siul2_Port_Ip_Init(NUM_OF_CONFIGURED_PINS_PortContainer_0_BOARD_InitPeripherals, g_pin_mux_InitConfigArr_PortContainer_0_BOARD_InitPeripherals);
IntCtrl_Ip_EnableIrq(FlexCAN0_1_IRQn);
IntCtrl_Ip_InstallHandler(FlexCAN0_1_IRQn, CAN0_ORED_0_31_MB_IRQHandler, NULL_PTR);
/* set PIT 0 interrupt */
IntCtrl_Ip_Init(&IntCtrlConfig_0);
IntCtrl_Ip_EnableIrq(PIT0_IRQn);
/* Initialize PIT instance 0 - Channel 0 */
Pit_Ip_Init(PIT_INST_0, &PIT_0_InitConfig_PB);
/* Initialize channel 0 */
Pit_Ip_InitChannel(PIT_INST_0, PIT_0_CH_0);
/* Enable channel interrupt PIT_0 - CH_0 */
Pit_Ip_EnableChannelInterrupt(PIT_INST_0, CH_0);
/* Start channel CH_0 */
Pit_Ip_StartChannel(PIT_INST_0, CH_0, PIT_PERIOD);
FlexCAN_Ip_Init(INST_FLEXCAN_0, &FlexCAN_State0, &FlexCAN_Config0);
FlexCAN_Ip_SetTDCOffset_Privileged(INST_FLEXCAN_0,1,12);
FlexCAN_Ip_ConfigEnhancedRxFifo_Privileged(INST_FLEXCAN_0, &CAN0_EnhanceFIFO_IdFilterTable[0]);
FlexCAN_Ip_SetRxMaskType_Privileged(INST_FLEXCAN_0,FLEXCAN_RX_MASK_INDIVIDUAL);
FlexCAN_Ip_SetRxIndividualMask_Privileged(INST_FLEXCAN_0,1,0x1FFFFFFF);
FlexCAN_Ip_SetRxIndividualMask(INST_FLEXCAN_0,2,0x7FF<<18);
FlexCAN_Ip_SetStartMode(INST_FLEXCAN_0);
FlexCAN_Ip_ConfigRxMb(INST_FLEXCAN_0, RX_MB_IDX, &rx_info, MSG_ID);
rx_info.is_polling = FALSE;
rx_info.msg_id_type = FLEXCAN_MSG_ID_STD;
FlexCAN_Ip_ConfigRxMb(INST_FLEXCAN_0, RX_MB_IDX, &rx_info, 0x1);
rx_info.msg_id_type = FLEXCAN_MSG_ID_EXT;
FlexCAN_Ip_ConfigRxMb(INST_FLEXCAN_0, 2, &rx_info, 0xFACE);
FlexCAN_Ip_RxFifo(INST_FLEXCAN_0, &rxFifoData);
for(;;)
{
if(1 == toggleLed)
{
toggleLed = 0;
tx_loop[63] = tx_cnt++;
if(tx_cnt > 10)
{
Pit_Ip_StopChannel(PIT_INST_0, CH_0);
}
else
{
//Siul2_Dio_Ip_TogglePins(LED_PORT, (1UL << LED_PIN));
Siul2_Dio_Ip_TogglePins(TEST_PORT, (1UL << TEST_PIN));
FlexCAN_Ip_Send(INST_FLEXCAN_0, TX_MB_IDX, &tx_info, MSG_ID, (uint8 *)&tx_loop);
// Polling
while(FlexCAN_Ip_GetTransferStatus(INST_FLEXCAN_0, TX_MB_IDX) != FLEXCAN_STATUS_SUCCESS)
{
FlexCAN_Ip_MainFunctionWrite(INST_FLEXCAN_0, TX_MB_IDX);
}
}
}
if(1 == canfdrxflag)
{
canfdrxflag = 0;
Siul2_Dio_Ip_TogglePins(LED_PORT, (1UL << LED_PIN));
FlexCAN_Ip_Send(INST_FLEXCAN_0, TX_MB_IDX, &tx_info, MSG_ID, (uint8 *)&rxFifoData.data);
while(FlexCAN_Ip_GetTransferStatus(INST_FLEXCAN_0, TX_MB_IDX) != FLEXCAN_STATUS_SUCCESS)
{
FlexCAN_Ip_MainFunctionWrite(INST_FLEXCAN_0, TX_MB_IDX);
}
FlexCAN_Ip_RxFifo(INST_FLEXCAN_0, &rxFifoData);
}
}
FlexCAN_Ip_SetStopMode(INST_FLEXCAN_0);
FlexCAN_Ip_Deinit(INST_FLEXCAN_0);
return 0;
}
/* END main */
/*!
** @}
*/
二,测试结果
代码写的对不对,测试结果走一圈。
PCAN-VIEW配置如下:
可以看到,启动之后S32K344发了10次的CANFD帧都被收到了,而且带FD,BRS那种。
后面就是PC断通过CAN盒子给S32K344下发数据,然后K344收到之后回环回来。
从Trace窗口可以看到,只能接收标准帧0X123,0X456,和扩展帧0XABDC。后面再发0X789标准帧没有反应,说明ID滤波是起效果的。
看完了这种数字效果,来点示波器CAN0_TX上的总线效果。
这个是启动之后10次的定时触发,准确的2ms定时触发并且发送CAN数据,通道1是CAN0_TX的数据,通道3是引脚PTA12的定时翻转效果。
放大看看一帧的情况,看看总帧时间是否靠谱:
那么这个150us是否靠谱呢,我们借助下PEAK的神器:CAN FD Frame Analyzer:
https://www.peak-system.com/CAN-FD-Frame-Analyzer.563.0.html?&L=1
无需硬件,免费使用。
因为示波器测试的是结尾的上升沿,所以总体看下来150us还是靠谱的存在,说明这个CANFD是确实发出来了。但是为了进一步确认,去查看下仲裁帧bit时间和数据断bit时间:
仲裁帧bit时间如下,2us,波特率是500KHZ,所以说这个bit时间2us是正确的:
数据帧bit时间,200ns,定义的5Mhz,正好就是200ns,所以也是准确的:
所以,到目前为止,所有的功能已经全部实现了。
三,坑位标点排雷
3.1 PC接受现实CANFD实际数据段并没有高频
小编在这个圈里面差点迷失了自我。刚开始,小编很顺利,就是用PIT定时2ms去发的CANFD,然后PEAKCAN看收到了数据,就很开心,觉得已经OK了。然而冥冥之中,小编又想用示波器看看总线,不看不知道,一看吓一跳,
看看,500K/5M的64字节标准帧发出来这么长时间,这倒推一下,妥妥的就是按照500K去发的。
PC收到的数据这样:
所以小编就开始找问题了,什么BRS,ISO CAN,FIFO,enhancedCBT Can都试了,CAN transiver能不能支持ISO CAN 也怀疑了,最后毛了,连PEAK CAN能不能收5M都怀疑了,所以上了一块RT同样配置的,人家妥妥的稳定的能收到,只是
Type 变成了FD,BRS,于是查看了自己,发送的时候,使能BRS, 使能CANFD,再一搞,直接收不到了。所以,开始怀疑波特率配置,我之前用的那组波特率配置,是因为别人用了能够收发同样的数据,所以才偷懒直接用,因为不行,所以小编去NXP官方论坛网上找了一个贼牛的表格 :
CAN Bit Timing calculation v2.1.zip:
https://community.nxp.com/t5/MPC5xxx-Knowledge-Base/MPC5xxx-S32Kxx-LPCxxxx-CAN-CAN-FD-bit-timing-calculation/ta-p/1119319
里面直接可以选择K344,选择CAN收发器,然后填入目标波特率,直接给你推算几个合适的。我一看,这里推荐的没有一组是和我老的波特率配置一致的,所以按照表格果断改了配置,表格是这样的:
再试一把,还没行!然后又开始各种寻寻觅觅,凄凄惨惨戚戚!最后发现了一个神秘的点,S32K344RM里面是这么说的:73.3.10.3 Transceiver delay compensation
就是加上TDC offset,于是小编果断加上了这段代码:
FlexCAN_Ip_SetTDCOffset_Privileged(INST_FLEXCAN_0,1,14);
这个就是使能TDC offset,这个14就是按照:
Offset = (FPSEG1 + FPROPSEG + 2) × (FPRESDIV + 1)=(2+2+2)*(1+1)=12
加了之后,就可以接收了,而且还很稳定。
3.2 ISO CAN和NO ISO CAN
这里主要说明的就是,PEAK View的配置ISO CAN要和芯片的FlexCAN的ISO CAN 配置要对应,比如选了ISO CAN,就都要选,否则一个不选就会通讯错误。另外在平时选择CAN收发器的时候也要注意,如果要用ISO CAN,也的选择支持ISO CAN标准的CAN收发器。