问题

在定时器中断里不能正确访问全局变量。比如如下代码。
折腾了几个小时根本不知道为什么,因为原来写别的程序也是一样的写完全没有这种诡异的问题。

unsigned int counter;
unsigned char flag;

main(){
    flag = 0;
    counter = 0;
    Timer0Init();
    while(1);
}

void timer0() interrupt 1
{
    tt++;
    if (tt>=200)
    {
        tt = 0;
        // 现象是不管main函数中初值设为0还是1,这里都只会执行0的分支
        // 就好像main里面设置的值完全没起作用似的
        if (flag == 1)
        {
            // do something 1
            flag = 0;
        } else {
            // do something 2
            flag = 1;
        }
    }
}

原因

有的编译器在优化代码时会试图按自己的理解使用寄存器里备份的数据而不去读取变量地址里最新的数据。因为编译器认为是一样的,为了节省资源就不会每次都去内存中读取变量的数据。

解决方法1:换旧版本的Keil UV4

自己机器上安装了两个不同版本的UV4,我注意到出问题时是用了较新版本(V4.72)的UV4。尝试用旧版UV4(V4.00)打开同一个工程后就问题消失。这也说明了为什么以前没出过这种问题。
至于为什么会有这种差别也没有仔细研究,可能跟新旧版本 编译时的默认设置有关系。

解决方法2:全局变量定义时加上volatile关键字

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
比如之前在新版uv4中有问题的代码改成下面这样就可以解决。

volatile unsigned int counter;
volatile unsigned char flag;

版权属于:芒果爱吃胡萝卜

本文链接:http://mangolovecarrot.net/2020/11/22/175

转载时须注明出处及本声明

Last modification:November 22nd, 2020 at 09:09 pm