搜索
bottom↓
回复: 4

请教:M051单步执行总是跳过静态变量的声明,switch语句进不去

[复制链接]

出0入0汤圆

发表于 2010-11-22 22:47:47 | 显示全部楼层 |阅读模式
水平有限,用M051板调试按键程序时遇到奇怪的问题:
1、静态变量的声明语句直接跳过去,在给的库所带的例子中加入相似的语句也是这样。
2、用马老师状态机思想编的按键扫描程序,switch死活进不去,总是执行到switch后,就直接跳出按键扫描函数。

程序如下:
//定义长按键的TICK数,以及连_发间隔的TICK数
#define KEY_LONG_PERIOD        100
#define KEY_CONTINUE_PERIOD    25

//定义按键返回值状态(按下,长按,连_发,释放)
#define KEY_DOWN                0x80
#define KEY_LONG                0x40
#define KEY_CONTINUE            0x20
#define KEY_UP                  0x10

//定义按键状态
#define KEY_STATE_INIT            0
#define KEY_STATE_WOBBLE          1
#define KEY_STATE_PRESS           2
#define KEY_STATE_LONG            3
#define KEY_STATE_CONTINUE        4
#define KEY_STATE_RELEASE         5
........................省略...........

uint8_t GetKey(void)
{
      static uint8_t s_u8KeyState = KEY_STATE_INIT ;
      static uint8_t s_u8KeyTimeCount = 0 ;
      static uint8_t s_u8LastKey = KEY_NULL ;  //保存按键释放时候的键值

//    uint8_t s_u8KeyState = KEY_STATE_INIT ;
//    uint8_t s_u8KeyTimeCount = 0 ;
//    uint8_t s_u8LastKey = KEY_NULL ;  //保存按键释放时候的键值
//    uint8_t KeyTemp = KEY_NULL ;


    KeyTemp = u8_Read74hc165_f() ;        //获取键值
       
             
    switch(s_u8KeyState)
    {
        case KEY_STATE_INIT :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        s_u8KeyState = KEY_STATE_WOBBLE ;
                    }
                                }
               
        break ;

        case KEY_STATE_WOBBLE :      //消抖
                {
                    s_u8KeyState = KEY_STATE_PRESS ;   
                }
        break ;

        case KEY_STATE_PRESS :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        s_u8LastKey = KeyTemp ; //保存键值,以便在释放按键状态返回键值
                        KeyTemp |= KEY_DOWN ;  //按键按下
                        s_u8KeyState = KEY_STATE_LONG ;
                    }
                    else
                    {
                        s_u8KeyState = KEY_STATE_INIT ;
                    }
                }
        break ;

        case KEY_STATE_LONG :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        if(++s_u8KeyTimeCount > KEY_LONG_PERIOD)
                        {
                           
                                                        s_u8KeyTimeCount = 0 ;
                            KeyTemp |= KEY_LONG ;  //长按键事件发生
                            s_u8KeyState = KEY_STATE_CONTINUE ;
                        }
                    }
                    else
                    {
                        s_u8KeyState = KEY_STATE_RELEASE ;
                    }
                }
        break ;

        case KEY_STATE_CONTINUE :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD)
                        {
                            s_u8KeyTimeCount = 0 ;
                            KeyTemp |= KEY_CONTINUE ;
                        }
                    }
                    else
                    {
                        s_u8KeyState = KEY_STATE_RELEASE ;
                    }
                }
        break ;

        case KEY_STATE_RELEASE :
                {
                    s_u8LastKey |= KEY_UP ;
                    KeyTemp = s_u8LastKey ;
                    s_u8KeyState = KEY_STATE_INIT ;
                }
        break ;

        default : break ;
    }
    return KeyTemp ; //返回键值
                    
}
请大家帮分析下原因。谢谢!

出0入0汤圆

 楼主| 发表于 2010-11-23 02:04:18 | 显示全部楼层
补充下,用的是KEIL MDK 环境。

出0入0汤圆

 楼主| 发表于 2010-11-23 11:53:37 | 显示全部楼层
第一个问题找到了答案:为什么静态变量声明语句调试时单步执行会直接跳过。
参考这篇帖子http://www.cnblogs.com/avril/archive/2010/10/19/1855878.html
先看一小段程序:


#include<iostream>
using namespace std;

int nG = 1;
void Fun( )
{
    static gg = 2;
    int local  = gg;
}
int main( )
{                                      
    static int a = 3;
    int b = nG;
    int c = a;
    Fun( );
    static oo = 4;
    c = oo;
    return 0;   
}
     
    我们所关心的是这些变量如 nG  gg  a  等等在内存中的分布如何?它们都紧挨着放在一块?还是各有个的存储块。  
    为了解决上述问题,首先提出第一个问题?
    Fun()中的局部静态变量 gg 在多次调用Fun()后(比如2次 )的值是多少?你肯定毫无犹豫地答到:  2
    我如果问为什么?你可能毫无犹豫的答道:局部静态变量 gg  只是在第一次进入函数时被初始化。
    变量 gg 真的是在第一次进入 函数时才被初始化的吗??!!
    我们可以单步调试跟踪一下,你会发现调试时会直接跳过  static  gg = 2;这一行。
    因此我说变量 gg 在进入Fun函数之前就已经存在了,你信吗?
    我们可以将断点插在  1  处 ,调试  Alt  + 8 进入底层的汇编代码

12:       static int a = 3;               //对应着main函数中的声明,此时EBP = 0x0012FFC0
13:       int b = nG;
00404BB8   mov         eax,[nG (00476f90)]   //这两句给 b  赋值 ,可以发现全局变量 nG 的地址是:0x00476F90
00404BBD   mov         dword ptr [ebp-4],eax // b 的地址是 0x0012FFBC
14:       int c = a;
00404BC0   mov         ecx,dword ptr [nG+8 (00476f98)]  //这两句是给 a赋值,可以发现局部静态变量a的地址是
00404BC6   mov         dword ptr [ebp-8],ecx                     // 0x00476F98,c的地址是:0x0012FFB8
15:       Fun( );
00404BC9   call        @ILT+790(Fun) (0040131b)   --------看下面 Fun的汇编代码
16:       static oo = 4;
17:       c = oo;
00404BCE   mov         edx,dword ptr [nG+0Ch (00476f9c)]  //局部静态变量 oo的地址是 0x00476F9c
00404BD4   mov         dword ptr [ebp-8],edx
18:       return 0;
00404BD7   xor         eax,eax
19:   }

Fun的汇编代码:
7:        static gg = 2;
8:       int  local = gg;
004051B8   mov         eax,[nG+4 (00476f94)]//这两句在给local赋值,可以发现 局部静态变量 gg的地址是
004051BD   mov         [nG (00476f90)],eax     0x00476F94
9:    }
由上述可以发现 变量 nG  gg  a  oo它们几个的内存地址很相近
0x00476F90:-----------------nG的地址
0x00476F94:-----------------gg的地址
0x00476F98:-----------------a   的地址
0x00476F9C:----------------oo的地址
我们可以发现这些全局变量和静态变量的内存地址,是在一块的。
总结:
  我们通常都说main函数是程序的入口点,所以就很容易造成一种误解。认为程序一运行就进入了main,这是
不对的。既然main是函数,是函数就得被调用了才能执行。那么是谁调用了main函数呢?
  通过调用堆栈我们发现,在main函数前还有一个函数:mainCRTStartup()。这个函数通常被称为启动代码,
  由他对程序在进入main函数之前进行初始化(如加载动态链接库分配资源等等),毕竟程序的正常运行不是
仅仅依靠小小的main就行的。当然初始化过程中包含对全局变量和静态变量的内存分配。这里的全局变量可能好理解,但是静态变量就有点难理解了。比如Fun中的 gg 他是一个局部静态变量啊 为什么会在进入main函数之前就被初始化啦。这就是静态变量的特性,这就是通常我们为什么说静态变量的生命周期,和整个程序的生命周期是一样的。
下面简要的说一下本程序的运行流程:
  首先由启动代码完成一些必要资源的加载,和全局变量、静态变量(你程序中的所有静态变量,包括局部的)
  初始化,分配内存单元。
  进入main函数,首先进行的操作是在栈上分配一块内存单元,用来分配函数中的局部变量(除了静态变量)和
  参数的压栈以及函数地址的压栈。也就是说上面的main中的局部非静态变量 b  和   c就分配在此 内存块中,它   会在main函数返回后释放。
   同理在进入Fun函数时,首先也会在栈上分配一块内存单元,用来分配Fun函数中的局部变量(除了静态变       量)和参数的压栈以及函数地址的压栈。比如Fun中的local就分配在此段内存块中,它会在Fun函数返回以后
   被释放。
    最后return 0返回,释放分给main函数的内存块,main函数结束以后由mainCRTStartup( )完成全局变量和
   静态变量的释放,毕竟是由它调用mian函数的。
  最后再强调一下,Fun中的局部静态变量不是第一次进入Fun时被初始化的,而是在进入main函数之前就已经
  被初始化了。

出0入0汤圆

发表于 2010-11-24 02:28:02 | 显示全部楼层
1。不管是8位的AVR、51还是32位STM32、M051,只要采用高级语言C编写代码,系统总是自动加上一段STARTUP代码,它优先于用户的MAIN函数执行,它要做很多的事情,包括堆栈指针分配,变量分配初始化等。

2。全局变量和局部静态变量实际上都是在MAIN执行前就分配好地址并初始化了。局部静态变量根本不可能是“第一次”执行函数时才分配地址和初始化,如果它是在进入本函数才初始化,那么每次调用都要初始化。所以LZ的第1个疑问产生于你对C本身的了解还不够透彻。

3。至于第2个问题,既然你已经在“调试按键程序”,那么在这句后面加上一语句,例如:

  KeyTemp = u8_Read74hc165_f() ;        //获取键值
  
  temp =  s_u8KeyState;

然后在SWITCH这句设置断点,当程序到此语句停下后,查看s_u8KeyState和TEMP是多少,然后单步执行,找出问题。

出0入0汤圆

 楼主| 发表于 2010-11-27 02:32:26 | 显示全部楼层
谢谢马老师,这两个问题都解决了。其实switch语句还是执行了的,只不过单步执行时,没有一行一行的显示执行过程,直接就一次执行完,直接跳出switch了,记得用51单片机仿真时,可以一步一步执行switch的。

现在已经完成了基本的IO实验,HT1621显示,Timer0中断10MS键盘扫描。
正在做PWM和基于热敏电阻的AD实验。

准备用这个进入32位时代。

不过,学起来真费劲,手册里很多不详细,自己不太喜欢给的库那种方式,喜欢寄存器方式,但它的寄存器库方式也不方便,我习惯SYSCLK->CLKSEL0.XX这种方式。 目录里application note这个目录里有些how to指南就是这种方式。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-20 16:28

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表