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

2023-11-08 23:41

本文主要是介绍局部变量,慎用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. 局部变量必须要初始化;

 

 

这篇关于局部变量,慎用volatile (C8051,KEIL)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/373111

相关文章

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据

关键字synchronized、volatile的比较

关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。随着JDK新版本的发布,synchronized关键字的执行效率上得到很大提升,在开发中使用synchronized关键字的比率还是比较大的。多线程访问volatile不会发生阻塞,而synchronize

关键字volatile有什么含意?

1. 并行设备的硬件寄存器。存储器映射的硬件寄存器通常加volatile,因为寄存器随时可以被外设硬件修改。当声明指向设备寄存器的指针时一定要用volatile,它会告诉编译器不要对存储在这个地 址的数据进行假设,也就是要去相应的内存地址里取。 2. 一个中断服务程序中修改的供其他程序检测的变量。volatile提醒编译器,它后面所定义的变量随时都有可能改变。因此编译后的程序每次需要存储或读取这

java内存模型及volatile关键字

文章目录 一、基本概念二、java内存模型三、volatile关键字 一、基本概念 在并发编程过程中,我们经常会遇到三类问题:原子性问题,可见性问题,有序性问题。下面我们来介绍一下和这些问题相关的三个概念。 1.原子性 也就是执行一个操作,要不全部执行成功,要不执行失败。比如a=0,这个操作就是原子性的,要么赋值成功,要么赋值失败。再比如a++操作,这个操作就不是原子性的,它是三

KEIL中编译51程序 算法计算异常的疑问

KEIL开发 51 单片机程序 算法处理过程中遇到的问题 ...... by 矜辰所致 前言 因为产品的更新换代, 把所有温湿度传感器都换成 SHT40 ,替换以前的 SHT21。在 STM32 系列产品上的替换都正常,但是在一块 51 内核的无线产品上面,数据莫名其妙的总会遇到异常的情况,弯弯绕绕了好一阵子,最后才发现是程序在执行一个不算复杂的算法的时候会出错。 那么本文的目的就是说明

Java并发编程--深入理解volatile关键字

前言 一个月以前就准备写篇关于volatile关键字的博客,一直没有动笔,期间看了大量的文章,发现一个小小volatile关键字竟然涉及JMM(Java memory model),JVM(Java virtual machine),Java多线程同步与安全各个方面的知识,写起了非常的困难,后面附带的参考文献仅仅是我看过文献的一部分。 Java memory model(Java内存模型)

ThreadLocal: Java中的线程局部变量

在多线程编程中,确保数据的线程安全是一个重要的考虑因素。Java 提供了多种机制来处理线程安全问题,其中 ThreadLocal 是一种简单而强大的工具,用于创建线程局部变量,从而避免了同步操作的开销。本文将详细介绍 ThreadLocal 的概念、使用场景、最佳实践以及如何避免潜在的问题。 什么是ThreadLocal? ThreadLocal 是 Java 提供的一个类,它允许线程拥有自己

java volatile变量及其使用场景

java中的一种稍弱的同步机制,就是volatile变量,用于确保将变量的更新操作通知到其他线程。 变量声明为volatile后: (1)编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序(重排序不懂的,可以自行百度,需要理解)。 (2)volatile变量不会被缓存在寄存器或对其他处理器不可见的地方 因此volatile变量总是会返回最新的值。

【转】keil(arm)中配置c99方法 及 C99特性

配置方法:option->c/c++->misc controls:--c99 附c99特性: 在ANSI的标准确立后,C语言的规范在一段时间内没有大的变动,然而C++在自己的标准化创建过程中继续发展壮大。《标准修正案一》在1994年为C语言创建了一个新标准,但是只修正了一些C89标准中的细节和增加更多更广的国际字符集支持。不过,这个标准引出了1999年ISO 9899:1999的发表。它通常

【编程底层思考】Java中volatile关键字的使用和作用

在编程中,volatile 关键字是一个类型限定符,它用来告诉编译器,即使这个变量被频繁地访问,也不应该优化掉对它的读写操作。volatile 通常用于多线程编程中,特别是在嵌入式系统或硬件接口编程中,用来确保对某些变量的访问是按照代码的顺序执行的,而不是被编译器重排序。 以下是 volatile 关键字的一些使用场景和作用: 防止编译器优化:编译器在编译代码时,可能会对代码进行优化,以提高程序