为什么这个状态机识别的if语句放到switch里就出错-出新问题
本帖最后由 biying 于 2014-4-8 01:03 编辑这种一按开关就亮灯:
while (1)
{
switch (status)
{
case 0:
if (key_stime_ok)
key_stime_ok = 0; // 10ms到
if (read_key_n()==2) //若长按2秒
PORTB ^= 1<<2;
break;
}
}
下面这种得长按2秒才亮灯
while (1)
{
if (key_stime_ok)
{
key_stime_ok = 0; // 10ms到
if (read_key_n()==2) //若长按2秒
{
PORTB ^= 1<<2;
}
}
}
怎么同样的if语句,放到switch里面就不一样了?状态机都是一样的代码啊,请懂的朋友指点一下吧!谢谢! 看了几个小时了也找不到原因,没办法只有发上来请懂的朋友指点一下。我是用马潮老师的AVR学习板在练习,状态机代码也是教材里的,我只是想改成用switch语句来判断这个单键密码锁的程序,没想到一开始就卡在了长按开关这里 第一个用switch判断status的状态,要status==0时才执行if语句,第二个则直接执行,你要看status的值是由什么设定的。
想不明白,就这么简单还要想几个钟头? kyughanum 发表于 2014-4-7 02:53
第一个用switch判断status的状态,要status==0时才执行if语句,第二个则直接执行,你要看status的值是由什 ...
谢谢答复!之前我也试过了status==0也是一样的效果,我是想不通,必须状态机返回2(表示长按2秒了),灯才会有反应的,用了switch语句判断后,就只需短按就能让灯反应。你说状态机出问题了吧,可如果切换成用if来判断,就得长按灯才能有反应。到底这个switch语句是如何干扰状态机工作的? 我把源文件发上来,请大家帮我看一下吧! 就是用下面这段代码就正常,也就是长按2秒后灯才有反应 楼主,两个if结构是不一样的啊
你说的正常的第一个if后面有大括弧的,这样每次key_stime_ok==1时,read_key_n()只被执行一次,然后等待定时器再次把key_stime_ok变为1后再执行。就是每次间隔10ms,执行了200次后返回了结果2。
你说的不正常的第一个if后面没有大括弧,就是说他的作用只是当key_stime_ok==1时把他再变为0。后面的read_key_n()在每次while循环都被会被执行,同样是执行200次返回了2,但是间隔很快的,所以看起来是立即产生了输出。
所以还是得有仿真器,或者软件仿真看看程序到底怎么走的,所以我开始用的51,用keil就方便的很了。
你看明白了吗? biying 发表于 2014-4-7 03:05
谢谢答复!之前我也试过了status==0也是一样的效果,我是想不通,必须状态机返回2(表示长按2秒了),灯 ...
LZ,大概看了一下你的代码,switch (status) 这个中的status,只见在第13行的声明:
unsigned char simiao_counter,ermiao_counter,yimiao_counter,key_stime_counter,status;
并没有在其他地方有赋值,C语言里不是先赋值才使用的吗?这应该就是错误的原因吧?
并且,你这个状态,如果没有在其他地方有设置到在状态位,不懂你设置这个状态有什么用? xiaobendan 发表于 2014-4-7 08:27
楼主,两个if结构是不一样的啊
你说的正常的第一个if后面有大括弧的,这样每次key_stime_ok==1时,read_key ...
哈哈,是哦,现在才发现LZ的if (key_stime_ok)根本不一样
这种一按开关就亮灯:
while (1)
{
switch (status)
{
case 0:
if (key_stime_ok)
{
key_stime_ok = 0; // 10ms到
if (read_key_n()==2) //若长按2秒
PORTB ^= 1<<2;
}
break;
}
kyughanum 发表于 2014-4-7 08:41
LZ,大概看了一下你的代码,switch (status) 这个中的status,只见在第13行的声明:
unsigned char simia ...
哈哈,未赋值的变量默认是被清零的,楼主的这个问题和状态机没有关系,和switch没有关系。 switch(a)
{
case 0:
{
}break;
case 1:
{
}break;
.
.
.
default : break;
}
为什么这个状态机识别的if语句放到switch里就出错-已解决
谢谢大家的指点! xiaobendan 发表于 2014-4-7 08:27楼主,两个if结构是不一样的啊
你说的正常的第一个if后面有大括弧的,这样每次key_stime_ok==1时,read_key ...
谢谢指点!之前编译时提示大扩号出错,然后看第2版的书上例子里没有这个大括号,改来改去就成现在出错这种了。你说的运作流程我听懂了,也就是说那个10毫秒的清零(也就是按键消抖)没有和状态机联系起来,也许还没等到10毫秒的清零,状态机就被调用了200次,所以手感上像是一按就达到了应该长按2秒才出来的效果,对吧!谢谢你的指点! kyughanum 发表于 2014-4-7 08:45
哈哈,是哦,现在才发现LZ的if (key_stime_ok)根本不一样
谢谢,就是没有加大括号了。再请教你一下,你说的status没有在其他地方有设置到在状态位是什么意思? toptrying 发表于 2014-4-7 10:08
switch(a)
{
case 0:
谢谢指点,就应该改成你写的这种!再次感谢! toptrying 发表于 2014-4-7 10:08
switch(a)
{
case 0:
谢谢你,我是看教材上case例子里没有这个大括号,所以就没加。不过这个不是问题的原因,通过大家的指点,我找到原因了,应该用(key_stime_ok)作为if的运行条件,只有当10毫秒到了后,才去执行一次read_key_n(),这样效果就对了,谢谢! xiaobendan 发表于 2014-4-7 08:27
楼主,两个if结构是不一样的啊
你说的正常的第一个if后面有大括弧的,这样每次key_stime_ok==1时,read_key ...
这位老师,再请教你一下: while (1)
{
switch (status)
{
case 0:
{
if (key_stime_ok)
{
key_stime_ok = 0; // 10ms到
if (read_key_n()==2) //若长按2秒
{
PORTB ^= 1<<2;
}
/*if (read_key_n()==1) // 若短按
{
PORTB ^= 1<<1;
} */
}
}
break;
}
}
}我在上面的代码中加入短按的这段后,怎么长按、短按都没有反应了?我是想让长按、短按分别控制两个灯。 xiaobendan 发表于 2014-4-7 08:27
楼主,两个if结构是不一样的啊
你说的正常的第一个if后面有大括弧的,这样每次key_stime_ok==1时,read_key ...
换成下面这种switch语句就可以实现功能,怎么会这样?
switch (status)
{
case 0:
{
if (key_stime_ok)
{
key_stime_ok = 0; // 10ms到
switch(read_key_n())
{
case 1:
PORTB ^= 1<<2;
break;
case 2:
PORTB ^= 1<<1;
break;
}
}
}
break;
}
} switch (status)
{
case 0:
{
if (key_stime_ok)
{
key_stime_ok = 0; // 10ms到
if (read_key_n()==2) //若长按2秒
{
PORTB ^= 1<<2;
}
if (read_key_n()==1) // 若短按
{
PORTB ^= 1<<1;
}
}
}
break;
}
就是改成这种后出错的,后面短按if判断应该是再次调用了一个状态机,可是应该没有区别吧?用switch的话应该是只调用了一个状态机,然后就来判断长按和断按,这种我理解了,就是用if来两次调用来判断那种还不理解 switch (status)
{
case 0:
{
if (key_stime_ok)
{
key_stime_ok = 0; // 10ms到
switch(read_key_n())
{
case 1:
PORTB ^= 1<<2;
++mima;
break;
case 2:
PORTB ^= 1<<1;
break;
}
if (++mima >=5)
{
PORTB ^= 1<<7;
}
}
}
break;
还有个问题,这里用的++变量,导致PB.7常亮加间隔快闪,这怎么理解?这个++mima >=5是我写多了两个+,可为什么一运行就长亮呢? biying 发表于 2014-4-8 01:00
这位老师,再请教你一下: while (1)
{
switch (status)
今天比较忙
首先赞一下你的学习的精神
然后我得说一下,在论坛问问题,不要针对某个人,这样不好,某个人没时间,别人又不好插进来,你看,都这么长时间了,除了我以外没有人回复你了吧。
懂我的意思吗?
只要有时间,大家都会帮助你的,所以提问也要有技巧。有些问题也没有必要问,自己静下心来仔细想想就好,解决了,写到这里来,给和你同样求知的人一些帮助。 biying 发表于 2014-4-8 01:10
switch (status)
{
case 0:
连续调用两次,和一次的结果是哪里不一样?这样长按的时间就缩短了一倍,你看到的结果除了时间缩短,还有其他的变化吗?
你可以试试这样,再定义一个全局变量比如key_vaule,在if之前先取得read_key_n的值,然后在if里面使用key_vaule进行判断,这样不就是也调用一次了吗?然后看看结果 本帖最后由 xiaobendan 于 2014-4-8 11:01 编辑
biying 发表于 2014-4-8 02:07
switch (status)
{
case 0:
因为你在if的大括弧里面没有对这个变量清零啊,你看他小于5时,没有执行反转输出的语句,等到他符合条件后,一直在++,每次都符合,所以每次都会反转,知道这个变量溢出,unsigned char 是255,要是unsigned int,闪烁的时间会更长的,另外,20ms的闪烁你能看得到,眼力劲还真是不错啊,是根据亮度变化吗?
在反转的语句后加mima = 0;试试看是什么结果 明白了,原来你看到的闪是条件不成立时产生的,看起来很快,条件符合时常亮了,其实 不是常亮,是闪的太快了。 xiaobendan 发表于 2014-4-8 10:51
连续调用两次,和一次的结果是哪里不一样?这样长按的时间就缩短了一倍,你看到的结果除了时间缩短,还有 ...
我在阿莫轮坛发的问题,没想到都能这么快有回复,且还达到满意的指点,谢谢大家,也谢谢你的提醒,以后我会注意提问了。你说的对,先定义一个全局变量luru,当10ms到时,用这句uru=read_key_n()调用一次状态机,然后再用if来分成两种情况来判断(这样就只运行了一次状态机),就跟switch是一样的效果了。
if (key_stime_ok)
{
key_stime_ok = 0;
luru=read_key_n(); // 10ms到
if (luru==2) //若长按2秒
{
PORTB ^= 1<<2;
}
if (luru==1) // 若短按
{
PORTB ^= 1<<1;
}
} 本帖最后由 biying 于 2014-4-8 17:21 编辑
你这么一解释,终于让我明白为什么有的代码里要有类似的这种luru=read_key_n()句子,原来目的就是只想运行一次read_key_n()这种子函数,来达到后面的判断。调用两次将浪费CPU时间,同时可能导致相应模块出错。 下面这句我测试过好多次,短按和长按后,灯都没有反应,你的分析我理解了,每个10毫秒就调用了两次状态机,将导致2秒的长按变成按1秒就算长按了,因为2秒的长按计时是做到了状态机里,通过计数被调用200次,每10m调用一次,算下来就是2秒。通过分析应该长按一秒后灯应该有变化,短按另一颗灯也应该有变化才对啊,可为什么下载后灯都不变化呢?if (key_stime_ok)
{
key_stime_ok = 0; // 10ms到
if (read_key_n()==2) //若长按2秒
{
PORTB ^= 1<<2;
}
if (read_key_n()==1) // 若短按
{
PORTB ^= 1<<1;
}
}
下面这句你说的我理解了,原来程序是这样运行的:上电后如果不按键,if (++mima >=5)这句话会每隔10ms执行一次,导致mima 不停的加,直接到溢出后再继续加。如果把这个5次改成100次,就会看到灯是亮一秒灭秒的运行的,符合100X10ms=1秒的分析。原来程序是这样运行的啊!if (key_stime_ok)
{
key_stime_ok = 0; // 10ms到
switch(read_key_n())
{
case 1:
PORTB ^= 1<<2;
++mima;
break;
case 2:
PORTB ^= 1<<1;
break;
}
if (++mima >=5)//这里的两个++被我写重了,本来这句应该只是判断上面的短按是否达到5次的。
{
PORTB ^= 1<<7;
}
} 我从前几天初学单片机到现在,预计先设计一个用8脚tiny13A制作的单键密码锁,只有一个按钮作为人机交互,通过它可自行设定密码位数和密码,带看门狗和掉电模式(低功耗),到今天为止,学习进度已经达到了识别一位密码的能力,现在的效果是按5次(表示输入密码5)“开门”,长按2秒“关门”。按错如何处理还没有做。 biying 发表于 2014-4-8 17:18
下面这句我测试过好多次,短按和长按后,灯都没有反应,你的分析我理解了,每个10毫秒就调用了两次状态机, ...
这个要更仔细的观察了
首先说==2的问题,你看看每次10MS完成后都调用两次read_key_n(),那么完成200次的时候,就是返回2的时候,正好是你判断==1的时候,结果很明显,你明白了吗?
然后说==1的问题,在read_key_n()中只要key_time<200之前松开按键,程序都会返回1,但是这个是10MS才执行一次,而你判断==1的时候是在判断==2之后,假设你在10ms期间松开的,那么在==2这次调用的时候返回了1,结果很明显,而在紧接着==1的调用的时候,返回的是0了,所以就……
那么就只有在程序执行了==2的调用之后,在==1的调用之前的这点时间里面,你松开按键,才会有可能返回1,问题是这段时间相对于10MS来说,比例有多少,你自己也清楚吧。
为了证明一下,你可以把两个if的前后顺序调换一下试试看会不会有不同的结果。 xiaobendan 发表于 2014-4-8 19:53
这个要更仔细的观察了
首先说==2的问题,你看看每次10MS完成后都调用两次read_key_n(),那么完成200次的 ...
if (key_stime_ok)
{
key_stime_ok = 0; // 10ms到
if (read_key_n()==2) //若长按2秒
{
PORTB ^= 1<<2;
}
if (read_key_n()==1) // 若短按
{
PORTB ^= 1<<1;
}
}
谢谢!按你的分析,我又对状态机有了新的认识。为了更深刻的了解状态机,我正在对照汇编码,了解此程序中单片机每个机器时钟对代码的运行方式。C语言都才初学,现在又要初学汇编,真的太难了,不过如果不学汇编,我还真看不清C语言是如何执行的。就拿状态机中switch语句来说,在执行key_state_3这句话之前,我还以为到了switch语句后就直接跳到key_state_3这句了,可实际仿真的结果是,到switch语句后,是这样走的:key_state_0,key_state_1,key_state_2,然后才走到key_state_3。再如i运行到f (key_press)时,它是要再读一次(key_press)状态呢,还是取在程序调用read_key_n(),运行初始化读按键I/O电平时的呢?之前没搞清楚,现在仿真后明白了,原来状态机中switch语句后的那些if判断(key_press)状态的,都是取初始化读按键I/O电平时的那个值。如果能看懂汇编,也许我就能早明白这个过程了。 不用吧,AVR我也没有学过汇编的。关键是要细心,不能浮躁,更不能想当然,凡事都有个理字。
51的汇编,现在都看不懂了。 xiaobendan 发表于 2014-4-10 13:25
不用吧,AVR我也没有学过汇编的。关键是要细心,不能浮躁,更不能想当然,凡事都有个理字。
51的汇编,现在 ...
谢谢!第二版书上将汇编的也不适合我这种初学的,太难看懂了,结合JTAG,我也才看懂@0000006B: read_key_n
---- demo_9_2.c -----------------------------------------------------------------------------------
53: key_press = key_input; // 读按键I/O电平
+0000006B: 931A ST -Y,R17 Store indirect and predecrement
+0000006C: 930A ST -Y,R16 Store indirect and predecrement
+0000006D: E000 LDI R16,0x00 Load immediate
+0000006E: E0E0 LDI R30,0x00 Load immediate
+0000006F: 9987 SBIC 0x10,7 Skip if bit in I/O register cleared
+00000070: E0E1 LDI R30,0x01 Load immediate
+00000071: 2F1E MOV R17,R30 Copy register
等 SBIC那行过了后,再放开按键,后面的if,就会把key_press的值认做0了。像我这种初学单片机,不懂汇编,还真得用JTAG配合原汇编代码来一条条代码的看,再用心细细想,才可能明白 奇怪,为什么今天早上我来仿真调试的时候出现新程序下载不到板上,程序没有提示出错,可实际运行的程序是之前下载的,点了一通后之前下载的程序倒是没有运行了,可新程序还是没有真正下载到板上,而且我发现运行仿真的时候和以前不一样了,光标箭头跑到其它地方了,之前开始应该是在main的下面呢,看图怎么会停在break那里呢? 这个问题我转到另一个帖子了
初学单片机-单键密码锁制作日志
http://www.amobbs.com/thread-5576508-1-1.html
(出处: amoBBS 阿莫电子论坛)
页:
[1]