当时开发的一个产品,一项功能是在通电后播放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.注意临界段(或原子操作). (写变量时,关中断,写完后再开)
操作系统中对这种问题有另一种解决办法,即引入一个与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说明,因为每次对它的读写都可能由不同意义;
关键字:主函数 中断函数 共享变量
引用地址:
当主函数与中断函数共享变量问题
推荐阅读最新更新时间:2024-03-16 14:59
STM32F429之中断服务函数名
以下内容在startup_stm32f429_439xx.s文件内 ;******************** (C) COPYRIGHT 2015 STMicroelectronics ******************** ;* File Name : startup_stm32f429_439xx.s ;* Author : MCD Application Team ;* @version : V1.5.0 ;* @date : 06-March-2015 ;* Description : STM32F429xx/439xx devices vector table for
[单片机]
stm32 中断函数名称表
stm32 中断服务函数的名称有点特别,它们的名称是固定的, 但是表面看起来又好像没什么规律, 还是老办法,写下来死记了。 WWDG_IRQHandler PVD_IRQHandler TAMPER_IRQHandler RTC_IRQHandler FLASH_IRQHandler RCC_IRQHandler EXTI0_IRQHandler EXTI1_IRQHandler EXTI2_IRQHandler EXTI3_IRQHandler EXTI4_IRQHandler DMA1_Channel1_IRQHandler DMA1_Channel2_IRQHan
[单片机]
共用中断和共用中断函数的判断
STM32外部中断查询: 15-10线的外部中断共用一个中断函数,怎么在该函数里查询是哪个中断线产生了中断呢? 使用 EXTI_GetITStatus()来查询哪根线产生了中断。比如EXTI_GetITStatus(EXTI_Line13)就是查询13线是否产生了中断的。 还有 :当初你在设置中断引脚的时候都是不能把PB1和PD1同时设置为中断源的。如果非要设置,后面设置的会把前面的覆盖掉的。 编写中断服务函数经常需要使用两个函数。 第一个是判断某个中断线上的中断是否发生(即标志位是否置位): ITStatus EXTI_GetITStatus(uint32_t EXTI_line); //放在中断服务函数开头,
[单片机]
PIC C语言编程_PICC中断函数的实现
PICC可以实现C语言的中断服务程序。中断服务程序有一个特殊的定义方法: voidinterruptISR(void); 其中的函数名“ISR”可以改成任意合法的字母或数字组合,但其入口参数和返回参数类型必须是“void”型,亦即没有入口参数和返回参数,且中间必须有一个关键词“interrupt”。 中断函数可以被放置在原程序的任意位置。因为已有关键词“interrupt”声明,PICC在最后进行代码连接时会自动将其定位到0x0004中断入口处,实现中断服务响应。编译器也会实现中断函数的返回指令“retfie”。一个简单的中断服务示范函数如下: voidinterruptISR(void)//中断服务程序 {
[单片机]
STM32中断函数名
和普通的ARm裸板开发不同。使用stm32库函数编程时,中断函数名是固定死的。 具体函数名可以在启动文件startup_stm32fxxx_xd.s的Vector Table Mapped(中断函数向量表)中查看。 启动文件 startup_stm32fxxx_xd.s里面都是汇编代码 ; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY EXPORT __Vectors EXPORT __Vectors_End EXPORT __Vectors_Size __V
[单片机]
单片机avr 中断寄存器 中断c语言函数 外部中断介绍
简介:中断的概念,ATmega16中断向量表,avr中断寄存器SREG,MCU控制和状态寄存器MCUCSR,通用中断控制寄存器GICR, MCU控制寄存器MCUCR,ICCAVR中断函数格式介绍 中断就是暂停正在执行的程序语句,转去执行另一功能(函数)的程序语句,执行完后,立即返回原先暂停执行的语句处,继续执行。单片机的中断系统解决了单片机运算速度快,外设(如键盘)速度慢之间的数据传输问题,提高了单片机的实时性和数据处理能力。 中断源:产生中断的地方,ATmega16具有20个中断源和一个复位中断,不同的单片机中断源的个数是不一样的。单片机的中断源分为外部中断源和内部中断源,有三个外部中断源有INT0,INT1,INT2,当连接在
[单片机]
STM32是如何进入中断函数xxx_IRQHandler的
STM32是如何进入中断函数xxx_IRQHandler的,如:void USART1_IRQHandler(void) 前段时间开始接触STM32的时候遇到这样一个问题,程序里面配置好中断设置时,程序运行的时候是怎么进入中断函数的(当然这里所说的中断都是硬件中断),因为跑C程序的时候,本人的理解是,你总得有个“接力棒”或者一个“入口”吧,它才能进入到下一个函数中去。 以uart1配置作为例子, //中断初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStru
[单片机]