amoBBS 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
查看: 62292|回复: 404

题目:多功能按键设计。利用一个I/O口,接一个按键,实现3功能操作:单击 + 双击 + 长按。

  [复制链接]
发表于 2011-8-23 18:12:42 | 显示全部楼层 |阅读模式
题目:多功能按键设计。利用一个I/O口,接一个按键,实现3功能操作:单击 + 双击 + 长按。  
============================================================================
用户基本操作定义:
    1。短按操作:按键按下,按下时间<1s,属于一次短按操作
    2。长按操作:按键按下,按下时间>1s,属于一次长按操作

在正常0.5s内无按键操作为启始按键扫描条件下,扫描按键将产生以下3种按键事件:
    1。长按事件:任何1次出现的长按操作都属于长按事件
    2。单击事件:1次短按操作后,间隔0.5内没有短按操作
    3。双击事件:2次短按操作间隔时间<0.5s,则2次短按操作为1次双击事件,且2次短按都取消

特别操作情况定义:
    1。短按操作和长按操作间隔<0.5s,以及,长按操作和短按操作间隔<0.5s,均不产生双击事件
    2。连续n次(n为奇数)短按操作,且间隔均<0.5s,产生(n-1)/2次双击事件+1次单击事件
    3。连续n次(n为偶数)短按操作,且间隔均<0.5s,产生n/2次双击事件

对按键操作者的建议:     
    由于按键的多功能性质,建议操作者每次在单击/长按/双击按键事件发生后,隔0.5s后再进行下一次的按键操作。因为在特别操作情况下,程序是保证按定义进行判断和处理的,主要是怕操作者自己记不清楚导致操作失误。

对软件设计者的要求:
    1。应该全面进行分析,给出严格定义和判断条件,如上所示。如果自己都不清楚,你的设计出的系统就不稳定,不可靠。
    2。在1的基础上,编写出符合要求的程序,并进行全面测试。

/*=============
低层按键(I/0)扫描函数,即低层按键设备驱动,只返回无键、短按和长按。具体双击不在此处判断。参考本人教材的例9-1,稍微有变化。教材中为连_发。
===============*/

#define key_input    PIND.7    // 按键输入口

#define N_key    0             //无键
#define S_key    1             //单键
#define D_key    2             //双键
#define L_key    3             //长键

#define key_state_0 0
#define key_state_1 1
#define key_state_2 2

unsigned char key_driver(void)
{
    static unsigned char key_state = key_state_0, key_time = 0;
    unsigned char key_press, key_return = N_key;

    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_time = 0;                   //  
             key_state = key_state_2;   // 按键仍然处于按下,消抖完成,状态转换到按下键时间的计时状态,但返回的还是无键事件
        }
        else
             key_state = key_state_0;   // 按键已抬起,转换到按键初始态。此处完成和实现软件消抖,其实按键的按下和释放都在此消抖的。
        break;
      
      case key_state_2:
        if(key_press)
        {
             key_return = S_key;        // 此时按键释放,说明是产生一次短操作,回送S_key
             key_state = key_state_0;   // 转换到按键初始态
        }
        else if (++key_time >= 100)     // 继续按下,计时加10ms(10ms为本函数循环执行间隔)
        {
             key_return = L_key;        // 按下时间>1000ms,此按键为长按操作,返回长键事件
             key_state = key_state_3;   // 转换到等待按键释放状态
        }
        break;

      case key_state_3:                 // 等待按键释放状态,此状态只返回无按键事件
        if (key_press) key_state = key_state_0; //按键已释放,转换到按键初始态
        break;
    }
    return key_return;
}

/*=============
中间层按键处理函数,调用低层函数一次,处理双击事件的判断,返回上层正确的无键、单键、双键、长键4个按键事件。
本函数由上层循环调用,间隔10ms
===============*/

unsigned char key_read(void)
{
    static unsigned char key_m = key_state_0, key_time_1 = 0;
    unsigned char key_return = N_key,key_temp;
     
    key_temp = key_driver();
     
    switch(key_m)
    {
        case key_state_0:
            if (key_temp == S_key )
            {
                 key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
                 key_m = key_state_1;
            }
            else
                 key_return = key_temp;        // 对于无键、长键,返回原事件
            break;

        case key_state_1:
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
            {
                 key_return = D_key;           // 返回双击键事件,回初始状态
                 key_m = key_state_0;
            }
            else                                
            {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
                 if(++key_time_1 >= 50)
                 {
                      key_return = S_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = key_state_0;     // 返回初始状态
                 }
             }
             break;
    }
    return key_return;
}     

下面,根据程序分析按键事件的反映时间:
1。对于长键,按下超过1s马上响应,反映最快
2。对于双键,第2次按键释放后马上得到反映。
3。对于单键,释放后延时拖后500ms才能响应,反映最慢。这个与需要判断后面是否有双击操作有关,只能这样。实际应用中,可以调整两次单击间隔时间定义,比如为300ms,这样单击的响应回快一点,单按键操作人员需要加快按键的操作过程。如果产品是针对老年人的,这个时间不易太短,因为年纪大的人,反映和动作都比较慢。

   当然,上面两段可以合在一起。我这样做的目的,是为了可以方便的扩展为N击(当然,需要做修改)。可是最底层的就是最基本的操作处理短按和长按,不用改动的。至于双击,还是N击,在中间层处理。这就是程序设计中分层结构的优点。

测试代码环境如下:  


interrupt [TIM0_COMP] void timer0_comp_isr(void)       // 定时器10ms中断服务
{
       time_10ms_ok = 1;
}


main(viod)  
{  
    .........  

    while  
    {  
        if (time_10ms_ok)            //每10ms执行一次,  
        {  
             time_10ms_ok =0;  
             key = key_read();       //《====== 10ms一次调用按键中间层函数,根据返回键值,点亮不同的LED灯,全面测试按键操作是否正常  
             if (key == L_key)  
                 ........//点亮A_LED,关闭B_LED和C_LED  
             else if(key == D_key)  
                 ........//点亮B_LED,关闭A_LED和C_LED  
             else if(key == S_key)  
                 ........//点亮C_LED,关闭A_LED和B_LED  
         }  
     }  
}  

=================================================

通过以上这个看似简单的按键,看在应用中如何变化,以及如何在实际产品中全面、可靠的进行设计。
=======================================================================================================

后续:
    这个是一个网友要求我做的东西,他买了我编写的教材,学习了里面的状态机按键设计,但还是不能灵活的使用。我提出让一些有兴趣的朋友做一下,大家讨论,(见http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4971785&bbs_page_no=1&bbs_id=1003,28楼),但是很少人给出满意的回答。甚至是这样的回答:“晕,参加电设的也有很多强人,虽然不一定是精英,但还是很有实力的,这种题目很多人都很早就会做了,没必要当作电设的训练题。(见该贴30楼的回答)”。

这个如同我去年挑战一个北大高才生一样,真本事没有,就是嘴硬。

既然别人不感“兴趣”,我编写代码,实现这个简单的多功能按键。这也是对这位购买我编写书的朋友的一种答谢吧。

写完后,还想扩展一点,发点“偏见”如下:

相比国际上的一些品牌产品,国内的很多东西都做不精。其中系统分析和软件设计占有重要的因素。其实不是不想做的好,而是根本没有能力做好!

如上这样一个简单的多功能按键,都分析不透,设计不出好的代码,就不要谈上天下海的吧。D车追尾就是发生了特殊情况,设计中根本没有考虑周全,加上工作人员的不负责,产生如此大的灾难。

最近频繁出现的塌桥、电梯事故、火车出事,都是上天对现在浮躁社会的报应。我们都抱怨别人不负责,浮躁,那么你自己呢?

别人,别的行业我没有资格讲不好,但是对于正在学习的本专业大学生,尤其是要参加全国大学生电子设计大赛的学生,你做这个题目能拿多少分?评估一下有多少能力和水平?

实际上,现在的全国大学生电子设计大赛也变成是一个浮躁的产物,学校和学生都指望它能贴金,对于真正培养和锻炼人的目的和作用,实际上只是挂在前面遮羞布了。
发表于 2011-8-23 18:28:38 | 显示全部楼层
mark
发表于 2011-8-23 18:46:39 | 显示全部楼层
很有意义的帖子。

我这几天在PIC平台上也考虑过同样的东西,但是今天放弃了三种不同键识别的办法,改用一长一短两个键实现需要的功能。是在定时中断中设置几个变量来识别长短键。这样,短键可以在抬键后一个中断周期内快速响应,长键则在按下后3秒左右才开始响应,并能用查询法等待上次长键结束。

如果允许的双击间隔是500ms,则判断单点击要滞后500ms以上,太迟钝了。
发表于 2011-8-23 19:03:14 | 显示全部楼层
如果连这么简单的东东都写不出来,参加电设就是浮云
发表于 2011-8-23 19:08:43 | 显示全部楼层
“实际上,现在的全国大学生电子设计大赛也变成是一个浮躁的产物,学校和学生都指望它能贴金,对于真正培养和锻炼人的目的和作用,实际上只是挂在前面遮羞布了。 ”

/***********************************************************************************************************/
最近看了一个帖子,说电设与高考的对比....百度上搜查“电子设计大赛 高考”。看来我得好好戒除自己的浮躁心态了...
发表于 2011-8-23 19:19:45 | 显示全部楼层
回复【2楼】surf_131  
很有意义的帖子。
我这几天在pic平台上也考虑过同样的东西,但是今天放弃了三种不同键识别的办法,改用一长一短两个键实现需要的功能。是在定时中断中设置几个变量来识别长短键。这样,短键可以在抬键后一个中断周期内快速响应,长键则在按下后3秒左右才开始响应,并能用查询法等待上次长键结束。
如果允许的双击间隔是500ms,则判断单点击要滞后500ms以上,太迟钝了。
-----------------------------------------------------------------------

在windows平台上,鼠标双击事件的顺序大致为键按下、键抬起、双击键按下;这样不会出现单击滞后500ms的问题了。
 楼主| 发表于 2011-8-23 19:44:02 | 显示全部楼层
回复【2楼】surf_131
很有意义的帖子。
我这几天在pic平台上也考虑过同样的东西,但是今天放弃了三种不同键识别的办法,改用一长一短两个键实现需要的功能。是在定时中断中设置几个变量来识别长短键。这样,短键可以在抬键后一个中断周期内快速响应,长键则在按下后3秒左右才开始响应,并能用查询法等待上次长键结束。
如果允许的双击间隔是500ms,则判断单点击要滞后500ms以上,太迟钝了。
-----------------------------------------------------------------------

手机有吧。打开进入短信编辑状态,设置输入方式为英文,然后操作一个键单击、双击、长按,看屏幕输入有何变化。
但是你在打电话,输入电话号码的时候,此时只响应单击,和长按了。单击就是在释放后响应的。双击此时是两次单击。
在不同的工作状态,可以对不同按键操作做不同的处理。掌握了这些,就能设计出好的系统。
 楼主| 发表于 2011-8-23 20:18:49 | 显示全部楼层
回复【5楼】ifree64
-----------------------------------------------------------------------
在windows平台上,鼠标双击事件的顺序大致为键按下、键抬起、双击键按下;这样不会出现单击滞后500ms的问题了。

-----------------------------------------------------------------------
你分析的不对。

在windows平台上,鼠标双击事件为左键快速按两下,之间还是有时间间隔定义的。单其定义不同:

我们以双击打开文件夹中一个WORD文件为例:

将鼠标运动指在这个文件上,然后单击,表示选中该文件,如果快速双击,则表示选中该文件,并打开。你可以测试一下,如果将鼠标运动指在这个文件上,然后单击,过长点时间再次单击,是不会打开文件的。

所以,此时windows平台的定义为:
     第1次单击为选中该文件,如果第2次单击的时间与前面选择文件的单击间隔小于一定的时间,就自动打开该文件。因此,它的第一次单击不需要等待,可以马上动作。只是判断第2次单击与此次的间隔,如果小,属于双击,自动打开文件,如果长,则仍旧表示选择该文件。
     而在嵌入式系统中,有时没有屏幕光标的辅助,这样处理反而不方便。

在这个题目开始,我已经定义的操作过程。如果按windows平台上的鼠标双击事件,也是容易做到的,但此时“双击”的定义就不同了。

另外,windows平台上的鼠标很少处理长按。如果有长按,那么单击就不可能马上被响应,必须等待到释放,看按下的时间才能判断是单击或长按。

先仔细体会一下,操作操作,再提出你的看法。我的贴子,不是一般的肤浅东西。

应一句老话,没有金刚钻,就不会揽这些瓷器活。否则我也不敢吹嘘我编写的教材。出版此类教材的教授多了,有几个真正设计产品,亲自编写代码的?都是理论加肤浅和不实用的代码。

不好意思,晚上灌了的啤酒,说酒话。不过,酒话往往是真话和实话。
发表于 2011-8-23 22:16:15 | 显示全部楼层
老师,阅读你的帖子和教材我获益匪浅,今天我在看你出版AVR教材,对于你的教材中的第十五章:串行SPI接口应用 中的例15_1的源程序存在一下疑问:
基于74HC164并利用SPI口实现8路并口输出的扩展(原书P437页)
以下给出我存在疑问的源程序的一部分:
#difine SPIF 7
....
while(!(SPSR&(1<<SPIF))) {};
....
//SPI初始化
SPCR=0x50;
SPSR=0x01;

您在书中while语句后的注释为“等待SPI完成”但是我觉得你的程序好像不能实现这个功能
首先你把SPIF定义为7(0000_0111)让后与SPSR(0000_0001)位与,在while循环中只循环了5次就跳出了while循环,而要传送的是一个字节的数据(8位)所以我觉得你的程序在还没有将8位数据串行传输完就已经去执行下面的显示语句了,好像不能达到要求

也有可能是我没看懂马老师精妙的程序,还望不吝赐教
发表于 2011-8-23 22:27:32 | 显示全部楼层
马老师,在AVR教材中第十五章:串行SPI接口应用,例 15.2中您用采用完结A/D的方法实现数字万用表的功能,可是ATmega16只是8位的单片机,请问用没用什么办法用你的AVR开发板实现十二位的A/D转换呢?
我用的是TI的TLV2543芯片,是十二位的D/A芯片,用你讲的SPI的办法好像不行,应为ISP数据寄存器只有8位,无法存储12位转化结果
发表于 2011-8-23 22:39:42 | 显示全部楼层
受益匪浅。不大会画状态图,献丑了。

根据马老师程序画的状态图 (原文件名:machao.JPG)
发表于 2011-8-23 22:52:35 | 显示全部楼层
呵呵,用马老师的AVR实验板已经成功实现了以上按键要求
发表于 2011-8-23 23:02:09 | 显示全部楼层
果断MARK
发表于 2011-8-23 23:30:36 | 显示全部楼层
很好
发表于 2011-8-24 01:14:34 | 显示全部楼层
刚在WIN7平台上试了下
鼠标左键:
____-_-----  即短按+短间隔+长按,在长按还没有松开前系统已经认为是双击。
____---_-__  即长按+短间隔+短按,判定为两个(间隔较长的)单击。

这样看来,WIN7上判断鼠标左键按下,是有个超时处理机制。超过一定时间即认为按键已按下。

鼠标右键:
在某文件上右键长按,松开后才会显示文件右键相关菜单项。如果右键按下期间鼠标移动过,则会显示 到当前位置 相关菜单。
如果你的浏览器支持右键动作,也可以试试(小心,别执行了关闭当前窗口的动作~~)。

然后进行另外几项测试:
1.单击选中某个文件,然后在文件名处单击(即修改文件名),会等大概500ms的样子,才进入修改主文件名状态。

2.单击选中某个文件,然后在文件名处双击,可以看到,文件会被直接打开而不会进入文件名修改状态。

3.就在你现在看到的文字位置处,单击,然后间隔稍长,再单击(些过程实质为双击,间隔稍长是为了更清楚的看到过程),
   你会看到光标在第一次单击时就移过来了,第二次单击时就是双击选中的效果。

4.打开记事本,鼠标移到“文件”菜单项上,然后双击。你会看到依然是单击的效果,显示相关下拉菜单。

5.打开记事本,单击“文件”菜单项,显示相关下拉菜单;第二次单击“文件”菜单项,下拉菜单收回;第三次单击“文件”,
   下拉菜单再次显示。

6.打开记事本,单击“文件”菜单项,显示相关下拉菜单,然后双击菜单(如 文件),你会看到相关下拉菜单项会“刷新”一
   下(是不是5中的收回再显示呢?)。

7.打开IE8,如果隐藏了菜单栏按 Alt 键,以显示。双击“文件”,可以看到相关下拉菜单显示后又立即收回(是不是两个单击?)。

8.双击IE8工具栏上的“页面”按钮 或者 Chrome 右上角的设置按钮,可以看到相关下拉菜单显示后又立即收回。


综上7条,是不是可以看出,WIN7上不同的应用程序对单击、双击的操作不同?
发表于 2011-8-24 09:08:56 | 显示全部楼层
楼主,按键方面处理的方法不错
发表于 2011-8-24 10:21:58 | 显示全部楼层
看过类似的做法,但没真正写过
发表于 2011-8-24 10:59:54 | 显示全部楼层
长键则在按下后3秒左右才开始响应,
----------------------------------------
今天利用这个长键状态做了个延迟灯,一旦有键按下就点燃。发现延时不是3秒,而是2秒。这让那个延迟略显仓促。不过暂时不改了。
当初是为了加快按键响应把定时器间隔减小了。
特此说明。
发表于 2011-8-24 11:25:01 | 显示全部楼层
看那个帖子时想着随便写写就很好写出双击、长按的,只是脑子里过了下大概写法就过去了。现在再看马老师的写法,发现想法和实现出来还真是有点差距的,尤其是完美的实现。又一次见识到了马老师严谨细致的态度,受教了。
发表于 2011-8-24 11:32:43 | 显示全部楼层
参考一下
发表于 2011-8-24 14:44:53 | 显示全部楼层
按键状态机,好用!
发表于 2011-8-24 16:39:53 | 显示全部楼层
按键状态机,好用!
发表于 2011-8-24 16:42:35 | 显示全部楼层
顶一下。
发表于 2011-8-24 16:56:02 | 显示全部楼层
不要灌水,有意义么
回复【22楼】dongzhiqing  
-----------------------------------------------------------------------
发表于 2011-8-25 21:41:32 | 显示全部楼层
为什么 每次进入子函数都要把

key_state = 0呢
unsigned char key_driver(void)  
{  
    static unsigned char key_state = key_state_0, key_time = 0;
发表于 2011-8-25 21:57:04 | 显示全部楼层
回复【7楼】machao  
回复【5楼】ifree64
-----------------------------------------------------------------------
在windows平台上,鼠标双击事件的顺序大致为键按下、键抬起、双击键按下;这样不会出现单击滞后500ms的问题了。
-----------------------------------------------------------------------
你分析的不对。
在windows平台上,鼠标双击事件为左键快速按两下,之间还是有时间间隔定义的。单其定义不同:
我们以双击打开文件夹中一个word文件为例:
将鼠标运动指在这个文件上,然后单击,表示选中该文件,如果快速双击,则表示选中该文件,并打开。你可以测试一下,如果将鼠标运动指在这个文件上,然后单击,过长点时间再次单击,是不会打开文件的。
所以,此时wi......
-----------------------------------------------------------------------

不知马老师是否熟悉Windows平台上的程序开发,我的话是没有错误的,请看如下这句引自MSDN的原话
(http://msdn.microsoft.com/en-us/library/ms645606(v=vs.85).aspx)

Only windows that have the CS_DBLCLKS style can receive WM_LBUTTONDBLCLK messages, which the system generates whenever the user presses, releases, and again presses the left mouse button within the system's double-click time limit. Double-clicking the left mouse button actually generates a sequence of four messages: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP

这就是一个双击事件并不是只有一个WM_LBUTTONDBLCLK消息,而是按照先后顺序有4个消息;至于如何对这四个消息如何处理是应用程序自己的事。
如果只需要处理单击事件,响应WM_LBUTTONDOWN消息即可,要处理双击事件,就响应WM_LBUTTONDBLCLK,这样处理单击的事件的应用程序也不会
引入500ms的延迟,处理双击的也可得到这个双击事件。至于两次单击之间是多长的时间,这只是一个系统的参数而已。
发表于 2011-8-25 22:41:01 | 显示全部楼层
回复【24楼】kihell 红舞林檬浩
为什么 每次进入子函数都要把
key_state = 0呢
unsigned char key_driver(void)   
{   
    static unsigned char key_state = key_state_0, key_time = 0;   

-----------------------------------------------------------------------

没用c写过程序,只用汇编的人说说我的理解,望指正

定义为static类型,只在系统初始化赋此值
注意下一句就没定义成static,所以key_return = N_key每次调用都会被执行
这也是我画状态图时很犹豫的地方,不知该如何表示
发表于 2011-8-25 23:04:32 | 显示全部楼层
25楼的兄弟,你没理解马老师的意思
当你第一次单击之后,神仙也不知道这是单击还是双击
这时有2种处理方法
1、等待500ms(可调),这时就可判断是单击还是双击。马老师程序就是这么做的。
2、先按单击处理一次,如果在500ms(可调)内又一次单击,则继续执行双击。这时有个问题,如果单击和双击无关,那么第一次单击就是多余的,完全可以说是错误的。windows取了个巧,定义单击是选定,双击是打开,两个操作具有逻辑上的延续性,所以显得没有延时。
   如果你定义单击是删除,双击打开,你看看有没有延时。
发表于 2011-8-25 23:31:06 | 显示全部楼层
mark
发表于 2011-8-26 00:03:05 | 显示全部楼层
回复【27楼】packer  
25楼的兄弟,你没理解马老师的意思
当你第一次单击之后,神仙也不知道这是单击还是双击
这时有2种处理方法
1、等待500ms(可调),这时就可判断是单击还是双击。马老师程序就是这么做的。
2、先按单击处理一次,如果在500ms(可调)内又一次单击,则继续执行双击。这时有个问题,如果单击和双击无关,那么第一次单击就是多余的,完全可以说是错误的。windows取了个巧,定义单击是选定,双击是打开,两个操作具有逻辑上的延续性,所以显得没有延时。
   如果你定义单击是删除,双击打开,你看看有没有延时。
-----------------------------------------------------------------------

你没有理解我的意思而已。

我并不是说马老师这样的做法是错误的,因为要判断双击必须要等待500ms(可调),但马老师现在这样的处理方法使得
对于一个需要单击事件的应用必须等到500ms以后才能有响应,这就是2楼兄弟说的500ms的延迟,太迟钝了。
当然,如果对于单击和双击同一个按键定义完全截然不同的功能这个500ms的延迟就是必须付出的代价了,不过我认为
这样的功能定义实在是太不人性化了,需要用户来学习适应并不是一个优秀的人机交互设计。

windows鼠标双击的事件机制不用于马老师这里的处理方式,马老师对于双击事件是吃掉了前面的单击,而windows是仍然将前面的
单击事件放到消息队列里面。

“单击--长时间--单击”动作序列在windows里是这样的消息: down、 up、 down、 up
“单击--短时间--单击”动作序列在windows里是这样的消息:down、up、double_down、up,马老师的方法是: double_down
发表于 2011-8-26 00:24:20 | 显示全部楼层
学习
发表于 2011-8-26 00:28:31 | 显示全部楼层
回复【29楼】ifree64  
“单击--长时间--单击”动作序列在windows里是这样的消息: down、 up、 down、 up
“单击--短时间--单击”动作序列在windows里是这样的消息:down、up、double_down、up,马老师的方法是: double_down
-----------------------------------------------------------------------
单击(短)应该是 down、 up、nodown.........
长按是down、noup.........
双击是 down、 up、double_down.........

另外说明下我用“单击+长按”做的东西,是调整时间(小时、分等)的某一位。单击时该位数值循环步进。如果增加双击操作,可以增添一个反向循环步进,或者-2操作。比如,8点单击调到9点,双击就-2,回到7点。但是这之后的逻辑处理比较麻烦,于是忽略双击。
我感觉单键操作在lz指示的方向上,可以发展出例如莫尔斯电码的自动识别等等应用来。
 楼主| 发表于 2011-8-26 02:14:06 | 显示全部楼层
回复【26楼】packer
回复【24楼】kihell 红舞林檬浩
为什么 每次进入子函数都要把  
key_state = 0呢  
unsigned char key_driver(void)   
{   
    static unsigned char key_state = key_state_0, key_time = 0;   
-----------------------------------------------------------------------
没用c写过程序,只用汇编的人说说我的理解,望指正
定义为static类型,只在系统初始化赋此值
注意下一句就没定义成static,所以key_return = n_key每次调用都会被执行
这也是我画状态图时很犹豫的地方,不知该如何表示
-----------------------------------------------------------------------

意思是对的。
    在C中,static 定义的变量如同一个全局变量,在整个程序中,该变量(内存)一直存在。与全局(在所有函数外定义的)变量的不同处有2点:

1。此变量具备局部变量的特点,只能在本定义的函数中使用,其它函数中则不能对起操作。
2。由于1的特点,可以在另外的函数中定义一个相同名称的static类型变量,此时尽管两个变量“同名”,但是实际是不同的两个变量,分别永久占用2个不同的内存单元。
   
    所以对于static变量,在调用第1次调用定义此变量的函数时,会在RAM中分配一个地址,并赋初值。当定义此变量的函数返回后,该变量所占用的RAM并不释放,仍旧保持原来的数据,但对于其它函数,这个变量是“隐身”的(类似C++中的私有变量)。当再1次调用定义此变量的函数时,由于该变量已经存在了,所以就不会再次被赋初值的。
   
    而通常在一个函数中定义的变量,属于局部变量,每次调用定义此变量的函数时,就会在RAM中分配一个地址,并赋一个初值。当定义此变量的函数返回后,该变量所占用的RAM就被释放掉了,此时这个变量是“不存在”的。

如果上面的解释还是不理解的话,下面给一个表浅的解释:
    在C语言中,如果在定义变量的定义语句中同时赋值,表示系统应该在为该变量分配内存空间后,赋一个规定的初值(如果定义变量语句中没有赋值,那么其初值是不定的。但在多数系统中通常为0)。
    那么如何理解下面的2个变量定义呢:
    unsigned char key_driver(void)   
    {   
        static unsigned char key_state = key_state_0;
        unsigned char key_return = N_key;
        ........

    key_state是static类型,在第1次调用函数时,需要为其分配内存单元,赋初值。以后该变量永久保持,因此不是每次调用都被赋初值的(不是每次调用都要为其分配内存单元的!)
    而key_return就是一个局部变量,每次调用函数时,需要为其分配内存单元,要赋初值。而函数返回后,该变量取消了,对应分配的内存释放掉。(是每次调用都要为其分配内存单元的,所以每次要赋初值!)

=========================================
在20几年前,给学生上C语言。考试中有这样的题目:
    1。什么是全局变量、局部变量、静态变量、动态变量?各有什么特点?
    2。请说明和解释C语言中以下定义的3个变量属于什么性质的变量:
   
    unsigned char key_time = 0;
    unsigned char key_driver(void)   
    {   
        static unsigned char key_state = key_state_0;
        unsigned char key_return = N_key;
        ........
   
     3。总结、归纳出在C中如何正确定义和使用变量,说明为什么。
     。。。。。

    现在,这样的题目不敢出了。不仅学生回答不出,而且就是许多教C的教师也说不明白(实际上,还有细分:比如:静态局部、静态外部等)。

给点提示:在上例中key_time是全局静态变量,key_state是局部静态变量,key_return是局部动态变量。它们之间的区别请仔细体会。

难一点的思考:有不有全局动态变量?如果有的话,是什么形式的,如何形成?为什么和什么情况下要使用全局动态变量?
 楼主| 发表于 2011-8-26 03:09:54 | 显示全部楼层
回复【29楼】ifree64
回复【27楼】packer   
25楼的兄弟,你没理解马老师的意思
当你第一次单击之后,神仙也不知道这是单击还是双击
这时有2种处理方法
1、等待500ms(可调),这时就可判断是单击还是双击。马老师程序就是这么做的。
2、先按单击处理一次,如果在500ms(可调)内又一次单击,则继续执行双击。这时有个问题,如果单击和双击无关,那么第一次单击就是多余的,完全可以说是错误的。windows取了个巧,定义单击是选定,双击是打开,两个操作具有逻辑上的延续性,所以显得没有延时。
   如果你定义单击是删除,双击打开,你看看有没有延时。
-----------------------------------------------------------------------
你没有理解我的意思而已。
我并不是说马老师这样的做法是错误的,因为要判断双击必须要等待500ms(可调)......
-----------------------------------------------------------------------

两位无需争论了,都静下来仔细琢磨,我回答下面2个问题:

1。利用状态机的方式,可以提供每次按键的down、up状态、本次down - up的间隔、上次up-本次down的间隔。有了这些,你的上层可以做各种的分析。实现W里面鼠标左键的所有功能没有问题的。

   只是我的题目定义的单击、双击操作与W鼠标指向打开一个文件的单击、双击有区别:
   
   W鼠标双击一个文件,包括2个动作:1-选中该文件,2-打开文件,单击一个文件的动作是选中该文件。由于双击的第1个动作与单击相同,所以其双击操作的第1次单击不需要等待,可以马上响应的。
   
   如果双击的动作中开始部分与单击动作不同,单击的响应一定要滞后的,当然这个滞后时间可以调整到适合。
   
2。至于“这样的功能定义实在是太不人性化了,需要用户来学习适应并不是一个优秀的人机交互设计”,则是寡闻了。这个不是我发明创造的,你的手边就有这样的“实在是太不人性化”的“不是一个优秀的人机交互设计”的最有力证明:手机!!
   
    我在6楼已经给出了实际的东西:
    --------------------------------------------------------------------------------------------------------
    手机有吧。打开进入短信编辑状态,设置输入方式为英文,然后操作一个键单击、双击、长按,看屏幕输入有何变化。
    但是你在打电话,输入电话号码的时候,此时只响应单击,和长按了。单击就是在释放后响应的。双击此时是两次单击。
    --------------------------------------------------------------------------------------------------------
   
    应该先去证明一下,而后再发表意见。如果你的手机没有按键键盘,那么也应该找一个体会一下。
   
    请不要说,因为手机的键盘是“实在是太不人性化”的“不是一个优秀的人机交互设计” ,所以IPHONE取消了键盘,用手写了。现实中大量的电子设备和仪器,并没有屏幕、鼠标、手写的交互,就是简单的LED灯和按键,所以多功能按键到处都有的。

     看个最简单的例子,到商店找个电子手表,它就2-3个按键,你设置一下日期和时间,体会一下吧。至少你能发现一个按键有短按和长按连_发的功能。如果你认为它“实在是太不人性化”的“不是一个优秀的人机交互设计”,那么你如何设计?为电子表增加一个鼠标接口?

==================================================================================================================
     现在的系统,成本核算非常重要。你可以增加一个或几个键,使得“人机交互人性化”,单成本也是要增加的。对于有些不常用、特殊的功能,通常使用多功能按键完成,而常用,经常的操作,尽量简单,不用多功能的按键。这是综合平衡的办法。

      东西是死的,人是活的。只要你有能力,就能设计出合理的交互方式。
      还是讲手表上的一个键,如果是用户在查询时间方式工作,希望这个按键只有单击的单一功能,响应要快,而用户在设置时间方式工作,希望这个按键是多功能的,有单击和长按连_发的功能,但此时单击的响应要慢点了。
      如何处理这个矛盾?
      这个也简单呀。你写2套低层的驱动,一套就是扫描按键的按下,马上响应,另一套是需要等待的。然后增加一个状态判断,如果是上层在查询时间方式,那么使用前一套扫描按键,反映肯定快,如果上层在设置时间方式,就使用第2套扫描按键,此时单击响应要慢一点。这样不就平衡了吗?谁一天到晚去设置日期和时间?
     还不是大状态机套小状态机吗?
     
==================================================================================================================
    在我编写的教材中,已经说明状态机是系统分析的一种方法,不是我发明的。采用状态机的按键设计也不是我率先提出的。都是早就有的东西。我只是拿按键这个小的例子,引出状态机设计的方法,让大家去体会,并能掌握这个方法去使用在其它的地方,就是一个系统主工作方式,通常都是可以用状态机的思路来进行分析和设计,并实现的。
发表于 2011-8-26 08:49:23 | 显示全部楼层
学习了
发表于 2011-8-26 08:54:21 | 显示全部楼层
回复【33楼】machao  

我在6楼已经给出了实际的东西:
    --------------------------------------------------------------------------------------------------------
    手机有吧。打开进入短信编辑状态,设置输入方式为英文,然后操作一个键单击、双击、长按,看屏幕输入有何变化。  
    但是你在打电话,输入电话号码的时候,此时只响应单击,和长按了。单击就是在释放后响应的。双击此时是两次单击。
    --------------------------------------------------------------------------------------------------------
     
    应该先去证明一下,而后再发表意见。如果你的手机没有按键键盘,那么也应该找一个体会一下。
-----------------------------------------------------------------------

状态机的思想我也是非常喜欢用的,并且很好用。

昨天发帖子时因为网络故障,关于人机交互这方面我的意思表达得并不全面,我的意思不是说使用单击和双击就是差的人机交互,而是如果一个按键的功能单击和双击风马牛不相及,甚至逻辑矛盾,那么就是差的人机交互设计。比如单击用于删除、双击打开,那么在需要打开文件、或者删除文件时,就必然会发生一些单击、双击的错误操作。而像windows的资源管理器那样对单击、双击的具有“逻辑延续性”的功能定义正正是需要认真学习的人机交互功能设计。

刚才也特意试了下我手中的山寨手机在英文输入下的按键处理,可能这个手机功能实在太山寨了,没有长按和双击的支持,也许其他手机有这样的功能吧。不过,我也仔细想了一下手机如何利用非常少的按键实现众多功能的,貌似问题的关键不是单击、双击、长按。而是“确定”、“取消”两个按键在不同时候有不同功能,通过“确定”、“取消”、“上”、“下”这几个功能键构成了一个菜单系统。是不是这种方式才是更好的利用少量按键实现多种功能的方法呢(这里也用一个状态机来规定不同状态下同一按键的功能)?当然适当的定义长按和双击可以带来更加灵活的变化,不过我比较反对对同一个键的不同按的方式定义“逻辑不相关”的功能。比如“+”键,单击加一次,长按加十次,按着不放再连_发“+”的功能,应该就是一种非常“直白"的功能定义,但如果把”+“键,单击定义为加,双击定义为减,恐怕就不是一个很好的功能第一。

PS:我前面关于windows对鼠标单击、双击的处理提到windows的消息机制,这个是windows系统很底层的驱动行为;不知为什么大家都用windows资源管理器的行为来讨论windows对鼠标单击、双击的处理。windows的鼠标驱动和windows资源管理器之间的关系正如马老师贴出来的按键驱动与上层应用之间的关系。
发表于 2011-8-26 09:15:15 | 显示全部楼层
回复【33楼】machao  
   
W鼠标双击一个文件,包括2个动作:1-选中该文件,2-打开文件,单击一个文件的动作是选中该文件。由于双击的第1个动作与单击相同,所以其双击操作的第1次单击不需要等待,可以马上响应的。
   
  这个也简单呀。你写2套低层的驱动,一套就是扫描按键的按下,马上响应,另一套是需要等待的。然后增加一个状态判断,如果是上层在查询时间方式,那么使用前一套扫描按键,反映肯定快,如果上层在设置时间方式,就使用第2套扫描按键,此时单击响应要慢一点。这样不就平衡了吗?谁一天到晚去设置日期和时间?
-----------------------------------------------------------------------

请问马老师,如何用2套底层驱动切换的方式实现类似windows资源管理器对鼠标单双击功能呢?我觉得这个问题的答案才是windows鼠标驱动和您的方式在本质上的区别。
发表于 2011-8-26 09:18:02 | 显示全部楼层
马老师这句话太对了“现在的系统,成本核算非常重要。”
我经常被要求只给4个键,而要完成10种以上功能操作,我倒是想学explorer,实在学不来
发表于 2011-8-26 09:22:53 | 显示全部楼层
学习了!
发表于 2011-8-26 10:01:33 | 显示全部楼层
每次见马老师都有收获,谢谢马老师!
自从用了状态机后,腰也不疼了,腿也不酸了,走路也更有劲了。
发表于 2011-8-26 10:23:51 | 显示全部楼层
mark
发表于 2011-8-26 10:24:21 | 显示全部楼层
马上电赛了!
发表于 2011-8-26 10:43:00 | 显示全部楼层
学习了。。。
 楼主| 发表于 2011-8-27 13:45:33 | 显示全部楼层
回复【35楼】ifree64
-----------------------------------------------------------------------

1."刚才也特意试了下我手中的山寨手机在英文输入下的按键处理,可能这个手机功能实在太山寨了,没有长按和双击的支持......"

(原文件名:QQ截图20110827133745.png)
   如果手机键盘如上,那么你如何直接使用键盘操作输入英文字母B或者C的?

2。“请问马老师,如何用2套底层驱动切换的方式实现类似windows资源管理器对鼠标单双击功能呢?”

   要实现这个的方法我在33楼提示过。底层键盘扫描就是最基本的:利用状态机的方式,提供每次按键的down、up状态、本次down-up的间隔、上次up-本次down的间隔。也就这4个DD。
   有了这些,你的上层可以做各种的分析和处理。实现W里面鼠标左键的所有功能都没有问题的。
发表于 2011-8-27 17:37:56 | 显示全部楼层
回复【43楼】machao  
回复【35楼】ifree64
-----------------------------------------------------------------------
1."刚才也特意试了下我手中的山寨手机在英文输入下的按键处理,可能这个手机功能实在太山寨了,没有长按和双击的支持......"

(原文件名:qq截图20110827133745.png)
   如果手机键盘如上,那么你如何直接使用键盘操作输入英文字母b或者c的?

-----------------------------------------------------------------------
我用的手机设定了一定的延时,在这个时间内可以在a、b、c三个字母间切换,但这不是双击也不是长按吧。


2。“请问马老师,如何用2套底层驱动切换的方式实现类似windows资源管理器对鼠标单双击功能呢?”
   要实现这个的方法我在33楼提示过。底层键盘扫描就是最基本的:利用状态机的方式,提供每次按键的down、up状态、本次down-up的间隔、上次up-本次dow......
-----------------------------------------------------------------------
我的意思是在您楼主位的代码中,识别到是双击时,“吃掉”了双击前的这个单击,就不好去做类似W里面的单、双击处理了,并且引入了单击识别的500ms延迟。而你的新方法看起来好像是要在按键函数中返回按键的down、up状态以及down、up间的间隔给上层,由上层来识别这个是单击还是双击,并作相关处理?为什么不考虑引入消息系统,类似与W那样来返回按键情况呢?
发表于 2011-8-27 18:39:51 | 显示全部楼层
你专业是不是在pc上搞开发,没接触过底层驱动啊,感觉你对windows很熟,但不理解马老师说的
OSI分七层哪,上面讨论的都是最底层最基础的,消息系统已经属于上层了,不在本问题讨论范围。
发表于 2011-8-27 19:29:24 | 显示全部楼层
楼上有点搞笑了,谁说底层代码就不需要消息机制了?
发表于 2011-8-27 19:39:19 | 显示全部楼层
状态机里面最重要的四个概念就是:“状态”、“事件”、“转换”和“动作”,这里面的“事件”主要就是用消息机制来实现的。OSI分七层不假,但是消息并不是应用层独享的。
发表于 2011-8-27 19:52:15 | 显示全部楼层
把我在那个帖子里的状态机转过来吧。



(原文件名:key.jpg)
发表于 2011-8-27 19:53:34 | 显示全部楼层
楼上的,偷换概念没意思。“消息机制”和我们所说的”消息系统“是一个概念吗

我为什么特别强调7层协议,就是感觉44楼没理解马老师在43楼最后一句:
“有了这些,你的上层可以做各种的分析和处理。”

一个按键处理可以分解成好几层任务,本贴只讨论最最最最底层的,而44楼一直想把上层的任务加进来。
我替他着急
发表于 2011-8-27 20:04:47 | 显示全部楼层
回复【45楼】packer  
你专业是不是在pc上搞开发,没接触过底层驱动啊,感觉你对windows很熟,但不理解马老师说的
osi分七层哪,上面讨论的都是最底层最基础的,消息系统已经属于上层了,不在本问题讨论范围。
-----------------------------------------------------------------------

难道windows的消息系统属于应用层?我第一次这么听说,系统底层关心的是检测硬件的输入,并且把它们放入消息队列;
应用层仅仅是从消息队列中取出消息,并且处理这个消息。
发表于 2011-8-27 20:08:57 | 显示全部楼层
回复【49楼】packer  
楼上的,偷换概念没意思。“消息机制”和我们所说的”消息系统“是一个概念吗
我为什么特别强调7层协议,就是感觉44楼没理解马老师在43楼最后一句:
“有了这些,你的上层可以做各种的分析和处理。”
一个按键处理可以分解成好几层任务,本贴只讨论最最最最底层的,而44楼一直想把上层的任务加进来。
我替他着急
-----------------------------------------------------------------------

你可以将按键的处理分成多层,但真正的应用层希望看到的是一个简单一致的界面。应用层希望看到的是:
有没有单击?有没有双击?而不是还要自己根据时间间隔等来自己判断这个是单击还是双击。
发表于 2011-8-27 20:27:12 | 显示全部楼层
楼上,什么叫“最最最最底层”?

“最最最最底层”应该是IO口扫描吧,单击,双击,长按算“最最最最底层”吗?

什么情况下算单击,双击,长按呢,单击,双击,长按这些事件又是由谁来处理呢?难道也是“最最最最底层”来处理吗?再说离开了你所说的“上层的任务”,那你的“最最最最底层”有存在的必要吗?

分析问题不要只看到你所说的“最最最最底层”,更要看到你所说的“上层的任务”,要不然设计出来的东西就是闭门造车。

ifree64所说的是已经完全成熟的概念,这个概念并不是只有你所说的“上层的任务”才能用,可能你的理解“上层的任务”就是WINDOWS这样的系统,那就当我没说以上的所有的话。既然已经有了成熟的按键处理机制,在单击,双击,长按这些操作的概念也没有异议的情况下,为什么还要闭门造车搞一套?
发表于 2011-8-27 21:03:55 | 显示全部楼层
回27楼:

“25楼的兄弟,你没理解马老师的意思
当你第一次单击之后,神仙也不知道这是单击还是双击
这时有2种处理方法
1、等待500ms(可调),这时就可判断是单击还是双击。马老师程序就是这么做的。
2、先按单击处理一次,如果在500ms(可调)内又一次单击,则继续执行双击。这时有个问题,如果单击和双击无关,那么第一次单击就是多余的,完全可以说是错误的。windows取了个巧,定义单击是选定,双击是打开,两个操作具有逻辑上的延续性,所以显得没有延时。
   如果你定义单击是删除,双击打开,你看看有没有延时。”


其实我觉得这并不是WINDOWS取了巧而是MS在详细考虑之后做的一个明智的决定,如果“单击是删除,双击打开”那就不是有没有延时的问题了,因为你已经单击删除文件了,还怎么能把已经被删除的文件打开?

我倒是认为应该按你的第二种方法来处理更能符合人们操作的习惯,因为人们更乐意用那种一按下就有反应的系统,如果一个系统按键按下后还需要等一段时间才能确认输入的话,除非这个按键有着特殊功能,并且在说明书里面详细介绍了这种用法,否则是决不能接受的。如果你的电脑键盘需要每按一个键都要等0.5秒才能看到这个按键输入后的反应的话,估计你早就想把这个键盘砸掉了。
发表于 2011-8-27 21:10:33 | 显示全部楼层
咳咳,哥儿们,别激动,楼盖高了别忘了楼主位是什么帖子,我们讨论主题到底是什么

请细细品味马老师43楼这句话
“有了这些,你的上层可以做各种的分析和处理。实现W里面鼠标左键的所有功能都没有问题的”

我的理解,ifree64所说的消息系统是要建立在本贴讨论的内容上(即在任务分层上要比消息系统低级),有了这些用户可以自由发挥。

另外,大家应该知道马老师为什么要出这个贴子,可不是什么“要闭门造车搞一套”
发表于 2011-8-27 21:17:56 | 显示全部楼层
回复【53楼】igoal
回27楼:
如果你的电脑键盘需要每按一个键都要等0.5秒才能看到这个按键输入后的反应的话,估计你早就想把这个键盘砸掉了。
-----------------------------------------------------------------------

看来大家专业不同,接触的领域不同,思维也不同。一说按键,你想到的是电脑键盘,马老师43楼给的是手机键盘。
十几个键要实现101键的功能,我想这就是大家最大的不同。
 楼主| 发表于 2011-8-27 21:37:43 | 显示全部楼层
to ifree64,igoal:

    igoal理解的应该是更透彻的。
   
    对于一个按键,或鼠标的按键,最底层就是I/O扫描了,那么最基本的就是按键的4个基本的量:down、up状态和down-up之间的时间间隔、上次up-本次down之间的时间间隔(实际这2个间隔也可以有上层的判断处理)。 有了这个基本的东西,多种功能组合都可以实现。

    对于Windows系统,在不同的情况下,该按键的功能是不一样的,所以最低层的I/O扫描可以非常简单,只要把4个量(状态)正确的反馈到上层就可以了(机械按键应该把消抖处理掉)。其它可以不去考虑,由上面去处理。

    而在嵌入式系统中,通常按键没有那么多的变化,功能是固定的,因此我的这个例子在低层把单次和长按直接做了判断,也就是简化了处理,使得整个系统比较简单和简洁。这个符合嵌入式系统软件硬件可剪裁的性质。

     ifree64还没有开窍,好象简单的认为“应用层”的下面就是最低层I/O了?
     
     在我LZ的例子中,已经是3层,最上层就是得到的:“有没有单击?有没有双击?有没有长按?”。注意:尽管最低层已经多做了事情,但还是没有给出双击。双击是中间层给出的。这种思想方法与WINDOWS中实际是相同的。

下面回答ifree64在44楼的问题:

1:“我用的手机设定了一定的延时,在这个时间内可以在a、b、c三个字母间切换,但这不是双击也不是长按吧。”
   
   没有说此时一定是双击或长按,但此时该键的操作就是多功能的,应该属于你定义的“不人性化的人机交互界面”吧。假定你如果输入AAA这样3个字符,输入一个A后,你必需等待吧这个延时时间后,才能收入下一个A吧。这个与需要等待判是否有双击是同一个道理,程序必须等待这个延时,才能最后确定你的输入。你把我的代码简单一改,就能做到这个效果,但这个延时还是少不了。

2:“而你的新方法看起来好像是要在按键函数中返回按键的down、up状态以及down、up间的间隔给上层,由上层来识别这个是单击还是双击,并作相关处理?为什么不考虑引入消息系统,类似与W那样来返回按键情况呢?”
   
     我不是在给W编写低层,我只是针对单一按键实现固定的功能。小的系统没有W那么多的资源,有必要写的那么庞大和复杂吗?在我LZ的例子中,已经是3层,最上层就是得到的:“有没有单击?有没有双击?有没有长按?”。注意:尽管最低层已经多做了事情,但还是没有给出双击。双击是中间层给出的。这种处理的思想与方法与WINDOWS中实际是相同的。

=============================================================
至于单击和双击之间做什么事情,是系统定义和设计的问题。

如果单击和双击操作要做完全不相干的事情,那么这个500ms就必须等待,
如果双击动作是在单击动作上的延续,那么单击后可以不需要等待了,反正击1次都做相同的动作,但此时你第2次的单击就必须在500ms后。

要做到后一种方式,稍微改改代码就可以了。这个没有必要争论。关键是思想方法的掌握。一旦你真正了解了,你就会先考虑你的系统如何操作才更加适合人的操作。

微软是聪明的公司,朝三暮四的方法,耍了一群猴子。
发表于 2011-8-27 23:32:44 | 显示全部楼层
回复【56楼】machao  

     ifree64还没有开窍,好象简单的认为“应用层”的下面就是最低层I/O了?
----------------------------------------------------------------------
对于是否“开窍”我表示一笑而过。

在我的观点中,我并没有认为应用层下面就是“最底层”I/O,这样的误解可能是这句话造成的:

2:“而你的新方法看起来好像是要在按键函数中返回按键的down、up状态以及down、up间的间隔给上层,由上层来识别这个是单击还是双击,并作相关处理?为什么不考虑引入消息系统,类似与W那样来返回按键情况呢?”

我的本意本来就是应该把这些诸如“单击”、“双击”的功能放到底层去做。只不过我们在对“底层”的理解上出现了一些概念上的差别。你把这些功能的实现代码称为“中间层”,而我统统的把它们认为是底层,如果要划分得这么细,那么每增加一级函数嵌套,就增加一层,就不知我的系统将有多少层了。在我看来,你处理双击的部分的"中间层“,是为上层服务的,那么在上层来看,它就是上层的底层。当然,其实我们没有必要执着于它是底层还是中间层这个称谓。

回到“单击延迟”这个话题来,我早就同意如果要把单击和双击同一个按键定义完全不同的功能,那么这个时候对单击的识别延迟是必然的。我只是以为

1、马老师这个中间层在识别双击时,在中间层“吃掉了”双击事件的前一个单击按键事件,因而在中间层就引入了单击的延迟这种做法不是特别恰当,可以学习Windows的事件处理机制来避免这个在中间层中引入的单击延迟。

2、如果对同一个按键的单击和双击功能定义得功能非常逻辑不相关是不人性化的。比如单击删除、双击打开文件;比如单击“加”、双击“减”;但关于人性化的问题是需要具体问题具体分析的,这个问题不能一概而论;这个人性化问题这里不讨论,这个问题太笼统(iPhone的应用程序开发甚至有一本专门的书来介绍人机交互设计,并且它成功的关键就是人机交互)。

对于第一点,就用手机的英文输入来讨论吧,要输入“AAA”,按照马老师前面的观点,这是类似于你双击的延时的例子。但请注意虽然输入第一个A需要等待一段时间后才能确认这个A,但从按下A到屏幕上看到这个A是即时而没有延时的,而你中间层的双击识别代码却是需要500ms延迟的,我想这个才是关键。如果你说为了实现这样的要求,需要不同的“中间层”,那么好了,这本来就是我的讨论的出发点。

------------
微软是聪明的公司,朝三暮四的方法,耍了一群猴子。
------
技术讨论没有必要涉及到这些“宗教”意义的话语吧。


PS:
写完后再看一下马老师最后的话,发现其实我们之间的观点并没有根本的分歧,只是因为论坛只言片语产生的一些误解。
你的方法,可以对单击、双击定义完全不同的功能,但引入必然的单击延迟;
MS的事件机制,如果要对单击、双击定义完全不同的功能,需要自己做类似的处理,但没有单击的延迟。窃以为,MS的方法更好一些。
 楼主| 发表于 2011-8-28 02:31:24 | 显示全部楼层
楼上的,你的帖子自己矛盾:

“回到“单击延迟”这个话题来,我早就同意如果要把单击和双击同一个按键定义完全不同的功能,那么这个时候对单击的识别延迟是必然的”

“MS的事件机制,如果要对单击、双击定义完全不同的功能,需要自己做类似的处理,但没有单击的延迟。窃以为,MS的方法更好一些”

==========
你已经定义了条件:“单击、双击定义完全不同的功能”!那么,当第一次单击后,任何系统都要等待,不可能马上去做单击的功能。它必须等待一定的时间,在这段时间内没有再次的按键,才能执行单击的功能。就是MS也必须这样。

那么如果MS不需要延时响应单击是必然要延时,你能具体说明它如何实现不等待的吗?你看到表象,好象MS的应用层收到的“单击”或“双机”事件,马上就去处理了,没有延时等待。但其实下层在给出“单击”事件前,已经吃掉延时时间。


下面给你一个参考的代码:

unsigned char key_read(void)  
{  
    static unsigned char key_m = key_state_0, key_time_1 = 0;  
    unsigned char key_return = N_key,key_temp;  
      
    key_temp = key_driver();  

    if (work_state == 上层不需要判断双击的时候)  
    {
        key_return = key_tmp;                       //此处提供的单击和长按的“事件”,单击不需要延时!
    }
    else if (work_state == 上层需要判断使用双击的时候)
    {
        switch(key_m)  
        {  
            case key_state_0:  
                if (key_temp == S_key )  
                {  
                    key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击  
                    key_m = key_state_1;  
                }  
                else  
                    key_return = key_temp;        // 对于无键、长键,返回原事件  
                break;  

            case key_state_1:  
                if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)  
                {  
                     key_return = D_key;           // 返回双击键事件,回初始状态  
                     key_m = key_state_0;  
                }  
                else                                 
                {                    // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键  
                     if(++key_time_1 >= 50)  
                     {  
                          key_return = S_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件  
                          key_m = key_state_0;     // 返回初始状态  
                      }  
                 }  
                 break;  
            }
      }
      return key_return;  
}

转过来吗?增加一个判断,根据应用层的需要,当不需要“双击”时,直接返回每次单击事件,没有延时。当上面需要单击、双击时,走下面。此时单击的响应肯定要推迟的。      

我说你没有“开窍”没有讽刺你的意思,是指你还没有真正掌握真谛,彻底的明白。

手机的按键,在不同的方式下,作用是不同的。比如你打开拨号盘输入电话号码打电话时,这个2号键就只有2个功能:单击和长按。
任何一次单击,马上输入2,没有延时等待,也没有ABC2的转换
按下2不放,那么需要等一段时间,然后调出预先设置好的2号快速拨号的整个号码。

你还是仔细体会和想想,对比这个键在不同情况下的不同表象。从我提供的参考代码中,已经出现了这样处理雏形。

==============================================
你是PC程序员吗?写过MS的低层?MS让你动低层吗?微软打输官司赔钱,都不公开低层代码。
 楼主| 发表于 2011-8-28 02:52:44 | 显示全部楼层
做好人做到底了,回答ifree64另一个要求:

“请问马老师,如何用2套底层驱动切换的方式实现类似windows资源管理器对鼠标单双击功能呢?”

=======================================================
还是在LZ位的代码上改动,实现“类似windows资源管理器对鼠标单双击功能”

unsigned char key_read(void)  
{  
    static unsigned char key_m = key_state_0, key_time_1 = 0;  
    unsigned char key_return = N_key,key_temp;  
      
    key_temp = key_driver();  
      
    switch(key_m)  
    {  
        case key_state_0:  
            if (key_temp == S_key )  
            {  
                 key_time_1 = 0;               // 第1次单击,返回事件,并到下个状态判断后面是否出现双击  
                 key_m = key_state_1;  
            }  
            key_return = key_temp;            // 对于无键、单击、长键,返回原事件,都是马上响应  
            break;  

        case key_state_1:  
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)  
            {  
                 key_return = D_key;           // 返回双击键事件,回初始状态  
                 key_m = key_state_0;  
            }  
            else                                 
            {                        // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键  
                 if(++key_time_1 >= 50)  
                 {  
                      key_m = key_state_0;     // 超过500ms,不可能有双击出现,返回初始状态  
                 }  
             }  
             break;  
    }
    return key_return;  
}
 楼主| 发表于 2011-8-28 02:55:26 | 显示全部楼层
我没有动最低层I/O扫描按键的函数,只是稍微调整中间的代码,三段不同代码,分别对应三种不同情况。如果还不“开窍”,偶也没有办法了。已经解释到家了。
发表于 2011-8-28 04:01:03 | 显示全部楼层
回复【32楼】machao  
-----------------------------------------------------------------------

&#160;&#160;&#160;&#160;2。请说明和解释C语言中以下定义的3个变量属于什么性质的变量:
&#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;unsigned&#160;char&#160;key_time&#160;=&#160;0;&#160;
&#160;&#160;&#160;&#160;unsigned&#160;char&#160;key_driver(void)&#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;{&#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;static&#160;unsigned&#160;char&#160;key_state&#160;=&#160;key_state_0;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;unsigned&#160;char&#160;key_return&#160;=&#160;N_key;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;........
&#160;&#160;&#160;
&#160;&#160;&#160;&#160;&#160;3。总结、归纳出在C中如何正确定义和使用变量,说明为什么。
&#160;&#160;&#160;&#160;&#160;。。。。。

&#160;&#160;&#160;&#160;现在,这样的题目不敢出了。不仅学生回答不出,而且就是许多教C的教师也说不明白(实际上,还有细分:比如:静态局部、静态外部等)。

给点提示:在上例中key_time是全局静态变量,key_state是局部静态变量,key_return是局部动态变量。它们之间的区别请仔细体会。&#160;

难一点的思考:有不有全局动态变量?如果有的话,是什么形式的,如何形成?为什么和什么情况下要使用全局动态变量?

给马老师纠个错,这里的key_time是全局非静态变量(因为没有加static修饰符,全局可见),如果一定要认为static与auto两个存储类别互斥,那么它就是个“全局自动变量”。
发表于 2011-8-28 07:38:40 | 显示全部楼层
回复【58楼】machao  
楼上的,你的帖子自己矛盾:
“回到“单击延迟”这个话题来,我早就同意如果要把单击和双击同一个按键定义完全不同的功能,那么这个时候对单击的识别延迟是必然的”
“ms的事件机制,如果要对单击、双击定义完全不同的功能,需要自己做类似的处理,但没有单击的延迟。窃以为,ms的方法更好一些”
==========
你已经定义了条件:“单击、双击定义完全不同的功能”!那么,当第一次单击后,任何系统都要等待,不可能马上去做单击的功能。它必须等待一定的时间,在这段时间内没有再次的按键,才能执行单击的功能。就是ms也必须这样。
那么如果ms不需要延时响应单击是必然要延时,你能具体说明它如何实现不等待的吗?你看到表象,好象ms的应用层收到的“单击”或“双机”事件,马上就去处理了,没有延时等待。但其实下层在给出“单击”事件前,已经吃掉延时时间。
下面给你一个参考的代码:
unsigned char key_rea......
-----------------------------------------------------------------------

抱歉我的那句话让你误解了,我表达得有问题。
我的意思是:ms的事件机制没有单击的延迟,但如果要对单击、双击定义完全不同的功能,需要自己做类似的处理(这时将引入单击延迟)。

我不是ms的程序员,但写过一些ms上的程序,虽然ms的底层不让动,但国内、外广大的系统研究爱好者、外挂作者、反外挂、反反外挂、杀软编写的人可是天天在ms系统的底层开战。扯远了。
发表于 2011-8-28 07:59:32 | 显示全部楼层
回复【60楼】machao  
我没有动最低层i/o扫描按键的函数,只是稍微调整中间的代码,三段不同代码,分别对应三种不同情况。如果还不“开窍”,偶也没有办法了。已经解释到家了。
-----------------------------------------------------------------------

我并不是不理解你的代码,我也没有“反对”过你“最底层”的io扫描代码。

你看我的这句话:
----------
“对于第一点,就用手机的英文输入来讨论吧,要输入“AAA”,按照马老师前面的观点,这是类似于你双击的延时的例子。但请注意虽然输入第一个A需要等待一段时间后才能确认这个A,但从按下A到屏幕上看到这个A是即时而没有延时的,而你中间层的双击识别代码却是需要500ms延迟的,我想这个才是关键。如果你说为了实现这样的要求,需要不同的“中间层”,那么好了,这本来就是我的讨论的出发点。”
----------
看到你为了不要这个单击延迟,写了一个不同的中间层代码,最后这个中间层代码类似于ms的处理机制。

仔细想想,让你非常满意的是这种“最底层"+不同“中间层”的代码可以针对不同需要而可有不同功能的按键识别。
让我不舒服的是,楼主位的“中间层”代码有单击延迟。当然这并不是问题,因为对于单击、双击要定义风马牛不相及功能的情况下必须得有这个延迟。
发表于 2011-8-28 08:09:44 | 显示全部楼层
我“开窍”了吗:

                          +  中间层1   +    应用层1
                          |
                          |
                          +  中间层2   +   应用层2
                          |
最底层i/o扫描  + |
                          +  中间层3   +  应用层3
                          |
                          |
                          +  中间层4 +   应用层4

不同应用层代表需要不同功能按键识别的情景,不同中间层对底层io进行这个转换,提供这个所需要的功能。
如果我把这个中间层理解为应用层的内部实现,也就是与应用层一层,就相当于应用层来识别单击、双击了。只是这样来理解,应用层内部分层了。

修改:
但,好像有一个问题:不同中间层代码是不能同时使用的,也不能即使用中间层又使用最底层,因为中间层使用的条件是:每10ms。
如果有两个任务需要两个不同的中间层,好像不能由这两个任务自己使用自己的中间层来处理按键,这会影响中间层的状态识别吧,按键是一个全局的资源,只能互斥访问。因此系统在一个时间段内只能有一个中间层处于活跃状态。因此只能这样用:

                         应用层
                             |
    ---------------------------------
    |                   |                   |                  |
状态1             状态2           状态3           状态4
  +                   +                  +                 +
中间层1         中间层2         中间层3        中间层4
   +-----------+---------+---------+
                               |
                         最底层IO

不同状态是应用层所处于的需要不同按键功能的不同阶段。
发表于 2011-8-28 08:18:09 | 显示全部楼层
mark
发表于 2011-8-28 08:39:05 | 显示全部楼层
讨论很激烈啊,学习了
发表于 2011-8-28 08:52:48 | 显示全部楼层
学到太多了,
发表于 2011-8-28 09:06:03 | 显示全部楼层
回复【5楼】ifree64
回复【2楼】surf_131   
很有意义的帖子。
我这几天在pic平台上也考虑过同样的东西,但是今天放弃了三种不同键识别的办法,改用一长一短两个键实现需要的功能。是在定时中断中设置几个变量来识别长短键。这样,短键可以在抬键后一个中断周期内快速响应,长键则在按下后3秒左右才开始响应,并能用查询法等待上次长键结束。
如果允许的双击间隔是500ms,则判断单点击要滞后500ms以上,太迟钝了。
-----------------------------------------------------------------------
在windows平台上,鼠标双击事件的顺序大致为键按下、键抬起、双击键按下;这样不会出现单击滞后500ms的问题了。

-----------------------------------------------------------------------

仔细想想,实际上是双击之前单击事件已经被执行了的,你想想是不是这样啊?但是在实际的产品设计中,这种设计可能是不行的。昨天刚做了一个,本来是只有单击和长按的,客户说长按太简单,改一下吧,于是改成连续快速单击3次,最后一次才长按,就响应,由于我喜欢在按键按下时就响应单击事件,于是就变成了连续三次响应单击事件,最后又响应了长按事件,好在是这个单击事件中只是变换一下屏幕的显示,无伤大雅,如果是有带动输出什么的,不是乱套了吗?
如果要在长安时不会执行单击的事件驱动,那么就只能在按键断开时进行处理了,注意我的长按事件的捕获也不是放在按键断开的位置的。
发表于 2011-8-28 09:39:16 | 显示全部楼层
楼上出现的问题之前已经讨论过了,就是单击删除,双击打开这个问题的变种。

还举手机拨号的例子,我用的手机NOKIA 1650,在拨号的状态下如果按“1”键,并且不抬起的话,屏幕上立即就显示“1”这个字符出现,也就是说你在按下这个键的瞬间,这个显示“1”字符的动作就被触发了,但是由于这个时候你没松手,所以在过一小段时间后就会转到快捷拨号的界面里去,但你不能说我本来就是要快捷拨号的,你显示个“1”给我干啥啊?但事实上显示这个“1”不仅表示你正常拨号状态下按了个“1”键,也表示你调出了快捷拨号列表中第一条的电话号码。所以这个显示的“1”是有双重意义的,具体做什么是要看具体情况的。但是在这个单击与双击事件中,这两种功能是没有冲突的,所以可以共存。

而你所说的“连续快速单击3次,最后一次才长按,就响应,由于我喜欢在按键按下时就响应单击事件,于是就变成了连续三次响应单击事件,最后又响应了长按事件,好在是这个单击事件中只是变换一下屏幕的显示,无伤大雅,如果是有带动输出什么的,不是乱套了吗?”这种完全是设计问题,你把界面的切换和系统输出功能的执行共用一个按键了,虽然一个是单击一个是双击,看似不矛盾,但事实上你的程序中的双击事件与单击事件是风马牛不相及的,所以才会出现混乱。假设你把单击设置为设定数值加一,双击设定数值加10的话估计就没那么纠结了,所以说到底还是设计问题。

所以说对于双击操作来说,在双击发生以前一定会触发一个单击事件,但是这个事件到底处不处理,怎么处理是需要程序员自己定义的,而不是要在所谓中间层帮助程序员把这个单击事件吃掉,只返回一个双击事件回来,也不是在单击事件发生后一定要等着双击事件的发生,等够了一段时间还没出现双击事件才把刚才发生的单击事件发送给上层进行处理。如果这样处理的话,那就要每个上层应用都有个不同的中间层才行,就像你在WIN上写了个程序,还要编个键盘驱动一样。
发表于 2011-8-28 09:59:35 | 显示全部楼层
mark
发表于 2011-8-28 10:02:28 | 显示全部楼层
JH
发表于 2011-8-28 10:56:18 | 显示全部楼层
还是igoal把这个问题阐释得明白点。
发表于 2011-8-28 11:14:49 | 显示全部楼层
哈哈挺热闹的啊
发表于 2011-8-28 11:18:12 | 显示全部楼层
哎,只能羡慕大家没遇到变态客户了。

我就接过一个项目,按键有短按和双击两种定义,而且明确规定双击之前不许有短按操作。这时候,先返回一次单击消息,再回个双击这种方法就不合适了。也就是说,你们这个方法也是有局限性的,不要太教条了。

还是马老师33楼说的好
“东西是死的,人是活的。只要你有能力,就能设计出合理的交互方式。”
发表于 2011-8-28 11:30:29 | 显示全部楼层
其实楼上不用这么纠结,“先返回一次单击消息,再回个双击这种方法”这种方法是可以使用的,这需要你在你的程序中做一下判断,在单击消息发生后一段时间没有双击消息被接收到就认为是单击,不过请注意:这个判断并不是发单击消息还是发双击消息的那一层中做的,而是在接收这个按键消息的代码层中做的。


这也就是我想讲的,下面一层的代码不要帮助上层的代码做本来属于上层代码的工作,否则就破坏了代码的分层机制。
发表于 2011-8-28 12:20:54 | 显示全部楼层
回复【75楼】igoal
------------------------
谢谢,我不纠结,只是一个常年把4k ROM用到4090字节的人,对你们这种思路表达羡慕嫉妒恨。

请注意马老师 56楼这2句
------------------------
而在嵌入式系统中,通常按键没有那么多的变化,功能是固定的,因此我的这个例子在低层把单次和长按直接做了判断,也就是简化了处理,使得整个系统比较简单和简洁。这个符合嵌入式系统软件硬件可剪裁的性质。

小的系统没有W那么多的资源,有必要写的那么庞大和复杂吗?在我LZ的例子中,已经是3层,最上层就是得到的:“有没有单击?有没有双击?有没有长按?”。
发表于 2011-8-28 12:23:05 | 显示全部楼层
mark
发表于 2011-8-28 13:05:07 | 显示全部楼层
回复【76楼】packer  
-----------------------------------------------------------------------

请参考农民讲习所通用程序的例子,在单片机中引入一个消息队列,并不会引入过分的资源占用吧,而且这样的消息队列并不仅仅只为
按键服务,为一个消息队列而付出的代价我认为是值得的,至少4096的flash还足以负担。
发表于 2011-8-28 14:15:17 | 显示全部楼层
简单处理的时候我也这样做,但老实说,用按键中断 + 定时器 可靠性才高也更容易将功耗降下去
另外,与或逻辑处理来识别长按或短按会更清晰一点,就像论坛中“三步按键识别”什么的,这样扩展性要好很多

PS:楼上很兄弟都拿“层”说事。清晰的软件设计哲学中最重要的名言是:Do one thing,and do it well!  
一个程序只做一件事,并且把它做好!
它的目标就是设计出高内聚、低耦合的源码。
其实一个函数就是一个抽象层。
不管有没有刻意去做,我们总将复杂的任务抽象出许多功能单一的不同的层。除非你的工程只有一个函数。
发表于 2011-8-28 14:23:53 | 显示全部楼层
回复【78楼】ifree64
-----------------------------------------------------------------------

再次表达对资源不受限的同志羡慕嫉妒恨。还引入消息队列,我经常1个字节RAM掰成2个nibble用(还有人知道什么是nibble吗)


回复【75楼】igoal
这也就是我想讲的,下面一层的代码不要帮助上层的代码做本来属于上层代码的工作,否则就破坏了代码的分层机制。
-----------------------------------------------------------------------
设计思想是这样的。但要因地制宜,灵活应用,不要太教条
发表于 2011-8-28 14:51:43 | 显示全部楼层
回复【80楼】packer  
回复【78楼】ifree64
-----------------------------------------------------------------------
再次表达对资源不受限的同志羡慕嫉妒恨。还引入消息队列,我经常1个字节ram掰成2个nibble用(还有人知道什么是nibble吗)
回复【75楼】igoal
这也就是我想讲的,下面一层的代码不要帮助上层的代码做本来属于上层代码的工作,否则就破坏了代码的分层机制。
-----------------------------------------------------------------------
设计思想是这样的。但要因地制宜,灵活应用,不要太教条

-----------------------------------------------------------------------
你这说法就不对了,我上面说了,“一个函数就是一个抽象层”。“层”大家都在用,只是不同的人有不同的抽象方式,而已。
“高内聚、低耦合”的设计方式前人的经验总结。
软件设计中,可维护性和或扩展性应该是放在第一位的,第二位才是性能。分层设计,将复杂的任务分成功能单一的不同的层
是有利于可维护性的,并且也提高了扩展性。

PS:最近开始流行进来的 函数式编程 将“高内聚、低耦合”提高到了新高度:函数的输出只由其输入条件来决定。
所以不会有隐式的输入条件更不会有全局变量这此间接影响输出的东西存在。
为什么要这样做?
发表于 2011-8-28 15:49:29 | 显示全部楼层
回复【80楼】packer  
回复【78楼】ifree64
-----------------------------------------------------------------------
再次表达对资源不受限的同志羡慕嫉妒恨。还引入消息队列,我经常1个字节ram掰成2个nibble用(还有人知道什么是nibble吗)
-----------------------------------------------------------------------

对这种资源首先的应用感兴趣,能说说你的资源情况并且要完成的功能吗?
 楼主| 发表于 2011-8-28 16:02:04 | 显示全部楼层
回复【64楼】ifree64
我“开窍”了吗:
                          +  中间层1   +    应用层1
                          |
                          |
                          +  中间层2   +   应用层2
                          |
最底层i/o扫描  + |
                          +  中间层3   +  应用层3
                          |
                          |
                          +  中间层4 +   应用层4
不同应用层代表需要不同功能按键识别的情景,不同中间层对底层io进行这个转换,提供这个所需要的功能......
-----------------------------------------------------------------------

快了。
=====================================================
拿PC的USB口做例子,这个USB口的低层驱动在MS安装后就有了(且看成类似I/O层面的一个低层,可以得到USB基本数据的通信)。但你第1次插入一个USB转RS232的DD(用过吗)就需要装驱动,第1次插入一个编程器也要装驱动。那么此时安装的驱动是什么?就是中间层!而编程器的话,还要安装应用软件,这就是应用层。
    这些安装的中间层就是对应不同的应用层,他们将低层基本数据拿到,然后经过转换,提供自己的应用层使用。
===========================================================
“但,好像有一个问题:不同中间层代码是不能同时使用的,也不能即使用中间层又使用最底层,因为中间层使用的条件是:每10ms。
如果有两个任务需要两个不同的中间层,好像不能由这两个任务自己使用自己的中间层来处理按键,这会影响中间层的状态识别吧,按键是一个全局的资源,只能互斥访问。因此系统在一个时间段内只能有一个中间层处于活跃状态。”

如果是在OS支持下,程序的写法需要改变,但思想方法是不变的。

你提到了“因此系统在一个时间段内只能有一个中间层处于活跃状态。”,不知道你是明白了还是疑问。难道系统在一个时间段内能有几个任务处于活跃状态吗?

由于系统在一个时间段内能有1个任务处于活跃状态,所以不存在同时调用不同中间处理的情况。

还是说MS的鼠标左键,你打开了2个窗口,一个是资源管理器(需要支持连击),另一个是编程器(不需要连击),那么你单击一下鼠标,2个窗口都会动作吗?不会吧。

鼠标的光标的位置,表明你现在哪个应用处在活动状态,你在资源管理器窗口下点击鼠标,资源管理器就调用自己的中间层,而在编程器窗口中,它调用另外的中间层。因此中间层的调用是不会出现冲突的。你把这个弄明白就可以了。

对于小的单片机系统,不必要,也不可能像MS处理鼠标按键那样来处理一个按键。如果真的要求这样的话,你可能还要努力学习半年3个月的。

我给的例子,实际就是一个中间层代码,因为使用状态机的方法,就可以合并。比如58楼的代码就是你认为的2个中间层。我合并成2个状态,分别处理。这2个状态,就是上面应用层的2个不同应用。

不管上面的应用是有十个还是100个,但每次只能是一个在活动中,这个也是“状态”,比如手机,你输入短信和打电话时输入电话号码不会同时发生,这就是2个不同的“应用状态”,这个状态将作为按键中间层的判断,根据上面的“应用状态”,进入不同的按键判别处理。

============================================
我不是ms的程序员,但写过一些ms上的程序,虽然ms的底层不让动,但国内、外广大的系统研究爱好者、外挂作者、反外挂、反反外挂、杀软编写的人可是天天在ms系统的底层开战。

井底之蛙的东西就不要多说了。你说的这些,基本上都不是真正的低层,还是中间层,甚至还只是中间层的上上层罢了。真正底层在哪里?在ROM中。你安装过什么程序,需要改写BIOS的?

=========================================================
实际上,编写单片机的程序就是从“BIOS”开始的,这个BIOS不可能,也不需要像BIOS那样如此的全面,只要适合自己系统的专用就可以了。

当你的系统需要一个按键有单击和双击的操作,而且是执行不同的功能,那么单击的响应就要拖后了。如果认为这个拖后不能忍受,那么就要重新定义操作方式,比如换成单击和和长按,更简单是使用2个按键,不同键的单击执行不同的任务。

哪种操作方式好,是你系统设计所决定的。我只是按要求实现一个多功能键的代码设计。

这个实际就是一个基础,当你掌握了一个多功能键如何去实现,以及其特点后,你才会更好的去考虑并和用户商量整个系统采用如何操作,是使用一个多功能按键,还是使用2个键?使用2个键,操作简单;使用1个多功能键,操作复杂点,单击响应慢点,单成本低,空间节省。

如果采用后者,那么你再分析,是否系统在整个运行阶段中是否都需要多功能键?还是只是在某个阶段需要多功能键。

许多系统,当在查看、选择过程中只需要单击,仅在设置参数时需要多功能键操作,而且设置参数平时很少使用,那么就把系统运行分成不同状态阶段。按键中间层根据不同阶段,处理判断按键,当上层不是处在设置参数时,就不判断是否有双击。这样做处理,就能做到比较好的平衡。

手机键盘就是一个很好的例子。手机不可能装26个键,要在十个键上输入26个字母,你只能采用多功能键。只要是多功能,就要带来延时响应的问题。

比如连续输入8个2:22222222
你在短信输入时,花费的时间肯定比打电话拨号要慢的多。打电话拨号,你可以连续快速的按8次“2”,而短信输入时,你按一下“2”,必须等一下。

在短信输入情况下,你感觉2的显示响应很快,好象单击没有延时,但是光标却没有移到下个位置,要延时500ms,当在这段时间内没有按键的话,才最后确认是2,移动光标。如果我设计成按下2不显示,过500ms没有按键再显示2并光标移到下一个位置的话。这两种设计对于输入8个2操作,整体需要的时间效果是相同的。

不同的是,前一种比较人性化,操作者很快知道自己的选择是否对的,然后等待。而第二种就不行了,快速按了几次“2”号键,自己也不知道是2还是A、B、C了,当你看到字母已经确认了,不对还的改。

从程序角度,实现这样的操作,延时不可避免,只是把延时放在什么地方合理(这个就是我前面说的朝三暮四的比喻。)

[我有时把程序员比做耍猴人,使用者比做猴子,程序员要动脑筋,有本事,去满足使用者的需要。这里没有讽刺贬低任何人的含义,因为我自己也是猴子,用PC、用手机....不就是在用其他程序员写的程序吗。]

作为程序员,如果你两种方式的操作都能实现,也知道其不同点,那么你就具备能力,可以整体考虑人机操作界面应该选择什么操作方式为最好。

就上面讲的手机输入例子,当然是第一种方式为好,那么照搬我LZ位的代码肯定不行,可是稍微改一下不就可以了吗?

没有什么程序是万能的,关键是方法和思路你是否真正掌握(此时则开窍了),代码只是体现。
========================================================================

我自己也没了到,一个多功能按键引出的争论,排除纯技术原因,还反映出什么?这个到是值得深思和反省。
发表于 2011-8-28 16:08:09 | 显示全部楼层
回复【81楼】ufbycd
软件设计中,可维护性和或扩展性应该是放在第一位的,第二位才是性能。
-----------------------------------------------------------------------

sorry,在我的现实生活中,完成项目(很多时候是在用户限定资源内)拿到设计费才是第一位的。第二位是可靠性。你说的都要往后排。

回复【82楼】ifree64

主要在家电领域,成本极其敏感,而且经常甲方非常强势,做不到就不用你的。
 楼主| 发表于 2011-8-28 16:56:30 | 显示全部楼层
回复【84楼】packer
回复【81楼】ufbycd  
软件设计中,可维护性和或扩展性应该是放在第一位的,第二位才是性能。
-----------------------------------------------------------------------
sorry,在我的现实生活中,完成项目(很多时候是在用户限定资源内)拿到设计费才是第一位的。第二位是可靠性。你说的都要往后排。
回复【82楼】ifree64  
主要在家电领域,成本极其敏感,而且经常甲方非常强势,做不到就不用你的。

-----------------------------------------------------------------------

认同。

PC程序员和嵌入式程序员不同,就是嵌入式程序员还可以分为有无OS支持,也是不同的。对于在WINCE等平台上做嵌入式程序的,与PC区别不大,比较少,或者可以根本不去考虑硬件底层和驱动。而从单片机的程序员不同,在硬件设计阶段,就需要从软件编程出发考虑问题了,所有的最底层,中间和应用,整个系统都要考虑。

就我个人观点:最牛的属于后一种程序员(当然是具备相当水平的)。让他在PC上做事情编程,熟悉3个月,就可以独立。而一个好的PC程序员,他学习一年,甚至再多的时间,也不能独立的做单片机的事情。

我的这个专栏是讨论学习AVR的应用,8位的DD,不可能跑MS、也不可能作为手机、GPS的主控处理。因此,在有限资源下,可靠的完成规定的性能是首要的,这样就可以拿设计费了。可维护性和或扩展性是在可能的情况下才考虑的(资源许可、时间许可等)。

我LZ位的代码,首先肯定是可靠、稳定的实现了规定的功能,并同时体现了一定的可维护性和扩展性。使用了一个中间层次的目的也在与此。
发表于 2011-8-28 18:07:18 | 显示全部楼层
回复【83楼】machao  

你提到了“因此系统在一个时间段内只能有一个中间层处于活跃状态。”,不知道你是明白了还是疑问。难道系统在一个时间段内能有几个任务处于活跃状态吗?

由于系统在一个时间段内能有1个任务处于活跃状态,所以不存在同时调用不同中间处理的情况。
-----------------------------------------------------------------------

unsigned char key_driver(void);                // 最底层

unsigned char key_read_1(void);                // 中间层1
unsigned char key_read_2(void);                // 中间层2
unsigned char key_read_3(void);                // 中间层3

如果有三个任务分别使用三个不同的中间层:

void task1(void)
{
        unsigned char key;
        //???
        while(1)
        {
        if(f10ms == 1)
        {
                f10ms = 0;
                key = key_read_1();
        }
        }
}

void task2(void)
{
        unsigned char key;
        //???
        while(1)
        {
        if(f10ms == 1)
        {
                f10ms = 0;
                key = key_read_2();
        }
        }
}


void task3(void)
{
        unsigned char key;
        //???
       
        while(1)
        {
        if(f10ms == 1)
        {
                f10ms = 0;
                key = key_read_3();
        }
        }
}

虽然task1、task2、task3不会同时运行,但通常它们是轮换着得到cpu时间,在各自的时间片里去调用
不同的中间层,但这些不同的中间层最终使用同一个底层,由于时间片通常实在ms级别的,最终还不得导
致所有状态乱套吗?除非一个task运行时另外的task在睡眠。



================
还是说MS的鼠标左键,你打开了2个窗口,一个是资源管理器(需要支持连击),另一个是编程器(不需要连击),那么你单击一下鼠标,2个窗口都会动作吗?不会吧。

鼠标的光标的位置,表明你现在哪个应用处在活动状态,你在资源管理器窗口下点击鼠标,资源管理器就调用自己的中间层,而在编程器窗口中,它调用另外的中间层。因此中间层的调用是不会出现冲突的。你把这个弄明白就可以了。
-------------------------------------------------------------------------
Windows是如何做到这个的我还是明白,但用你的中间层如何做到我就不明白了。这两个应用程序会按照系统的任务调度算法得到自己的CPU时间,
在自己的CPU时间里如何去每10ms调用一次中间层获取按键;两个应用程序轮流获得cpu时间的间隔可能就在10ms左右。这个情况下只能是系统
检测鼠标按键,根据当前哪一个是激活窗口,然后向哪一个激活窗口的消息队列发送鼠标键盘消息。恕我愚钝,实在想不出怎样在这样的情况下使用
不同的中间层,特别是这个中间层不是一个普通函数,而是一个需要每10ms调用的函数。



============================================
我不是ms的程序员,但写过一些ms上的程序,虽然ms的底层不让动,但国内、外广大的系统研究爱好者、外挂作者、反外挂、反反外挂、杀软编写的人可是天天在ms系统的底层开战。

井底之蛙的东西就不要多说了。你说的这些,基本上都不是真正的低层,还是中间层,甚至还只是中间层的上上层罢了。真正底层在哪里?在ROM中。你安装过什么程序,需要改写BIOS的?

=========================================================
井底之蛙是指的什么?很愿意听听我这方面认识到底局限在哪里?谢谢。
发表于 2011-8-28 18:39:26 | 显示全部楼层
mark
发表于 2011-8-28 18:59:33 | 显示全部楼层
好贴,学习了
 楼主| 发表于 2011-8-28 20:19:28 | 显示全部楼层
回复【86楼】ifree64
-----------------------------------------------------------------------

不要再多说了,越说越不靠谱,会越丢人的。

我在83楼已经提到过“如果是在OS支持下,程序的写法需要改变,但思想方法是不变的。”

现在我知道,我是高估你了。原先还以为你对操作系统OS还有点概念,可能只是在某个环节上卡主了。现在我知道,你对OS下的编程的思想基本是空白。

现在已经没有继续讨论何解释的必要了。还是建议多学习学习,掌握了一定的基础再说吧。
发表于 2011-8-28 22:00:12 | 显示全部楼层
回复【86楼】ifree64
回复【83楼】machao   
=========================================================  
井底之蛙是指的什么?很愿意听听我这方面认识到底局限在哪里?谢谢。
-----------------------------------------------------------------------

本来我还说马老师太尖刻,看了这个帖子看来一点不过。你对整个OS概念很混乱,根基很差,知其然不知其所以然。
就知道时间论片,但不知道时间片是建立在什么基础上。

建议买本ucos-ii的书,从头照着一段段实现,你很多迷惑的东西迎刃而解。
发表于 2011-8-28 22:21:52 | 显示全部楼层
回复【89楼】machao  
回复【86楼】ifree64
-----------------------------------------------------------------------
不要再多说了,越说越不靠谱,会越丢人的。
我在83楼已经提到过“如果是在os支持下,程序的写法需要改变,但思想方法是不变的。”
现在我知道,我是高估你了。原先还以为你对操作系统os还有点概念,可能只是在某个环节上卡主了。现在我知道,你对os下的编程的思想基本是空白。
现在已经没有继续讨论何解释的必要了。还是建议多学习学习,掌握了一定的基础再说吧。
-----------------------------------------------------------------------

在我的贴子里写的那些代码当然是不能工作的。
不引入消息机制,以现在的中间层方法我不明白你如何能做到不同应用层使用不同中间层代码的。

哦,对了你说了os支持下写法需要改变,思想不变。把你现在的写法生硬的往多任务的方式套自然丢人了,不过丢人没关系,能从中学到点新东西就行了。
发表于 2011-8-28 22:23:35 | 显示全部楼层
状态机确实很好。。。
发表于 2011-8-28 22:24:46 | 显示全部楼层
回复【90楼】packer  
回复【86楼】ifree64
回复【83楼】machao   
=========================================================  
井底之蛙是指的什么?很愿意听听我这方面认识到底局限在哪里?谢谢。
-----------------------------------------------------------------------
本来我还说马老师太尖刻,看了这个帖子看来一点不过。你对整个os概念很混乱,根基很差,知其然不知其所以然。
就知道时间论片,但不知道时间片是建立在什么基础上。
建议买本ucos-ii的书,从头照着一段段实现,你很多迷惑的东西迎刃而解。
-----------------------------------------------------------------------

那就请你先上一课吧,我对os的概念混乱在哪里?时间片建立在什么基础之上?
发表于 2011-8-28 23:16:30 | 显示全部楼层
回复【93楼】ifree64
-----------------------------------------------------------------------

UCOS-II是我遇到的最简单的OS,在51上就能实现,而且参考书很多,所以我推荐它。如果你能耐心的把它读完,86楼的问题你就不会问了。

另外,把马老师楼主位的这句话再写一遍,希望大家共勉
---------------------------------------------------
最近频繁出现的塌桥、电梯事故、火车出事,都是上天对现在浮躁社会的报应。我们都抱怨别人不负责,浮躁,那么你自己呢?
发表于 2011-8-29 02:06:00 | 显示全部楼层
回复【84楼】packer  
回复【81楼】ufbycd
软件设计中,可维护性和或扩展性应该是放在第一位的,第二位才是性能。
-----------------------------------------------------------------------
sorry,在我的现实生活中,完成项目(很多时候是在用户限定资源内)拿到设计费才是第一位的。第二位是可靠性。你说的都要往后排。
回复【82楼】ifree64
主要在家电领域,成本极其敏感,而且经常甲方非常强势,做不到就不用你的。

-----------------------------------------------------------------------
无论有没有OS,当你做了稍大点的项目,你就会发现我说的没错。
在稍大点的项目中,可维护性和可扩展性直接影响到项目的成功与否及以后的维护成本的大小,
并且在设计过程中,如果需求不断变化没有好的可维护性和可扩展性你往往都是在重头开始,不断推翻之前的代码。
还有如果有多人参加同一项目……
好了,困了,不说了,睡觉……
发表于 2011-8-29 07:40:05 | 显示全部楼层
回复【95楼】ufbycd
-----------------------------------------------------------------------

这么打个比方,有个项目招标,为了可维护性和可扩展性,我需要用16krom的芯片,价格20元。而放弃可维护性和可扩展性可以用8k芯片,价格10元。(仅仅是个比方)
这时候你说我应该把谁放第一位?
而在项目资源足够的情况下,我不考虑可维护性和可扩展性,只能说我弱智。
所以在我看来,一切前提是完成项目。可能是在严酷的市场竞争中拼打多年,变的太现实了。

再回顾 81楼
-----------------------------------------------------------------------
igoal:这也就是我想讲的,下面一层的代码不要帮助上层的代码做本来属于上层代码的工作,否则就破坏了代码的分层机制
我:设计思想是这样的。但要因地制宜,灵活应用,不要太教条
你:你这说法就不对了,。。。
-----------------------------------------------------------------------

我自认为说的滴水不漏。首先,我赞同这种思想,并在实际工作中也贯彻这种思想。只是提醒大家不要太僵化。
发表于 2011-8-29 07:45:41 | 显示全部楼层
记录一下。
发表于 2011-8-29 08:26:26 | 显示全部楼层
留个脚印
发表于 2011-8-29 08:38:46 | 显示全部楼层
技术贴,mark
友情提示:标题不合格、重复发帖,将会被封锁ID。详情请参考:论坛通告:封锁ID、获得注册邀请码、恢复被封ID、投诉必读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2019-8-22 16:57

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

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

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