请教: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 ; //返回键值
}
请大家帮分析下原因。谢谢! 补充下,用的是KEIL MDK 环境。 第一个问题找到了答案:为什么静态变量声明语句调试时单步执行会直接跳过。
参考这篇帖子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函数之前就已经
被初始化了。 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是多少,然后单步执行,找出问题。 谢谢马老师,这两个问题都解决了。其实switch语句还是执行了的,只不过单步执行时,没有一行一行的显示执行过程,直接就一次执行完,直接跳出switch了,记得用51单片机仿真时,可以一步一步执行switch的。
现在已经完成了基本的IO实验,HT1621显示,Timer0中断10MS键盘扫描。
正在做PWM和基于热敏电阻的AD实验。
准备用这个进入32位时代。
不过,学起来真费劲,手册里很多不详细,自己不太喜欢给的库那种方式,喜欢寄存器方式,但它的寄存器库方式也不方便,我习惯SYSCLK->CLKSEL0.XX这种方式。 目录里application note这个目录里有些how to指南就是这种方式。
页:
[1]