shgdd520com 发表于 2010-4-2 10:49:40

马老师状态机按键程序请教!

#define key_input                PIND.7                        // 按键输入口
#define key_state_0        0
#define key_state_1        1
#define key_state_2        2

char read_key(void)
{
                static char key_state = 0;
                char key_press, key_return = 0;

                key_press = key_input;                                // 读按键I/O电平
                switch (key_state)
                {
                        case key_state_0:                                // 按键初始态
                                if (!key_press) key_state = key_state_1;        // 键被按下,状态转换到键确认态
                                break;
                        case key_state_1:                                // 按键确认态
                                if (!key_press)
                                {
                                        key_return = 1;                        // 按键仍按下,按键确认输出为“1”
                                        key_state = key_state_2;        // 状态转换到键释放态
                                }
                                else
                                        key_state = key_state_0;        // 按键已抬起,转换到按键初始态
                                break;
                        case key_state_2:
                                if (key_press) key_state = key_state_0;        //按键已释放,转换到按键初始态
                                break;
                }       
            return key_return;
}


马老师:
看了你的状态机程序,有不怎么明白的。以前都是用的检测按键按下了延时10毫秒,再检测是否还按下,按下了再处理。用的都是软件死延时,用这个状态机程序的话是不是就不用在检测按键按下后插入10MS延时了,就直接可以按键处理了。

fangmcu 发表于 2010-4-2 10:54:07

当然不用插入10MS延时,利用定时器产生10ms的标志,每隔10ms再去检测一次按键,这样做CPU的效率很高,要知道10ms单片机可以做很多的事情了.

ljy0421 发表于 2010-4-2 10:55:41

snail0204 发表于 2010-4-2 11:56:55

时基

shgdd520com 发表于 2010-4-2 12:58:48

比如下面,我定时 10MS 中断, 然后再主程序里检测 if(flag_10ms) { 按键处理 }啊? 还是直接就把扫描程序写在中断里呢?

void timer0_serves () interrupt 1 using 1
{
        TH0 = 0XXX;
        TL0 = 0XXX;
        flag_10ms = 1;
}

fangmcu 发表于 2010-4-3 15:20:25

取其标志就可以,当然不能将扫描程序写在中断里!!!!

shgdd520com 发表于 2010-4-6 10:50:39

bit flag_keyscan = 0;

void key_scan(void)
{
   ....
   ....
}

int main(void)
{
        while(1)
        {
                if(flag_keyscan)//10MS定时到,调用扫描
                {
                     flag_keyscan = 0 ;
                  key_scan();
                  调用键盘扫描程序,       
                }
        }
}

void timer0_init (void) interrupt 1
{
        TH0 = 0XXX;//10ms
        TL0 = 0XXX;
        flag_keyscan = 1;
}

是这样写的吗,大概的结构示意,写在主函数里10MS调用下扫描程序,然后根据扫描的键值进行处理。谢谢

machao 发表于 2010-4-7 21:16:35

回复【5楼】fangmcu 方谭
取其标志就可以,当然不能将扫描程序写在中断里!!!!
-----------------------------------------------------------------------

这个回答不全面,容易造成误解。

   将状态机按键扫描放在10毫秒定时中断中也是可以的,其最明显的优点是可以少用一个标志变量。

有许多的人喜欢将LED的动态扫描、按键扫描放在定时中断函数里直接处理。但是我个人并不推荐这样的设计。

首先,2个主要的因素需要注意:

1。LED的动态扫描,按键扫描要求的定时间隔在实际应用中并不要求十分严格。比如看本例的按键扫描,设计是10ms扫描一次。但实际上9.8\9.9\10\10.1\10.2....甚至11ms扫描一次也没问题。都能非常正确的扫描到按键的情况。

2。另外一个原则就是设计的中断服务函数执行时间应该尽量的短。这样假如系统中使用多个中断的话,就是不采用中断嵌套的方式,也能够保证系统能够及时响应各个中断的请求。

所以我喜欢在定时中断中只使用一个标志变量,这样中断服务函数执行时间是最快的,就不会阻塞其它中断的及时响应,而在主程序中判断标志变量,然后再去扫描按键。尽管此时扫描的间隔已经不是严格意义上的10ms,或多或少都有一些延误,而且延误也是不确定的,但都不会影响按键扫描的正确性(一个按键的过程至少在300ms以上)。

fangmcu 发表于 2010-4-12 15:59:34

谢谢马老师,你严谨注学态度,真令我佩服.

csliu911 发表于 2011-8-24 22:14:47

这个需要学习

niugege 发表于 2011-8-26 00:11:54

很好的帖子 顶起!

yzvip7 发表于 2011-9-10 23:29:08

另一个思路。10ms定时,在定时器先读一次,利用2次10ms中断读出按键值。有去抖功效。 在主程序里按键,程序大了。根本保障不了10ms

10ms为心脏。

ahit 发表于 2011-9-24 11:54:45

马老师解释的真好,关键是还能细心解答这种小问题,谢谢!

我也是刚刚有点明白状态机,和楼主的状态大概差不多。

我的理解是:
* 状态机就是把一个顺序的事情,分解成不同的阶段。当然这些状态是互斥的,即在同一时刻,只能处于1中状态。
* 把1件事情分成n个阶段,就变成n个时间序列,这样就把原来必须执行完整个事情,变为每次执行事件的1/n。
* 主程序工作在循环扫描中,比如每隔固定时间间隔(比如10ms)处理事情,而上面的1/n也许只需要1ms,这样主程序就有9ms做其他工作。注意的是1/n的完成事件必须小于固定时间间隔(比如10ms)。
* 每次扫描时,继续上次的阶段(比如2/n,或者3/n),执行完记录当前的阶段,以便下一次从当前的状态开始。

阶段1            
    |
阶段2
    |
阶段3
   ...
阶段n

ahit 发表于 2011-9-24 11:58:25

10ms后
阶段1
   |
其他事情
   |
10ms后
阶段2
   |
其他事情
10ms后
...

vibrate 发表于 2011-9-26 22:44:54

我的程序都会用到马老师的状态机按键扫描,实时性好呀。。

eleven_sue 发表于 2012-2-27 16:54:34

mark

ldzhi2012 发表于 2012-7-19 19:26:09

这个按键程序,学习了!多谢

hyc07209 发表于 2012-7-19 22:00:01

我的程序也是参考马老师的,一直在用!

miaoxun206 发表于 2012-8-9 16:31:14

学习下, mark

luckseason 发表于 2012-9-8 13:19:29

Mark:状态机,按键扫描。。

mainbp 发表于 2012-10-20 20:34:23

受教                        

Dreaml2012 发表于 2012-11-20 14:44:11

machao 发表于 2010-4-7 21:16 static/image/common/back.gif
回复【5楼】fangmcu 方谭
取其标志就可以,当然不能将扫描程序写在中断里!!!!
---------------------------- ...

在定时器中断定义一个变量,这种方法是不错,不过,当主函数里在执行其他程序时,中断产生的标志位,也不一定能及时响应(稍微有点延迟)去执行相应的处理(比如主函数里不断的执行LED点阵屏显示函数)

jz701209李 发表于 2013-1-7 17:01:39

学习一下......

amen 发表于 2013-2-18 16:51:33

machao 发表于 2010-4-7 21:16 static/image/common/back.gif
回复【5楼】fangmcu 方谭
取其标志就可以,当然不能将扫描程序写在中断里!!!!
---------------------------- ...

这样挺好

kero4frog 发表于 2013-2-21 17:39:13

不知不觉用了状态机还不知道,看了马老师的书才豁然开朗!

jimmyfan 发表于 2013-4-20 08:32:39

刚接触状态机正在探索中

15820702370 发表于 2013-5-24 16:28:14

{:lol:}考研状态机中。。。

sunbingbing 发表于 2013-6-3 08:41:11

{:smile:}{:smile:

韦斯克拉 发表于 2013-7-12 14:13:13

mark........

ZYBing 发表于 2013-7-12 16:06:49

mark.......

hongkong 发表于 2013-7-29 11:58:54

中断里放标志,我觉得很好,经常这样干

freshuman 发表于 2013-8-14 12:20:47

yzvip7 发表于 2011-9-10 23:29 static/image/common/back.gif
另一个思路。10ms定时,在定时器先读一次,利用2次10ms中断读出按键值。有去抖功效。 在主程序里按键,程序 ...

觉得这种方式最好!我一直用这种方式,程式简捷。

jhjkdz 发表于 2013-10-7 16:11:15

mark,学习了

cld795 发表于 2014-3-18 22:22:05

mark                        

汪凯露露 发表于 2014-12-18 19:59:25

mark    {:loveliness:}

robincui 发表于 2016-12-5 10:54:24

这个要mark一下
页: [1]
查看完整版本: 马老师状态机按键程序请教!