K60 FlexCAN 清中断标志的一个问题

客户反映这样一个现象:使用FlexCAN ,使能发送中断和接收中断,在中断服务函数中如果同时有发送中断标志和接收中断标志,在清除一个标志位的同时会自动把另外一个标志位也清除掉。
本博客将此case复现,并提供解决办法。

以下应用层代码:

#include "gpio.h"
#include "uart.h"
#include "can.h"
/* 实验名称:CAN 通讯测试
   实验平台:渡鸦开发板
   板载芯片:MK60DN512
   实验效果:使用CAN1模块的3号邮箱采用中断方式接收来自0x56的数据,使用2号邮箱向0x10地址发送数据,使能发送中断  */


extern CAN_Type * const CAN_InstanceTable[];

// CAN1 中断服务函数
void CAN_ISR(void)
{

    printf("Go to CAN_ISR\r\n");

    // 判断2号邮箱发送中断标志位是否置位
    if((CAN_InstanceTable[HW_CAN1]->IFLAG1 & 0x04) != 0x00)
    {
       printf("Tx interrupt\r\n");
      /* make sure clear IT pending bit according to IT enable reg */
      CAN_InstanceTable[HW_CAN1]->IFLAG1 |= 0x04; // 通常方式清除标志位
     // CAN_InstanceTable[HW_CAN1]->IFLAG1 = 0x04;
    }
    // 判断接收3号邮箱中断标志位是否置位
    else if((CAN_InstanceTable[HW_CAN1]->IFLAG1 & 0x08)!= 0x00)
    {
       printf("Rx interrupt\r\n");
      /* make sure clear IT pending bit according to IT enable reg */
        CAN_InstanceTable[HW_CAN1]->IFLAG1 |= 0x08; // 通常方式清除标志位
  //  CAN_InstanceTable[HW_CAN1]->IFLAG1 = CAN_IFLAG1_BUF4TO0I(0x08);
    }

}

int main(void)
{
    DelayInit();
    GPIO_QuickInit(HW_GPIOE, 6, kGPIO_Mode_OPP);
    UART_QuickInit(UART0_RX_PD06_TX_PD07, 115200);

    printf("CAN test\r\n");

    CAN_QuickInit(CAN1_TX_PE24_RX_PE25, kCAN_Baudrate_125K); // 初始化

    CAN_CallbackInstall(HW_CAN1, CAN_ISR); // 安装中断回调函数

    CAN_ITDMAConfig(HW_CAN1,3, kCAN_IT_RX);// 打开3号邮箱中断接收功能

    CAN_SetReceiveMB(HW_CAN1, 3, 0x56);// 配置3号邮箱接收

    CAN_ITDMAConfig(HW_CAN1,2, kCAN_IT_Tx);// 打开2号邮箱中断发送功能

  //  while(1)
  //  {
        CAN_WriteData(HW_CAN1, 2, 0x10, (uint8_t *)"CAN TEST", 8); // 使用2号邮箱发送数据
        DelayMs(1000);
        CAN_WriteData(HW_CAN1, 2, 0x10, (uint8_t *)"Hello !!", 8); // 使用2号邮箱发送数据
        DelayMs(1000);
 //   }

        while(1);
}

在发送数据前:IFLAG1 寄存器为0
这里写图片描述

在数据发送完成后,进去中断服务函数,IFLAG1 发送中断标志位置1
K60 FlexCAN 清中断标志的一个问题_第1张图片

这时通过CAN给K60 发送一组数据:
K60 FlexCAN 清中断标志的一个问题_第2张图片

单步执行一下程序:IFALG1的中断接收标志也置为1
这里写图片描述

执行完清标志位语句后:

CAN_InstanceTable[HW_CAN1]->IFLAG1 |= 0x04;

IFLAG1 全部位都变为了0,这就出现了问题~!
这里写图片描述

如果将清标志语句改为:

CAN_InstanceTable[HW_CAN1]->IFLAG1 = 0x04;

现象是:对应位被清楚,不影响其他标志位
这里写图片描述

这样之后会再一次进入中断服务函数,这次完成中断接收功能,并清楚标志位
这里写图片描述

总结起来:
清标志这里

change from: can_reg_ptr->IFLAG1 |= tmp_reg;
change to: can_reg_ptr->IFLAG1 = tmp_reg;

解决方法参考的是:https://community.freescale.com/message/346689#346689

K60 FlexCAN 清中断标志的一个问题_第3张图片

一开始还没想明白原因是什么,现在终于明白了原因。

我们通常操作寄存器的某位都是:

reg_ptr->IFLAG1 |= tmp_reg1;// 置位,tem_reg1 默位为1,其他全为0

reg_ptr->IFLAG1 &= tmp_reg2;// 清零,tem_reg1 默位为0,其他全为1

这种形式来修改某一寄存器中的某位,目的是只修改需要改的位,避免影响其它位。

它其实等同于,是一个read-modify-write的过程

reg_ptr->IFLAG1 =reg_ptr->IFLAG1 | tmp_reg1;
reg_ptr->IFLAG1 =reg_ptr->IFLAG1 & tmp_reg2;

但是用在IFLAG1寄存器就会有问题了:
K60 FlexCAN 清中断标志的一个问题_第4张图片

因为它是写1清0,

CAN_InstanceTable[HW_CAN1]->IFLAG1 |= 0x04;

这条语句本义是给IFLAG1[2]写1清0,其他位保持不变。但是实际效是:
IFLAG1[3]为1,又写了一次1,结果就清0了。

所以根本原因就是IFLAG1的位是写w1c,而一般的寄存器写什么就是什么。

你可能感兴趣的:(Freescale,Kinetis,K,MCU)