看到标题,大家应该可以意识到:这是一个刷写问题,同时,该问题Bug还与清中断标志位相关。
1、问题描述
Boot在升级Application程序的过程中,偶发的连续帧发送失败,导致Application刷写失败。
信息补充:ECU1属于网关节点,负责将上位机(Tester)的诊断命令路由给ECU2,ECU2是待升级的节点,ECU1和ECU2之间通过CAN总线升级。
失败的工况有如下几种情况:
ECU1发送首帧(FF)给目标节点ECU2;
ECU2给出流控帧(FC)响应;
ECU1收到ECU2的FC以后,根据流控信息发送连续帧(CF);
ECU1在某个时刻收到一帧响应报文(eg:SF),ECU1再发送一个CF帧以后,不再给ECU2发送CF(CF还没有发送完),直到发送超时,ECU2刷写失败。
ECU1发送首帧(FF)给目标节点ECU2;
ECU2响应流控帧(FC);
ECU1收到流控帧以后,
发送连续帧,在发送连续帧期间接收一帧,后面不再发送连续帧(实际没有发送完),对应上图的工况(b);
没有发送连续帧,导致ECU2升级失败,,对应上图的工况(c)。
2、原因分析
在问题工程中,首先确认CAN诊断报文的收/发,均使用中断的方式,且接收中断优先级高于发送中断优先级。对于中断的仲裁过程有疑惑的可以参考前文Aurix:中断仲裁过程。
当问题出现时,发现:发送中断例程没有完全执行。为什么呢?
嵌入式开发中,程序进入中断例程以后,有一件事必不可少:清除中断标志位。而本文的问题点在于中断标志位的清除动作没有设计好。为什么没有清除好呢?
首先,我们看一下,tc3xx CAN模块的中断寄存器IR(Interrupt Register),清除中断标志位的动作实质就是清除该寄存器对应的位域。怎么清除某个位域呢?手册解释:向对应的位域写“1”。eg:某个CAN报文发送完成以后,发送完成标志位TC(Transmission Completed)会自动置位( = 1),表示发送完成,之后程序跳转到发送完成中断例程,进入发送完成中断例程以后,我们会进行清除TC的操作,如果这个操作不恰当会带来Bug,本文的症结就在这。
typedef struct MCAN_IR_B_Tag
{
uint32 RF0N :1; //Rx FIFO 0 New Message
uint32 RF0W :1; //Rx FIFO 0 Watermark Reached
uint32 RF0F :1; //Rx FIFO 0 Full
......
uint32 TC :1; //Transmission Completed
.....
}MCAN_IR_B_T;
typedef union MCAN_IR_Tag
{
MCAN_IR_B_T B;
uint32 U32;
}MCAN_IR_T;
/* clear TC Bit */
MCAN->IR.B.TC = 1;
3、解决措施
由上可以看出,问题点主要出在驱动层,即:操作IR寄存器时,清除对应位域的时候,操作不当,那应该如何操作呢?如下示意:
/* clear RF0F Bit */
MCAN->IR.U32 = uint32(1 << 2);
联系客服