fomula 发表于 2010-11-22 22:47:47

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

水平有限,用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 ; //返回键值
                  
}
请大家帮分析下原因。谢谢!

fomula 发表于 2010-11-23 02:04:18

补充下,用的是KEIL MDK 环境。

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

12:       static int a = 3;               //对应着main函数中的声明,此时EBP = 0x0012FFC0
13:       int b = nG;
00404BB8   mov         eax,   //这两句给 b赋值 ,可以发现全局变量 nG 的地址是:0x00476F90
00404BBD   mov         dword ptr ,eax // b 的地址是 0x0012FFBC
14:       int c = a;
00404BC0   mov         ecx,dword ptr //这两句是给 a赋值,可以发现局部静态变量a的地址是
00404BC6   mov         dword ptr ,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 //局部静态变量 oo的地址是 0x00476F9c
00404BD4   mov         dword ptr ,edx
18:       return 0;
00404BD7   xor         eax,eax
19:   }

Fun的汇编代码:
7:      static gg = 2;
8:       intlocal = gg;
004051B8   mov         eax,//这两句在给local赋值,可以发现 局部静态变量 gg的地址是
004051BD   mov         ,eax   0x00476F94
9:    }
由上述可以发现 变量 nGggaoo它们几个的内存地址很相近
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函数之前就已经
被初始化了。

machao 发表于 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是多少,然后单步执行,找出问题。

fomula 发表于 2010-11-27 02:32:26

谢谢马老师,这两个问题都解决了。其实switch语句还是执行了的,只不过单步执行时,没有一行一行的显示执行过程,直接就一次执行完,直接跳出switch了,记得用51单片机仿真时,可以一步一步执行switch的。

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

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

不过,学起来真费劲,手册里很多不详细,自己不太喜欢给的库那种方式,喜欢寄存器方式,但它的寄存器库方式也不方便,我习惯SYSCLK->CLKSEL0.XX这种方式。 目录里application note这个目录里有些how to指南就是这种方式。
页: [1]
查看完整版本: 请教:M051单步执行总是跳过静态变量的声明,switch语句进不去