局部变量,慎用volatile (C8051,KEIL)

近期一个项目中发现一个问题,偶发性出现,不太好定位:

现象描述:

    当WDT开启时,偶发性出现看门狗复位。在HOST对Module进行I2C 操作时,相对容易触发。

定位问题:

    在while 循环中,针对每个函数执行前和执行后加IO口的操作,用逻辑分析仪是在执行哪个函数时出现的问题。

   经过逐步缩小问题范围,在I2C Slave接收到HOST发的写密码的操作,并且同时正处于Delay(x);执行时,出现概率最大。

    

分析Delay(100);函数:

 

void Delay(uint32_t ulCount)

{

  volatile uint32_t ulCounter = ulCount;

  while(ulCounter)

  {

  ulCounter--;

  }

}

Delay函数目的是实现软件延时。再来看看程序接收到I2C 密码后,会调用下列函数:

uint32_t MemoryGetInputPW(void)

{

   uint32_t ulTmp = 0;

 

   ulTmp |= ucaA0hLower[MMAP_A0H_LOWER_PASSWORD_ENTRY3];

   ulTmp <<= 8;

   ulTmp |= ucaA0hLower[MMAP_A0H_LOWER_PASSWORD_ENTRY2];

   ulTmp <<= 8;

   ulTmp |= ucaA0hLower[MMAP_A0H_LOWER_PASSWORD_ENTRY1];

   ulTmp <<= 8;

   ulTmp |= ucaA0hLower[MMAP_A0H_LOWER_PASSWORD_ENTRY0];

   return ulTmp;

}

利用在线调试功能,可以看到Delay函数中所使用的局部变量ulCounter用的是SRAM 0x3A开始的4个字节:

MemoryGetInputPW函数中的局部变量也是使用的0x3A开始的4个字节,可以看到进入到MemoryGetInputPW函数时,对应的0x3A开始的数据为0x00000007;

执行完了之后,0x3A的数据变为0x462E562E;

由于在Delay的局部变量采用的是volatile 定义方式,编译器编译后会直接访问0x3A的数据,在中断的函数如果修改了0x3A的数据,会导致在返回Delay函数后,其中的ulCounter值会被修改,不巧的话,这个值被改成很大,导致软件延时过长,看门狗复位。

解决办法:

  去掉Delay 函数中局部变量的volatile定义方式,uint32_t ulCounter = ulCount;

  修改之后,编译器会利用R4-R7来存储ulCounter, 而该数据会在进入中断前 压入STACK,得以保护。

  

思考:

 1. volatile 修饰变量,会告知编译器每次都从变量的地址读取数据,而在局部变量上,一般使用后即释放,会有很多函数的局部变量用同一地址的SRAM,如果中断函数改写了该数据,会导致执行异常。

2. 局部变量必须要初始化;

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值