单片机变量自动更改_单片机怎么让局部变量变成全局变量 - CSDN
  • 最近一个项目里面,在KEIL中用C语言在单片机里面定义了一个状态机全局变量,这个变量随时会改变,用于切换触摸屏的界面, 可是程序运行中出现了一个问题,这个状态机号总是出现了被莫名奇妙改变的问题,导致触屏不...

    最近一个项目里面,在KEIL中用C语言在单片机里面定义了一个状态机全局变量,这个变量随时会改变,用于切换触摸屏的界面,

    可是程序运行中出现了一个问题,这个状态机号总是出现了被莫名奇妙改变的问题,导致触屏不能正常跳转到预设的界面,

    经过检查分析,可能有两个原因:

    •)全局变量与局部变量同名重复定义。有可能是你定义了一个与全局变量一样名字的局部变量,并在子程序里面修改了变量值。

    •)缺少volatile修饰符。C语言中volatile修饰符不常用,它的作用是明确该变量是变化的,防止编译器对该变量进行优化,让处理器在运行时每次都从内存中读取数据,而不是用寄存器中的备份数据。此修饰命令建议仅对全局有重要作用的关键变量使用。

    用法:定义时:volatile unsigned char a;//定义了一个字符变量a

    经检查是第2个原因,在程序中定义变量时添加volatile修饰符后问题解决。

    关注我,分享科技开发、高效思维、理财等的点点滴滴。微信公众号:yonkotech

    展开全文
  • SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。...单片机编译器中局部变量和全局变量的深入解析   ...

    SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。SYD8801片上集成了Balun无需阻抗匹配网络、高效率DCDC降压转换器,适合用于可穿戴、物联网设备等。具体可咨询:http://www.sydtek.com/

     

    单片机编译器中局部变量和全局变量的深入解析

     

    本节博客摘录于:http://blog.sina.com.cn/s/blog_4c2e13ff0101e3gr.html

    通常我们都是学了标准c语言教程后从事单片机c语言的编写的, 那就先要明白一点,标准c语言实际上是起源于pc平台上的一种语言, 标准c语言肯定是不会照顾到单片机的特殊性的.因此单片机c编译器中的c语言是一种基于标准c,但是又有相应修改扩充的扩展c语言. 

         在标准c里,局部变量是函数在调用的时候才临时分配存储空间的,全局变量是程序整个生命周期都一直存在的. 不过要知道,临时分配存储空间是需要操作系统内存管理程序支持的,单片机中通常都没有操作系统,也就不能实现像pc平台中那样的局部变量的空间分配. 这里就需要深入了解一下单片机的c编译器究竟是如何处理局部变量的,如果对此没有概念,碰到调试过程中的一些奇异现象恐怕只能觉匪夷所思了.

         另外需要知道的一点是, 不同的编译器对于局部变量的处理方法也不一样, 不能学了一个就到处照搬. 这里拿KEIL C ,IARAVR,ICCAVR这个三个编译器做分析比较.

     

         

          首先说 Keil C51 , 它的局部变量并不是在堆栈中, C51 为了提高代码的效率, 根据 51处理器的特性. 编译器对函数局部变量的安排进行了处理.局部变量如果不能分配到 寄存器里, 就放在 RAM 中了.编译器通过覆盖分析,可以共享局部变量的地址空间.。 最终的DATA使用量取决于调用链中那个使用DATA最多的链。所以,在程序中增加一个局部变量,如果不是位于那个使用DATA最多的链中,需要的DATA数量是有可能不会增加的。

         如:main()->f11()->f12()->f13().... // 链1
                 |----->f21()->f22()->f23().... // 链2

         因为f11(),f21()不在同一个调用链上,显然,f11()中使用的局部变量,可以和f21()中的局部变量,使用同一个存储单元。因为它们中的任何一个处在生命期内的话,另一个必然已经离开它的生命周期,同时它的局部变量也离开了它的生命周期,这些局部变量所占用的存储单元当然可以另做它用了。
         假设链1目前的局部变量需要50个存储单元,链2需要40个存储单元。那么你在链2中加入不多于10个单元的局部变量的话,程序最终需要的存储单元数量是不会增加的。

     

     

          再说ICCAVR ,  它把局部变量存放在软件堆栈空间中. ICCAVR使用两个堆栈:一个用于子程序调用和中断操作的硬件堆栈,一个用于传递参数、临时变量和局部变量的软件堆栈。硬件堆栈是从数据内存的顶部开始分配的,在硬件堆栈下面再分配一定数量的字节作为软件堆栈。

          IARAVR对于局部变量的处理方法与ICCAVR一样. 它也有两个堆栈,一个是data stack ,一个是returnaddress stack.  分别用于存放临时变量,局部变量,传递参数, 和函数返回地址.

          这里需要注意的是, 局部变量存放在堆栈中的处理方式一定要保证堆栈足够大,特别是定义了局部数组变量的情况下,一旦数组过大,超过了堆栈大小就会发生堆栈溢出, 如果只是读取数据还好, 一旦写入数据,就会破坏堆栈空间以外的数据, 导致程序时常.

    所以在单片机c编译器里写程序时一定要了解单片机编译器扩展c语言的不同之处, 绝不能死板地照搬标准c.

     

     

    另外对于编译而言有这些规则:(摘录于:https://blog.csdn.net/chengdong1314/article/details/78663785

    r0-r3    用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。

                被调用函数在返回之前不必恢复 r0-r3。如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。

    r4-r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。

    r12        是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。
                 

    在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。

    13        是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。

    r14        是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复

    r15        是程序计数器 PC。它不能用于任何其它用途。
         

    注意:在中断程序中,所有的寄存器都必须保护,编译器会自动保护R4~R11

     

    展开全文
  • 在写stm32代码时,定义了一个全局变量i,但是在串口中断赋值一次之后,进行显示i,结果发现i的值发生了变化,具体为什么这样我不是很清楚,也许是定义的变量被优化了,也许是其他原因。 解决办法是:在定义变量的...

    在写stm32代码时,定义了一个全局变量i,但是在串口中断赋值一次之后,进行显示i,结果发现i的值发生了变化,具体为什么这样我不是很清楚,也许是定义的变量被优化了,也许是其他原因。
    解决办法是:在定义变量的前面加上 volatile

    展开全文
  • 去年写的文章自己也看不懂了,很粗糙,今天重新整理下。 现象:工作中遇到一个大坑,STC11F32设置的运行灯闪烁周期为500ms,大多数是500ms低电平和500ms高电平,但偶尔... 函数中定义的变量为16位,如果定义为l...

       去年写的文章自己也看不懂了,很粗糙,今天重新整理下。

       现象:工作中遇到一个大坑,STC11F32设置的运行灯闪烁周期为500ms,大多数是500ms低电平和500ms高电平,但偶尔运行灯会有很快熄灭或很快点亮的情况,肉眼观察到运行灯闪烁不均匀,用示波器观察时发现:有40ms左右的高电平或低电平出现,对,就这么简单的一个程序,奇哉怪哉!

        函数中定义的变量为16位,如果定义为long型32位,变量的存储运算部分翻译成汇编代码会更长些。

    
    unsigned int cnt_1ms; //定义全局变量:ms计数器
    unsigned int cnt_1ms_pre; //定义全局变量:ms计数器备份
    sbit LED = P3 ^ 3; //定义LED对应的IO口
    
    //主循环
    int main(void)
    {
        ......
    
        if ((cnt_1ms - cnt_1ms_pre) > 500)
        {
            led = ~led;
            cnt_1ms_pre = cnt_1ms;
        }
    
        ......
    }
    
    //定时1ms中断
    void timer0_interrupt(void) interrupt 1
    {
        ......
    
        TL0 = 0x0cd; //装在1ms定时初值
        TH0 = 0x0f8;
    
        cnt_1ms++;
    
        ......
    }

    main函数中的代码汇编语言如下:

        81:              if ((cnt_1ms - cnt_1ms_pre) >= 500)               //闪灯 
    C:0x041F    900163   MOV      DPTR,#cnt_1ms_pre(0x0163)
    C:0x0422    E0       MOVX     A,@DPTR
    C:0x0423    FE       MOV      R6,A
    C:0x0424    A3       INC      DPTR
    C:0x0425    E0       MOVX     A,@DPTR
    C:0x0426    FF       MOV      R7,A
    C:0x0427    900154   MOV      DPTR,#cnt_1ms(0x0154)
    C:0x042A    E0       MOVX     A,@DPTR
    C:0x042B    FC       MOV      R4,A
    C:0x042C    A3       INC      DPTR
    C:0x042D    E0       MOVX     A,@DPTR
    C:0x042E    FD       MOV      R5,A
    C:0x042F    C3       CLR      C
    C:0x0430    9F       SUBB     A,R7
    C:0x0431    FF       MOV      R7,A
    C:0x0432    EC       MOV      A,R4
    C:0x0433    9E       SUBB     A,R6
    C:0x0434    FE       MOV      R6,A
    C:0x0435    C3       CLR      C
    C:0x0436    EF       MOV      A,R7
    C:0x0437    94F4     SUBB     A,#0xF4
    C:0x0439    EE       MOV      A,R6
    C:0x043A    9401     SUBB     A,#0x01
    C:0x043C    400A     JC       C:0448
        82:         { 
        83:                         cnt_1ms_pre =  cnt_1ms; 
    C:0x043E    900163   MOV      DPTR,#cnt_1ms_pre(0x0163)
    C:0x0441    EC       MOV      A,R4
    C:0x0442    F0       MOVX     @DPTR,A
    C:0x0443    A3       INC      DPTR
    C:0x0444    ED       MOV      A,R5
    C:0x0445    F0       MOVX     @DPTR,A
        84:                         MCU_LED = ~MCU_LED; 
    C:0x0446    B2B3     CPL      MCU_LED(0xB0.3)
        85:                 } 

    中断部分的汇编如下:

       200: void timer0_interrupt(void) interrupt 1 
    
       205:          cnt_1ms++; 
    C:0x0C48    900155   MOV      DPTR,#0x0155
    C:0x0C4B    E0       MOVX     A,@DPTR
    C:0x0C4C    04       INC      A
    C:0x0C4D    F0       MOVX     @DPTR,A
    C:0x0C4E    7006     JNZ      C:0C56
    C:0x0C50    900154   MOV      DPTR,#cnt_1ms(0x0154)
    C:0x0C53    E0       MOVX     A,@DPTR
    C:0x0C54    04       INC      A
    C:0x0C55    F0       MOVX     @DPTR,A

      *.M51 文件中变量的地址如下:

    ......
      
    X:0154H         PUBLIC        cnt_1ms
    
    .......
    
    X:0163H         PUBLIC        cnt_1ms_pre

     

        看看main中的汇编代码,在做减法运算时先取cnt_1ms_pre,然后取cnt_1ms的低地址值给R4,再取高地址值给R5,所有取值时都是16位操作。问题来了:如果先取了cnt_1ms的高地址值,然后发生了中断,中断程序修改了cnt_1ms的值,中断返回后再取低地址值,这是减法的结果就不一定是你想要的结果了。 

        假设在减法运算中发生中断,中断前cnt_1ms_pre的值为0x0000,cnt_1ms的值为0x00FF,先取cnt_1ms的低地址值0xFF装载R4,本打算取cnt_1ms的高地址值0x00装载R5,结果被中断打断,中断后cnt_ms++,变量cnt_1ms值变为0x0100,中断执行完返回主循环继续装载R5,结果给R5装载值0x01,各位现在主循环中使用的cnt_ms为0x01FF了,这样减法操作后的比较条件成立,此时就会造成脉冲的变窄。 

        我起先老纠结中断时会压栈,会保存现场。实际中断时保存的是ACC, B, DPTR等寄存器。全局变量cnt_1ms被保存到了XDATA区,如果第二次装载寄存器之前其地址的值发生变换后,就可能造成错误。

        解决方法:主循环程序修改如下:用示波器观察led的闪烁,结果正常。

    
    unsigned int cnt_1ms; //定义全局变量:ms计数器
    unsigned int cnt_1ms_pre; //定义全局变量:ms计数器备份
    sbit LED = P3 ^ 3; //定义LED对应的IO口
    
    //主循环
    int main(void)
    {
        unsigned int cnt_1ms_bk; //定义全局变量:ms计数器备份  
    
        ......
    
        EA = 0;
        cnt_1ms_bk = cnt_1ms;
        EA = 1;
    
        if ((cnt_1ms_bk - cnt_1ms_pre) > 500)
        {
            led = ~led;
            cnt_1ms_pre = cnt_1ms_bk;
        }
    
        ......
    }
    
    //定时1ms中断
    void timer0_interrupt(void) interrupt 1
    {
        ......
    
        TL0 = 0x0cd; //装在1ms定时初值
        TH0 = 0x0f8;
    
        cnt_1ms++;
    
        ......
    }

        总结:由于全局变量的减法操作为16位,就分成了2次8位操作,在没有取出高地址的字节前发生中断,修改该全局变量,都会导致不良后果。

        取值操作过程分析:汇编还是要多步执行的,要是32位或16位操作的步骤会少但是一样存在问题。

    //part1:取低地址值
    MOV      DPTR,#cnt_1ms(0x0154)  //指向低地址
    MOVX     A,@DPTR                //存到寄存器A
    MOV      R4,A                   //从寄存器A搬到R4
    
    //part2:取高地址值
    INC      DPTR                   //指向高地址(地址自增)
    MOVX     A,@DPTR                //取高地址值存到寄存器A
    MOV      R5,A                   //从寄存器A搬到R5

     

     

          

     

     

     

       

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 关于标题就一句话,遇到类似的问题,查下是否定义了一个和全局变量同类型同名的局部变量,又对这个局部变量做了操作。

    关于标题就一句话,遇到类似的问题,查下是否定义了一个和全局变量同类型同名的局部变量,又对这个局部变量做了操作。

    展开全文
  • 引言 8051内核单片机是一种通用单片机,在国内占有较大的市场份额。在将C语言用于51内核单片机的研究方面,Keil公司做得最为成功。由于51内核单片机的存储结构的特殊性,Keil C51中变量的使用与标准C有所不同。正确...
  • 基础概念 单片机有寄存器和内存。...RAM:随机存储器,可以简单理解为变量都存这里。 暂且写这样理解,初始化后会有变化,后文会说。 单片机都有自带ROM/FLASH、RAM,但是很多厂商也会给他们外...
  • 变量&栈变量

    2012-04-19 10:39:47
    全局,静态,new产生的变量都在堆中 动态分配的变量在堆中分配  局部变量在栈里分配 函数中声明的变量在栈中  用了new标示符在堆中  全局变量和static变量都在全局区 程序为栈变量分配动态内存,...
  • 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的...
  • 2018年的春节是一个很特别的一段时光,留在外地没有回老家过年。其实一开始也过得很是无聊,天天睡到自然醒——看看电视、玩玩游戏——随便无规律的吃点。呵呵……就这样任性的堕落。 俗话说得好啊!...
  • 在使用IAR调试程序的时候,发现我定义的结构体全局变量即使我不去对他进行赋值,他的成员数值也在不断的变化,使得整个程序运行出现问题。 此问题没有完全了解清楚,只是暂时解决,附上问题...
  • 全局变量是否占用最终程序的存储空间,这个问题其实早在我们学习C语言的时候就已经告诉我们答案了。我隐约记得初学C语言的时候,书本上告诉我们: 全局自动变量——保存在读写数据段 全局静态变量——保存在读写...
  • 第一个问题:调试时候想实时查看某一变量变化? ...比如要查看u8 adcx的值变化,如下: 全速运行后,如下: 无法查看!...该变量非全局变量!...必须是当前断点处所在的函数里面有这个参数才行,...将变量adcx为全局变量
  • 提高单片机运行效率

    2019-08-03 18:01:18
    定义变量 条件编译 C、汇编混编 2、如何提高效率 保障时效性 精简代码 变量类型 宏定义 定义变量 条件编译 C、汇编混合编译 1、影响效率的因素 使用延时函数 代码精简 宏定义 数据类型...
  • 在早期的编译器中需要手动定义为register型,但是后来编译器可以自动将调用次数多的变量放入寄存器中。 auto:给变量动态分配内存,默认的分配类型。一般不需要手动声明; static:静态分配内存。变量在整个作用域...
  • 若定义某些会在中断中修改的全局变量,这时要注意两个问题:首先为了防止编译器优化中断变量,要在这些变量定义时前加volatile,其次在主循环中读取中断变量前应该首先关闭全局中断,防止读到一半被中断给修改了,读...
  • [第五章]函数对变量的作用一个程序中的变量包括两类: 1. 全局变量:全局变量指在函数之外定义的变量,在程序执行全过程有效。 2. 局部变量 : 局部变量指在函数内部使用的变量,仅在函数内部有效,当函数退出时...
  • 作者:cy757 转自:http://blog.csdn.net/cy757/article/details/5110872 转自... 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这
  • 静态局部变量和全局变量的区别!
  • SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。...函数里修改传入变量的值不会改变传入变量的指针  ...
1 2 3 4 5 ... 20
收藏数 5,766
精华内容 2,306
关键字:

单片机变量自动更改