在ADS上由于volatile惹得祸

  C语言关键字volatile是一个危险的东东,笔者再用ADS做S3C2440定时器中断实验就因为这个关键字出了错。出现错误情况的准确描述是:定义一个变量时没有用volatile关键字,而且紧接着while(1)循环里边就有对这个变量的读操作。

   这个实验想实现的功能是:定时时间为1s,用一个led灯显示这个时间,亮一秒钟,然后再灭一秒钟。程序实现思路是:开irq中断,开定时器0中断,并设置定时器0中断时间为1s;在中断服务程序中利用一个全局变量flag来传递定时时间到信号,每中断一次flag翻转一次;主循环中读取flag标志,根据flag标志决定led灯的亮灭。

中断服务程序

void __irq Timer0_Isr(void)
{
    flag=!flag;
    rSRCPND|=1<<10;
    rINTPND|=1<<10;    
}

flag定义在主程序中

unsigned int flag=0;

主循环程序

int Main()
{
    IO_Init();
    while(1){
        if(flag){
            Led1_Off();
        }
        else{
            Led1_On();     
        }
    }
   return(0);
}

    经测试定时1s,中断服务程序都是能正常工作的,但是led不能闪烁,一直亮。试了很多办法,无果。

    后来将变量的定义更改了一下,加一个关键字volatile。就解决了问题。但是,什么原因不得而知。

volatile unsigned int flag=0;

    走投无路的情况下,只能求助于反汇编代码。

对比源程序

对比反汇编(图片太大,网页浏览时无法完全显示,可以对着图片单击右键,选择图片另存为桌面查看)

    看到反汇编代码,很容易知道错误出现在哪儿。没加volatile时候,执行while循环需要重新读取flag的值时,不是从flag对应的内存单元中读的,而是读取保存flag临时数据的r2。虽然中断服务程序会将flag值更改,但是由于读取的是r2而且r2在主循环中始终不变,也就是说不能读到中断服务程序对flag的更新,所以led灯也不可能改变。

    当加了volatile后,执行while循环需要重新读取flag的值时,是从flag对应的内存单元中读的,所以主循环能读到中断服务程序对flag的更新,led也能正常工作了。

结论:ADS对加没加volatile的变量处理是有区别的,但是我认为ADS对这种情况的处理不是很正确

    之所以这样说,我源于下面三个实验。我用keil和gcc分别作了类似上边的实验,看编译器对没有加volatile的变量处理情况,发现这两个编译器都能正确编译,而唯独ADS对没有加volatile的变量处理的过分。

keil对没有加volatile的变量处理

测试程序

反汇编代码

gcc对没有加volatile的变量处理

第三个实验,在 if(flag) 前边加一个delay_time()函数,发现能够正常工作

 试想,ADS仅仅因为在 if(flag)前边加了一个delay()函数,就更改了它的策略,我真觉得ADS对这种情况(定义一个变量时没有用volatile关键字,而且紧接着while(1)循环里边就有对这个变量的读操作)处理的有问题。而且,我认为编译器对volatile的处理好像也不是这样的。我对ADS的编译还存在疑惑,我对编译器对volatile的处理还存在疑惑。

附实验源码下载地址:timeirq.zip

原文地址:https://www.cnblogs.com/amanlikethis/p/3433701.html