|
汽车零部件采购、销售通信录 填写你的培训需求,我们帮你找 招募汽车专业培训老师
副标题:S32K1xx SDK FlexCAN驱动使用MB收发CAN报文丢失问题root cause分析与解决方案探究
内容提要
1. S32K1xx SDK FlexCAN PD层驱动使用MB收发CAN报文丢失问题2. 问题分析(root case)
2.1 S32K1xx SDK的FlexCAN驱动CAN报文发送和接收API函数及中断ISR工作流程分析
2.2 FlexCAN中断掩码(IMASK1)和中断标志寄存器(IFLAG1)
2.3 S32K1xx SDK的FlexCAN驱动中MB的中断使能代码解析
2.4 问题复盘
3. 解决方案(solution)总结(summary)--外设控制和配置寄存器原子操作问题探究
1. S32K1xx SDK FlexCAN PD层驱动使用MB收发CAN报文丢失问题
最近有两个客户遇到使用S32K1xx SDK提供的FlexCAN PD层驱动时,收发报文丢失的问题。具体表现为:使用不同的MB发送或者接收指定ID的CAN报文,在总线负载(bus loading)较高(比如超过60%)时,会出现某些ID的CAN报文停止发送或者接收了。
比如,下面测试工程中,配置使用MB0~MB4来发送5个不同ID的CAN报文:
使用MB0发送ID=0x111的标准帧,数据长度为8的CAN 2.0 A/B报文;
使用MB1发送ID=0x222的标准帧,数据长度为8的CAN 2.0 A/B报文;
使用MB2发送ID=0x333的标准帧,数据长度为8的CAN 2.0 A/B报文;
使用MB3发送ID=0x444的标准帧,数据长度为8的CAN 2.0 A/B报文;
使用MB4发送ID=0x555的标准帧,数据长度为8的CAN 2.0 A/B报文;
工作一段时间后,发现ID = 0x222,0x333和0x444的报文就停止发送了。
这种情况发生后,除非终止传输(AbortTransfer)相应的MB,或者重新初始化(Init)整个FlexCAN模块才能恢复正常:status_t FLEXCAN_DRV_AbortTransfer(uint8_t instance, uint8_t mb_idx);status_t FLEXCAN_DRV_Init (uint8_t instance, flexcan_state_t *state, const flexcan_user_config_t *data);
2. 问题分析(root case)
跟踪调试发现,直接原因是FlexCAN模块的中断标志寄存器(IFLAG1)中配置用于发送或者接收丢失ID报文的FlexCAN的硬件消息缓存MB(Message Buffer)的发送和接收完成中断标志已经置位(为‘1’),但是对应的中断使能控制位在中断掩码寄存器(IMASK1)却没有置位(为‘0’)。
因此,内核将无法响应这些“问题”MB的发送完成和接收完成中断,进而导致FlexCAN的PD层驱动的全局状态结构体(g_flexcanStatePtr)中相应MB的状态一直为busy状态(state = FLEXCAN_MB_TX_BUSY或者FLEXCAN_MB_RX_BUSY),从而再次调用FlexCAN驱动的发送和接收API函数将返回busy,因此,这些“问题”MB将停止工作:
2.1 S32K1xx SDK的FlexCAN驱动CAN报文发送和接收API函数及中断ISR工作流程分析
S32K1xx SDK的FlexCAN驱动的发送API函数和MB中断ISR工作流程如下,每次操作FlexCAN的MB发送CAN报文之前,驱动代码都会检查保存在FlexCAN驱动全局状态结构体(g_flexcanStatePtr)全局当前MB的状态是否为busy,如果为busy,则直接退出了。
每次FlexCAN模块通过MB的发送和接收API函数使能MB中断,然后报文发送或者接收完成后,在MB中断ISR中关闭所使用的MB中断:
从驱动工作流程上看,并不能发现任何问题,因为FlexCAN模块每个MB的收发报文操作都有独立MB state进行加锁,操作前枷锁,操作完成后,再解锁。
那么问题出在哪里呢?
2.2 FlexCAN中断掩码(IMASK1)和中断标志寄存器(IFLAG1)
首先,我们来看看FlexCAN模块的中断使能和中断标志寄存器:
S32K1xx系列MCU的FlexCAN模块最多支持32个MB,每个MB在完成发送或者接收后,均可以产生自己的中断,寄存器IMASK1的每一个bit位对应FlexCAN模块的一个MB,若置1则使能相应MB的TX/RX中断,默认为0,关闭屏蔽中断:
相应地,FlexCAN模块提供一个写1清0(w1c)的32-bit中断标志位寄存器IFLAG1,每个bit位对应一个MB的中断事件标志。若使能Rx FIFO后,BUF0I~BUF4I保留不用,BUF5I为Rx FIFO接收数据可用(available)的标志位,BUF5I为Rx FIFO接收数据可用(available)的标志位,BUF6I为Rx FIFO警告(warning)事件标志位,BUF7I为Rx FIFO溢出(overflow)事件标志位:
Tips:在FlexCAN的MB发送或者接收完成中断ISR中,由于是16个MB共用一个中断向量/中断ISR,必须使用写1清0(w1c)的方式清除MB中断标志位,否则可能会影响其他MB的中断事件。
2.3 S32K1xx SDK的FlexCAN驱动中MB的中断使能代码解析
S32K1xx SDK的FlexCAN驱动中MB的中断使能代码如下:/*FUNCTION********************************************************************** * * Function Name : FLEXCAN_SetMsgBuffIntCmd * Description : Enable/Disable the corresponding Message Buffer interrupt. * *END**************************************************************************/status_t FLEXCAN_SetMsgBuffIntCmd( CAN_Type * base, uint32_t msgBuffIdx, bool enable){ uint32_t temp; status_t stat = STATUS_SUCCESS;
/* Enable the corresponding message buffer Interrupt */ temp = 1UL << (msgBuffIdx % 32U); if (msgBuffIdx < 32U) { if (enable) { (base->IMASK1) = ((base ->IMASK1) | (temp)); } else { (base->IMASK1) = ((base->IMASK1) & ~(temp)); } } return stat;}其反汇编结果如下:00000000 <FLEXCAN_SetMsgBuffIntCmd>: uint32_t msgBuffIdx, bool enable){ uint32_t temp; status_t stat = STATUS_SUCCESS;
if (msgBuffIdx > (((base->MCR) & CAN_MCR_MAXMB_MASK) >> CAN_MCR_MAXMB_SHIFT)) 0: 6803 ldr r3, [r0, #0] 2: f003 037f and.w r3, r3, #127 ; 0x7f 6: 428b cmp r3, r1 8: d207 bcs.n 1a <FLEXCAN_SetMsgBuffIntCmd+0x1a> { stat = STATUS_CAN_BUFF_OUT_OF_RANGE; a: f44f 7040 mov.w r0, #768 ; 0x300 e: 4770 bx lr temp = 1UL << (msgBuffIdx % 32U); if (msgBuffIdx < 32U) { if (enable) { (base->IMASK1) = ((base ->IMASK1) | (temp)); 10: 6a83 ldr r3, [r0, #40] ; 0x28 12: 4319 orrs r1, r3 14: 6281 str r1, [r0, #40] ; 0x28 status_t stat = STATUS_SUCCESS; 16: 2000 movs r0, #0 18: 4770 bx lr if (msgBuffIdx < 32U) 1a: 291f cmp r1, #31 1c: d80c bhi.n 38 <FLEXCAN_SetMsgBuffIntCmd+0x38> temp = 1UL << (msgBuffIdx % 32U); 1e: f001 011f and.w r1, r1, #31 22: 2301 movs r3, #1 24: fa03 f101 lsl.w r1, r3, r1 if (enable) 28: 2a00 cmp r2, #0 2a: d1f1 bne.n 10 <FLEXCAN_SetMsgBuffIntCmd+0x10> } else { (base->IMASK1) = ((base->IMASK1) & ~(temp)); 2c: 6a83 ldr r3, [r0, #40] ; 0x28 2e: ea23 0101 bic.w r1, r3, r1 32: 6281 str r1, [r0, #40] ; 0x28 status_t stat = STATUS_SUCCESS; 34: 2000 movs r0, #0 36: 4770 bx lr 38: 2000 movs r0, #0 }#endif }
return stat;}其中,对FlexCAN模块的MB中断屏蔽(使能)寄存器IMASK1的配置是一个读改写(Read-Modify-Write)操作,从而避免对目标MB之外的其他MB中断配置的影响。
使能中断代码的读改写(或)反汇编代码如下:(base->IMASK1) = ((base ->IMASK1) | (temp));6a83 ldr r3, [r0, #40] ; 0x284319 orrs r1, r36281 str r1, [r0, #40] ; 0x28关闭中断代码的读改写(与)反汇编代码如下:(base->IMASK1) = ((base->IMASK1) & ~(temp));6a83 ldr r3, [r0, #40] ; 0x28ea23 0101 bic.w r1, r3, r16281 str r1, [r0, #40] ; 0x28这个读改写的操作并非原子操作,而是分为了三个步骤,对应三条汇编指令:
① 使用汇编指令ldr读取IMASK1寄存器值到CPU内核通用寄存器(GPR);② 使用汇编指令bic/orr完成与/或操作,清除/置位某一特定bit位;③ 使用汇编指令str将CPU内核通用寄存器(GPR)中的结果写回IMASK1寄存器;
其中,若第②或者第③步(汇编指令)执行被其他外设中断(比如定时器中断)或者RTOS发生任务抢占,切换到中断ISR或者RTOS的其他任务代码执行时,也对IMASK1进行了其他bit位的改写操作,则会导致IMASK1寄存器的非预期(意外修改)结果。
2.4 问题复盘
下面,我们来复盘一下,这个问题发生的过程(流程如下表所示):
正常情况(T1~T6):假设当前时刻(T1)应用程序已经调用FlexCAN的发送API函数,通过MB0和MB2发送CAN报文,则相应的MB中断使能,即IMASK1= 0x0000_0005,然后在main()函数主循环中调用FlexCAN发送API函数通过MB1和MB4发送CAN报文,则IMASK1寄存器值的变化为0x0000_0005 --> 0x0000_0007 --> 0x0000_0017,到时刻T6时,MB0、MB1、MB2和MB4的中断都使能了;
异常情况(T100~T105):假设当前时刻(T100)应用程序已经调用FlexCAN的发送API函数,通过MB0和MB2发送CAN报文,则相应的MB中断使能,即IMASK1= 0x0000_0005,然后在main()函数主循环中调用FlexCAN发送API函数通过MB1发送CAN报文,但是,在调用发送API函数通过MB1发送CAN报文的过程中发生了定时器中断,在定时器中断中,调用发送API函数通过MB4发送了一帧CAN报文,则IMASK1寄存器值的变化为0x0000_0005 --> 0x0000_00015 --> 0x0000_0007,到时刻T105时,只有MB0、MB1和MB2的中断都使能了,但MB4的中断使能却被意外清除了,因此MB4的中断将不再被CPU内核响应了,响应地,MB4的驱动状态将一直保持为busy(FLEXCAN_TX_BUSY)。
Tips:关于S32K1xx的FlexCAN模块工作原理和使用指南以及其SDK驱动使用详解,请参考我的另外两篇公众号文章(点击文章标题即可直接跳转阅读):
《S32K1xx系列MCU应用指南之FlexCAN模块功能与应用详解》;《S32K SDK使用详解之can_pal组件和flexcan组件使用详解(含RxFIFO DMA和ID滤波器以及总线关闭恢复等)》;
Tips:Qorivva MPC56/57xx和S32R以及S32K3使用的也是FlexCAN模块,也可以参考以上公众号文章和本文内容。
Tips:关于S32 SDK的软件设计思想和架构,可以参考以下两篇公众号文章(点击文章标题即可直接跳转阅读):
《S32K SDK使用详解之S32 SDK软件编程思想详解》;《S32K SDK使用详解之S32 SDK软件架构详解》;
3. 解决方案(solution)
通过以上分析可知,为了保证FlexCAN的PD驱动程序中使能和关闭FlexCAN MB中断的操作是原子操作,只需要在添加其硬件访问(Hardware Access)层(SDK/platform/drivers/src/flexcan/flexcan_hw_access.c)API函数--FLEXCAN_SetMsgBuffIntCmd()将以下代码:/* Enable the corresponding message buffer Interrupt */temp = 1UL << (msgBuffIdx % 32U);if (msgBuffIdx < 32U){ if (enable) { (base->IMASK1) = ((base ->IMASK1) | (temp)); } else { (base->IMASK1) = ((base->IMASK1) & ~(temp)); }}修改如下,在操作FlexCAN模块的中断屏蔽寄存器(IMASK1)前调用S32K1xx 的interrupt_manager组件提供的API函数--INT_SYS_DisableIRQGlobal()关闭CPU内核全局中断,操作完成后,在调用API函数--INT_SYS_EnableIRQGlobal()使能CPU内核全局中断即可保证FlexCAN模块MB中断使能和关闭的原子操作:/* Enable the corresponding message buffer Interrupt */ temp = 1UL << (msgBuffIdx % 32U); if (msgBuffIdx < 32U) { if (enable) { /* disable CPU global interrupt */ INT_SYS_DisableIRQGlobal();
(base->IMASK1) = ((base ->IMASK1) | (temp));
/* enable CPU global interrupt */ INT_SYS_EnableIRQGlobal();
} else { /* disable CPU global interrupt */ INT_SYS_DisableIRQGlobal();
(base->IMASK1) = ((base->IMASK1) & ~(temp));
/* enable CPU global interrupt */ INT_SYS_EnableIRQGlobal(); } }
总结(summary)--外设控制和配置寄存器原子操作问题探究
总结此类问题,可以归结为MCU外设控制/配置寄存器的原子操作问题。即:一个控制/配置寄存器中多个功能控制/配置bit位在CPU运行时(runtime),由于外设中断(比如定时器)或者RTOS任务抢占调度,且在中断ISR或者高优先级RTOS任务代码中也有对同一控制/配置寄存器的其他控制/配置bit位置1或者清0操作,导致其动态控制/配置的读改写操作被意外打断,而带来的控制/配置寄存器值一致性问题。
其解决方案有以下两种:
① 在MCU外设功能设置时,针对外设模块的关键控制/配置/状态寄存器,设计专门的写1有效寄存器。
比如,FlexCAN模块的MB中断标志寄存器IFLAG1,就是写1清0(w1c)的,写0无效,这样想要清除某个MB的报文发送或者接收完成中断,只需度相应的bit位写1即可,不会影响其他MB的中断:
再比如, S32K1xx系列MCU的GPIO模块对引脚的输出控制,除了提供并行32-bit输出PSOR寄存器之外,还提供了写1有效的置位(Set)、清除(Clear)和翻转(Toggle)输出控制寄存器PSOR、PCOR和PTOR:
Tips:在S32K1xx SDK中,PinSettings组件为以上不同寄存器的操作提供了不同的LLD API函数,为了避免非原子操作带来的结果一致性问题以及外设访问效率,推荐使用写1有效的API函数:
关于S32K1xx系列MCU的端口和引脚模块以及相应的SDK驱动使用,请参考如下公众号文章(点击文章标题即可直接跳转阅读):
《S32K SDK使用详解之PinSettings组件配置与使用详解(S32K1xx PORT 和GPIO模块)》;
Tips:以下是我之前写的一篇介绍Freescale S08和S12(X)以及MagniV S12Z系列MCU上使用的TIM定时器模块通道中断标志清除注意事项的文章,亦可以作为参考(点击文章标题即可直接跳转阅读):
《外设使用Tips之TIM定时器使用FAQ和使用经验》;
② 如本文介绍的FlexCAN模块的MB中断使能/关闭配置问题,在未提供以上机制的关键外设控制/配置寄存器读改写操作之前,关闭CPU内核全局中断,操作完成之后,再重新使能CPU内核全局中断即可。
Tips:方案①的设计在32-bit的MCU设计中,已经被十分广泛地应用于定时器和GPIO引脚控制的外设模块;
另外,根据客户的反馈,FlexCAN报文丢失的问题在测试和整车上运行时也不是那么容易复现,因为设计得比较好的整车网络架构上,CAN总线的bus loading通常都不会很高(低于40%),只要极少数情况下才可能出现大于60%的bus loading。
为此,根据客户的反馈,我在S32K144-EVB上创建了一个测试工程,基于S32DS v3.4 IDE + S32K1xx SDK RTM 4.0.2。
在该测试工程中,还使能了4个提到的PIT定时器,分别定时100us、200us、300us和400us,并在其他中断ISR中调用不同的FlexCAN MB发送CAN报文,而且将PIT定时器的中断优先级设置高于FlexCAN模块的优先级,从而加速问题的复现:#define TIMR_INT_PRI_LEVEL 0#define CAN_INT_PRI_LEVEL 1大家可以从以下百度云盘链接下载该测试工程,作为参考,希望对大家有所帮助和启发。
链接: https://pan.baidu.com/s/1MPDg4DLg1Z0D3WTGbBGPXQ提取码: 4yg6
以上就是今天想要给大家分享的知识点,希望对大家有所帮助。 |
|