[转]当主函数与中断函数共享变量问题

本文探讨了一个关于C程序设计中的问题,当定时器中断时,由于ms_counter变量的字节级操作导致判断错误,使声音过早停止。作者通过深入分析中断处理和多字节变量读取,揭示了问题的根源并提供了解决方法,包括使用volatile关键字和临时变量避免共享冲突。
摘要由CSDN通过智能技术生成

当时开发的一个产品,一项功能是在通电后播放40秒的语音.
测试时发现,大约通电70-80次就有一次播放时间不够40秒就提前停止。
当时以为复位有问题,换了复位片,没好。又先后换了CPU,语音芯片,还有电源,都没有好转。排除了硬件芯片原因导致的此现象.
后来又从软件中查找原因。反复查找软件逻辑,也没发现问题。后来偶然发现在主while里增加大量延时后,稳定性提高。
几乎不再出现问题。但是我还是觉得不对劲,用了两天时间终于找到了原因。因为这是公司的程序,所以不能贴源码。
我把其他程序都略去,只把出错的程序大概写一下。大家看看能找到问题吗?

unsigned int ms_counter;                        
void T0()  
{         
    //定时器程序每100毫秒中断一次,程序略         if (ms\_counter<1000) 	  ms_counter++;  
}  
void main(void)  
{  
//初始化定时器程序每100毫秒中断一次,程序略                           unsigned char tt;  
    ms_counter=0;  
    tt=0;//用tt控制只响一次    
    while(1)  
    {  
        if (ms_counter<400)  
        {  
            if (tt==0)  
            {  
            tt=1;  
            Sound_on();  
            }  
        }  
        else  
        {  
            Sound_off();  
        }     
    		//其他程序    //。。。。。。        
    }  
}

高手们不要笑,菜鸟们坐好
问题出在ms_counter不到400时,程序提前执行了Sound_off();
原因分析:if (ms_counter<400)中的ms_counter是两字节的整型,而且在中断里有增一操作。
这就有一种错误的可能
if (ms_counter<400) //被编译器翻译成以下语句

+0000007C:   E9E0        LDI     R30,0x90         Load immediate  
+0000007D:   E0F1        LDI     R31,0x01         Load immediate  
+0000007E:   164E        CP      R4,R30           Compare  
+0000007F:   065F        CPC     R5,R31           Compare with carry  
+00000080:   F428        BRCC    +0x05            Branch if carry cleared  

在ms_counter==255时 R4是255 R5是0

CP R4,R30 ;这时R4是255 注意!如果在这两条语句中间产生了中断 ms_counter增一 以后 R4是0 R5是1
CPC R5,R31 ;这时R5是1

简单的说是由于在整型数增一进位的时候,又受到中断的影响。
本来正确值 0x00ff或0x0100(ms_counter),
实际错误值 0x01ff(ms_counter) 先判断低位时低位是FF,中断后判断高位时高位是01
ms_counter在255时被误认为511(0x01ff)导致提示音提前关闭。

当主函数与中断函数共用变量时,可能发生:

1.主函数对变量的读/写,可能造成中断函数对变量的读/写无效。

如:当主函数刚刚把变量读入到内部寄存器时,还未再回写到变量中 时,发生中断,中断中改写了变量。当中断返回时,主函数将值再回写到变量中。造成中断函数对变量的改写无效。

2.多字节变量读取错误。

如:当变量的其中一个字节读入到寄存器中时,发生中断,中断中改写了变量值。当中断返回时,变量的其他字节继续被读入到寄存器中,造成新旧字节组合错误。

主函数与中断函数共享变量问题类似两个线程共享资源的问题,如何解决共享资源冲突是系统结构设计的关键

解决方法

  1. volatile正确使用.
  2. 注意临界段(或原子操作). (写变量时,关中断,写完后再开)
  3. 操作系统中对这种问题有另一种解决办法,即引入一个与ms_counter相同类型的临时变量:
unsigned int tmp_counter;  
//在使用ms_counter做判断前作如下操作:
//排除中断前后新旧字节组成的错误值 ,确保本次取值操作期间没有被中断
do {  
  tmp_counter = ms_counter;  
}while (tmp_counter != ms_counter);  

然后使用tmp_counter代替ms_counter进行判断,这样可以保证回避楼主所述问题。

一般说来,volatile用在如下的几个地方:

1)、中断服务程序中修改的供其它程序检测的变量需要加volatile;

2)、多任务环境下各任务间共享的标志应该加volatile;

3)、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;

---------------------
作者:zyboy2000
来源:CSDN
原文:https://blog.csdn.net/zyboy2000/article/details/4166881
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值