0. 引言
真没想到距离写第一篇S32K144入门笔记(1) 从零开始进行开发环境搭建已经过去了半年。
当时是项目紧逼,20天之前要把这个片子的大部分外设跑起来,做好BOOT+APP架构,在十一之前出一个demo。
只能每天调完一个外设,本来计划是给每个外设都写一篇总结的。
后来demo完成就转手给其他同事了,然后进了其他项目,一转眼就是半年。
这半年里,S32K已经推到了多个项目里使用,形成S32K + ADAS(海思3566/赛灵思zynq/zux)的架构,但是都是在上层的业务做修改,底下的一些外设接口基本没怎么变过,所以没怎么遇到大问题,就没再回来这个项目做修改。唯一遇到的问题就是lin的,这个后面开单独一节来写。
最近正好面临工作交接,再次把项目过一遍,整理一些文档,留给后面的同事。
这件事证明了:自己一定要抽时间来做总结,单纯地不停赶项目,会给自己很充实的错觉。一定要思考,才能沉淀。
跑偏了,拉回,汽车电子设备,先写can。
1. 开发环境
- 硬件环境
为了通用,硬件没用项目的硬件,还是使用的就是官网的黄色长条开发板,在第一篇里介绍过。 - 软件环境
SDK用的 S32 DESIGN STUDIO for ARM,Version : 2018.R1。
库用的是 RTM3.0.0。
组件用的can_pal。
2. Demo分析
S32K144的资料很少,我们直接从SDK里拉个demo来分析把。
2.1 demo
用的是Pal层的demo程序,调用的是can_pal这个组件。
2.2 硬件引脚
要注意就是,调can,这个开发板必须使用12V供电,否则can phy那边起不来。
quickStart文档上标识了要使用的接口:
注意就是J107的跳线帽,需要使用12V电源供电,并切换。切换到1-2脚连接。
先看原理图
芯片上脚是: PTE4 和 PTE5.
然后过一个电平转换,这块我们不用管。
然后到can phy,输出
实际接线如下,注意跳线帽
2.3 demo程序分析
这个demo程序实现比较简单,和can相关流程就是,
- 通过CAN_Init接口配置can,配置参数can_pal1_Config0是用pe图形化配置,自动生成的代码,主要配置就是波特率等属性。
CAN_Init(&can_pal1_instance, &can_pal1_Config0);
- 通过CAN_ConfigRxBuff接口配置can邮箱,配置参数 can_buff_config_t buffCfg是临时写明的。要注意就是,这个变量不能申请临时局部变量,配置进去是不会保存值的,只是存了指针,can每次都会回来找配置的。
can_buff_config_t buffCfg = {
.enableFD = true,
.enableBRS = true,
.fdPadding = 0U,
.idType = CAN_MSG_ID_STD,
.isRemote = false
};
/* Configure RX buffer with index RX_MAILBOX */
CAN_ConfigRxBuff(&can_pal1_instance, RX_MAILBOX, &buffCfg, RX_MSG_ID);
- 主流程中通过CAN_Receive接口去接收can消息。
- 按键中断中,通过CAN_Send来发送can消息。
2.4 库分析
分析几个重要的接口吧,无非是过滤、发送、接收。
2.4.1 配置过滤ID - CAN_ConfigRxBuff
CAN_ConfigRxBuff调用了FLEXCAN_DRV_ConfigRxMb,进而调用FLEXCAN_SetRxMsgBuff。
这个是通过配置flexcan_mb_id来实现过滤。
volatile uint32_t *flexcan_mb = FLEXCAN_GetMsgBuffRegion(base, msgBuffIdx);
volatile uint32_t *flexcan_mb_id = &flexcan_mb[1];
再找FLEXCAN_GetMsgBuffRegion,返回的第N个message buffer的地址。
然后给flexcan_mb_id 赋值,看代码是赋给了message buffer的4字节偏移的位置。
再向下的机制就没再深究。
2.4.2 Can发送 - CAN_Send
CAN_Send是can发送接口,
调用完可以用 while(CAN_GetTransferStatus(INST_CAN_PAL1, TX_BUFF_IDX) == STATUS_BUSY); 来判断是否发送完成。
但是这种情况下,如果板子没接can线,就会一直卡在这里,所以可以用CAN_SendBlocking(INST_CAN_PAL1, TX_BUFF_IDX, &sendMsg,1);加一个超时机制。
看实际情况来使用吧。
2.4.3 Can接收 - CAN_Receive
与发送同理,有一个CAN_ReceiveBlocking超时机制的接口,这样做起来会比较灵活。
CAN_Receive(&can_pal1_instance, RX_MAILBOX, &recvMsg); 中接收buffer地址recvMsg会配给mb_message。
然后通过EDMA_DRV_ConfigSingleBlockTransfer接口,配置source地址和dest地址做一次搬运。
源数据都是放在RAMn地址上的。
3. 实际使用遇到的问题
基于这个demo来做了我们自己的工程,记录几个实际遇到的问题把。
3.1 基于demo测试,can收不到数据
因为加了过滤机制,只能收ID 为0x2的报文。
所以只能收到RX_MSG_ID的数据。
3.2 基于demo测试,can发不出数据
demo设计就是按键会发送一个can报文,但是实测在按键中断里,调用Can_send 无法发出can数据。
单步跟踪到FLEXCAN_StartSendData接口里面的
if (state->mbs[mb_idx].state != FLEXCAN_MB_IDLE)
{
return STATUS_BUSY;
}
status返回的是STATUS_BUSY,但是接收能收到。
解决方法:
在配置界面里把can_fd勾掉。
payload_size 改成8。
接收到数据:
3.3 数据发送混乱,第一次有帧号,后面就没了,can盒接收数据一会远程帧一会数据帧
- 第一次有帧号,后面就变成0了
跟踪:
在FLEXCAN_SetTxMsgBuff函数中,有这样一句:
if(cs->msgIdType == FLEXCAN_MSG_ID_STD)
{
/* ID[28-18] */
*flexcan_mb_id &= ~CAN_ID_STD_MASK;
*flexcan_mb_id |= (msgId << CAN_ID_STD_SHIFT) & CAN_ID_STD_MASK;
/* make sure IDE and SRR are not set */
flexcan_mb_config &= ~(CAN_CS_IDE_MASK | CAN_CS_SRR_MASK);
}
只有第一次进来是对的,cs->msgIdType = 0.是数据帧,会把帧号赋给flexcan_mb_id 。
后面就进不来了。
再往上跟踪
FLEXCAN_StartSendData()
里的 cs.msgIdType = tx_info->msg_id_type; 赋值的
这个msg_id_type是从
CAN_Send里的
flexcan_data_info_t dataInfo = {
.msg_id_type = (flexcan_msgbuff_id_type_t) s_hwObjConfigs[instance][buffIdx]->idType,
赋值的
2. 有时候消息会变成远程帧出去。
是数据帧还是远程帧,是由
CAN_Send里的dataInfo的is_remote决定的。
.is_remote = s_hwObjConfigs[instance][buffIdx]->isRemote,
单步调试,这个值只有第一次的时候为0,后面(第二次跑到这里)怎么就变成了43?
这个s_hwObjConfigs数组是从哪里来的?
3.
综合上述两个bug。
都是在Can_Send这个接口里出的问题。
都是读取s_hwObjConfigs这个配置出错的。
为啥呢?
来找这个s_hwObjConfigs是在哪里配的?
void ConfigCan0Buffer()
{
can_buff_config_t buffConfig = {
.enableFD = false,
.enableBRS = false,
.fdPadding = 0x00,
.idType = CAN_MSG_ID_STD,
.isRemote = false
};
CAN_ConfigTxBuff(INST_CAN_PAL1, TX_BUFF_IDX, &buffConfig);
CAN_ConfigRxBuff(INST_CAN_PAL1, RX_BUFF_IDX, &buffConfig, 0x55);
}
问题找到了,这个参数是指针传的,每次发送都要读取。
不能把buffConfig 写到函数内部,运行完了就释放了。
要写成全局变量。
3.4 实现中断接收
网上下载了一个NXP的demo,
使用 CAN_InstallEventCallback(INST_CAN_PAL1, can0_Callback, NULL); 注册中断回调就好。
在中断中可以做一些存储策略,比如用自己的环形队列。
3.5 其他资料
在组件上右键有Doxygen documentation,里面也有一些说明和例子,可以参考。