amoBBS 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
查看: 8426|回复: 35

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

[复制链接]
发表于 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延时了,就直接可以按键处理了。
发表于 2010-4-2 10:54:07 | 显示全部楼层
当然不用插入10MS延时,利用定时器产生10ms的标志,每隔10ms再去检测一次按键,这样做CPU的效率很高,要知道10ms单片机可以做很多的事情了.
发表于 2010-4-2 10:55:41 | 显示全部楼层
发表于 2010-4-2 11:56:55 | 显示全部楼层
时基
 楼主| 发表于 2010-4-2 12:58:48 | 显示全部楼层
比如下面,我定时 10MS 中断, 然后再主程序里检测 if(flag_10ms) { 按键处理 }啊? 还是直接就把扫描程序写在中断里呢?

void timer0_serves () interrupt 1 using 1
{
        TH0 = 0XXX;
        TL0 = 0XXX;
        flag_10ms = 1;
}
发表于 2010-4-3 15:20:25 | 显示全部楼层
取其标志就可以,当然不能将扫描程序写在中断里!!!!
 楼主| 发表于 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调用下扫描程序,然后根据扫描的键值进行处理。谢谢
发表于 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以上)。
发表于 2010-4-12 15:59:34 | 显示全部楼层
谢谢马老师,你严谨注学态度,真令我佩服.
发表于 2011-8-24 22:14:47 | 显示全部楼层
这个需要学习
发表于 2011-8-26 00:11:54 | 显示全部楼层
很好的帖子 顶起!
发表于 2011-9-10 23:29:08 | 显示全部楼层
另一个思路。10ms定时,在定时器先读一次,利用2次10ms中断读出按键值。有去抖功效。 在主程序里按键,程序大了。根本保障不了10ms

10ms为心脏。
发表于 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
发表于 2011-9-24 11:58:25 | 显示全部楼层
10ms后
阶段1
   |
其他事情
   |
10ms后
阶段2
   |
其他事情
10ms后
  ...
发表于 2011-9-26 22:44:54 | 显示全部楼层
我的程序都会用到马老师的状态机按键扫描,实时性好呀。。
发表于 2012-2-27 16:54:34 | 显示全部楼层
mark
发表于 2012-7-19 19:26:09 | 显示全部楼层
这个按键程序,学习了!多谢
发表于 2012-7-19 22:00:01 | 显示全部楼层
我的程序也是参考马老师的,一直在用!
发表于 2012-8-9 16:31:14 | 显示全部楼层
学习下, mark
发表于 2012-9-8 13:19:29 | 显示全部楼层
Mark:状态机,按键扫描。。
发表于 2012-10-20 20:34:23 | 显示全部楼层
受教                          
发表于 2012-11-20 14:44:11 | 显示全部楼层
machao 发表于 2010-4-7 21:16
回复【5楼】fangmcu 方谭
取其标志就可以,当然不能将扫描程序写在中断里!!!!
---------------------------- ...

在定时器中断定义一个变量,这种方法是不错,不过,当主函数里在执行其他程序时,中断产生的标志位,也不一定能及时响应(稍微有点延迟)去执行相应的处理(比如主函数里不断的执行LED点阵屏显示函数)
发表于 2013-1-7 17:01:39 来自手机 | 显示全部楼层
学习一下......
发表于 2013-2-18 16:51:33 | 显示全部楼层
machao 发表于 2010-4-7 21:16
回复【5楼】fangmcu 方谭
取其标志就可以,当然不能将扫描程序写在中断里!!!!
---------------------------- ...

这样挺好
发表于 2013-2-21 17:39:13 | 显示全部楼层
不知不觉用了状态机还不知道,看了马老师的书才豁然开朗!
发表于 2013-4-20 08:32:39 | 显示全部楼层
刚接触状态机正在探索中
发表于 2013-5-24 16:28:14 | 显示全部楼层
考研状态机中。。。
发表于 2013-6-3 08:41:11 | 显示全部楼层
{:smile:
发表于 2013-7-12 14:13:13 | 显示全部楼层
mark........
发表于 2013-7-12 16:06:49 | 显示全部楼层
mark.......
发表于 2013-7-29 11:58:54 | 显示全部楼层
中断里放标志,我觉得很好,经常这样干
发表于 2013-8-14 12:20:47 | 显示全部楼层
yzvip7 发表于 2011-9-10 23:29
另一个思路。10ms定时,在定时器先读一次,利用2次10ms中断读出按键值。有去抖功效。 在主程序里按键,程序 ...

觉得这种方式最好!我一直用这种方式,程式简捷。
发表于 2013-10-7 16:11:15 | 显示全部楼层
mark,学习了
发表于 2014-3-18 22:22:05 | 显示全部楼层
mark                          
发表于 2014-12-18 19:59:25 | 显示全部楼层
mark   
发表于 2016-12-5 10:54:24 | 显示全部楼层
这个要mark一下
友情提示:标题不合格、重复发帖,将会被封锁ID。详情请参考:论坛通告:封锁ID、获得注册邀请码、恢复被封ID、投诉必读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|阿莫电子论坛(原ourAVR/ourDEV) ( 公安备案:44190002001997(交互式论坛) 工信部备案:粤ICP备09047143号 )

GMT+8, 2019-9-15 18:52

阿莫电子论坛, 原"中国电子开发网"

© 2004-2018 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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