amobbs.com 阿莫电子技术论坛

标题: [分享][交流]发一个通用按键模块,简单易用 [2014-3-24 Update] [打印本页]

作者: y574924080    时间: 2013-7-17 18:56
标题: [分享][交流]发一个通用按键模块,简单易用 [2014-3-24 Update]
本帖最后由 Gorgon_Meducer 于 2014-3-24 22:51 编辑


傻孩子工作室作品

这是一个通用的按键模块,使用简单!支持最多255个按键,每个按键都支持包括按下检测、
松手检测、长按、连按的效果。内置去抖滤波处理,内置按键队列。而且是平台无关,全状
态开发的!



更新日志

    - 2014-3-24
        a. 修改key_queue接口:增加中断保护修改注释风格
        b. 更新Demo工程,并将key_task放入到system tick 异常处理程序中




下面我们开始进入正题:

0、        准备工作:
你现在需要一个有串口、有两个独立按键、有独立LED指示灯的单片机系统。为了体现平台无
关,请参考文档[attach]125819[/attach]搭建平台,该平台提供了串口收发函数、LED控制
接口函数、独立按键读取函数。这样我们就拥有一个共同讨论的基础。

我现在使用的是lpc1227的板子,提供了一个完整基于lpc1227的范例,但是基于之前搭建的平
台,代码移植起来是非常容易的。

在平台搭建完成的基础上,你仅仅需要提供一个获取按键键值函数就可以使用按键模块了。

1、        解压模块
[attach]178440[/attach]
将按键模块源代码下载下来,你可以看到里面有一个app_cfg.h文件,一个oopc.h文件,一个
key文件夹,将其解压到和工程同级的目录中,同时在你的工程中包含源文件key.c(key文件内)
和key_queue.c(key的子文件夹key_queue内)。

2、编写按键获取键值函数

你需要编写自己的按键键值获取函数。例如我在main.c中编写好了该函数:
  1. #define KEY_1                1
  2. #define KEY_2                2
  3. /*************************************************************************
  4. * Function Name: get_key_scan_value
  5. * Parameters: void
  6. * Return: return key value
  7. *
  8. * Description:
  9. *
  10. *************************************************************************/
  11. uint8_t get_key_scan_value(void)
  12. {
  13.     if (IS_KEY1_DOWN()) {   //该宏在平台搭建中有说明
  14.         return KEY_1;
  15.     }
  16.        
  17.     if (IS_KEY2_DOWN()) {   //该宏在平台搭建中有说明
  18.         return KEY_2;
  19.     }
  20.        
  21.     return KEY_NULL;
  22. }
复制代码
这里只是使用两个简单的独立按键。在实际工程应用中,可以根据你的情况来调整。需要
注意的是返回的键值不能为0 (它已经被KEY_NULL使用了)。

3、注册按键获取键值函数
打开主目录下的app_cfg.h,添加代码,注意宏GET_KEY_SCAN_VALUE(),示例如下:
  1. #ifndef __APP_CFG_H__
  2. #define __APP_CFG_H__

  3. #include <stdbool.h>
  4. #include <stddef.h>

  5. #include "oopc.h

  6. #define KEY_QUEUE_SIZE                 10
  7. #define KEY_COUNT                         100
  8. #define KEY_LONG_TIME                 30000
  9. #define KEY_REPEAT_TIME                20000

  10. //在这里声明自己编写的按键键值获取函数
  11. extern uint8_t get_key_scan_value(void);
  12. //用宏来注册
  13. #define GET_KEY_SCAN_VALUE()     get_key_scan_value()

  14. #endif /* __APP_CFG_H__ */
复制代码
4、接口使用说明
经过以上的步骤,按键模块已经移植完成了。使用时,在main.c函数中包含头文件key.h:
  1. #include ".\key\key.h"
复制代码
然后调用接口函数即可。接口函数是什么?有哪些?
嗯,我们先来看看接口头文件”key.h”:
  1. #ifndef __KEY_H__
  2. #define __KEY_H__

  3. #include ".\app_cfg.h"

  4. #include "key_interface.h"

  5. #define KEY_NULL        0

  6. extern void init_key_task(void);
  7. extern bool key_task(void);
  8. extern bool get_key(key_event_t* ptKey);

  9. #endif /* __KEY_H__ */
复制代码
可以看到,该模块提供了三个函数,这就是接口函数!介绍如下:
void init_key_task(void)为初始化按键模块函数,只需要在初始化时调用一次即可。
bool key_task(void)为按键任务函数,在超级循环或者定时器中断处理程序中被周期性的调用
  1. int main(void)
  2. {
  3.     …
  4.     init_key_task();
  5.     …
  6.     while(1) {     
  7.     …
  8.     //需要在超级循环或者定时器中断处理程序中被周期性的调用      
  9.     key_task();
  10.     …
  11.     }
  12. }
复制代码
bool get_key(key_event_t* ptKey) 用来获取按键:
    当返回false时,表示没有获取到有效按键;
    当返回true时,表示获取到有效按键,通过传入的参数读取按键信息。
结构体key_event_t定义在key_interface.h中,该结构体包含一个按键的全部信息:键值和按键事件类型。

key_interface.h

  1. typedef struct{
  2.     uint8_t chKeyValue;
  3.     key_status_t tEvent;
  4. }key_event_t;
复制代码
简单实用范例:

  1. do {
  2.     key_event_t tKey;
  3.     if  (!get_key(&tKey)) {
  4.         break;
  5.     }
  6.     if ((KEY_1 == tKey.chKeyValue) && (KEY_LONG_PRESSED == tKey.tEvent)) {
  7.     //! 识别KEY_1的长按键事件
  8.     …
  9.     }
  10. } while(false)
复制代码
说了那么多,还没有上代码呢,请看下面的DEMO:

  1. #include “key.h”

  2. /*************************************************************************
  3. * Function Name: main
  4. * Parameters: none
  5. *
  6. * Return: none
  7. *
  8. * Description: main
  9. *
  10. *************************************************************************/
  11. int main(void)
  12. {
  13.     …
  14.     // init_key_task ()初始化时调用一次即可
  15.     init_key_task ();
  16.     …
  17.     while(1) {      
  18.         // key_task()需要在超级循环或者定时器中断处理程序中被周期性的调用      
  19.         key_task();
  20.         //在这里添加你的按键应用代码
  21.         key_app();
  22.         …
  23.     }
  24. }
  25.        

  26. /*************************************************************************
  27. * Function Name: key_app
  28. * Parameters: void
  29. * Return: return FSM state
  30. *
  31. * Description:
  32. *
  33. *************************************************************************/
  34. static bool key_app(void)
  35. {
  36.     static uint8_t s_chState = KEY_APP_START;
  37.     static key_event_t s_tKey = {KEY_NULL, KEY_UP};
  38.     switch (s_chState) {
  39.         case KEY_APP_START:
  40.             s_chState = KEY_APP_GET;
  41.             //break;
  42.                        
  43.         case KEY_APP_GET:
  44.             if (get_key(&s_tKey)) {     //调用获取按键,根据按键键值和事件类型不同做不同的处理
  45.                 if (KEY_1 == s_tKey.chKeyValue) {                                       
  46.                     if (KEY_DOWN == s_tKey.tEvent) {
  47.                         s_chState = KEY_APP_DOWN;
  48.                     } else if (KEY_UP == s_tKey.tEvent) {
  49.                         s_chState = KEY_APP_UP;
  50.                     } else if (KEY_PRESSED == s_tKey.tEvent) {
  51.                         s_chState = KEY_APP_PRESSED;
  52.                    } else if (KEY_LONG_PRESSED == s_tKey.tEvent) {
  53.                        s_chState = KEY_APP_LONG_PRESSED;
  54.                    } else if (KEY_REPEAT == s_tKey.tEvent) {
  55.                        s_chState = KEY_APP_REPEAT;
  56.                    }
  57.                 } else if (KEY_2 == s_tKey.chKeyValue) {                                       
  58.                     if (KEY_DOWN == s_tKey.tEvent) {
  59.                         s_chState = KEY_APP_DOWN2;
  60.                     } else if (KEY_UP == s_tKey.tEvent) {
  61.                         s_chState = KEY_APP_UP2;
  62.                     } else if (KEY_PRESSED == s_tKey.tEvent) {
  63.                         s_chState = KEY_APP_PRESSED2;
  64.                     } else if (KEY_LONG_PRESSED == s_tKey.tEvent) {
  65.                         s_chState = KEY_APP_LONG_PRESSED2;
  66.                     } else if (KEY_REPEAT == s_tKey.tEvent) {
  67.                         s_chState = KEY_APP_REPEAT2;
  68.                     }
  69.                 }
  70.             }
  71.             break;
  72.                        
  73.         case KEY_APP_DOWN:
  74.             if (!print_key1_down()) {       //这是一个通过串口打印字符串"KEY1 DOWN\r\n"的状态机
  75.                 KEY_APP_RESET_FSM();
  76.                 return false;
  77.             }
  78.             break;
  79.                        
  80.         case KEY_APP_UP:
  81.             if (!print_key1_up()) {         //这是一个通过串口打印字符串"KEY1 UP\r\n"的状态机
  82.                 KEY_APP_RESET_FSM();
  83.                 return false;
  84.             }
  85.             break;
  86.                 …
  87.                 //省略一部分代码
  88. }
  89. }
复制代码
完整的范例,可以点这里下载:[attach]178441[/attach]
效果图:
[attach]125816[/attach]

4、参数调整
        经过上面的几个步骤,按键模块已经可以使用了,当然不同MCU的速度不同,所以提供
了以下宏来调整参数,使之更符合你的情况。

  1. //内置队列大小
  2. #define KEY_QUEUE_SIZE                 10
  3. //按键连续检测次数
  4. #define CHECK_KEY_COUNT                 20
  5. //长按计数
  6. #define KEY_LONG_TIME                 30000
  7. //连击计数
  8. #define KEY_REPEAT_TIME                20000
复制代码
5、注意事项:
        A.本模块是基于全状态开发的,占用处理器时间及少,同时key_task()函数也需要在超
级循环或者定时器中断处理程序中被周期性的调用。
        B.有效键值不能为0 (KYE_NULL已经占用了键值0)

个人水平有限,欢迎拍砖!



数据流图
[attach]150368[/attach]


状态图

[attach]161676[/attach]
[attach]161677[/attach]
[attach]161678[/attach]
作者: qufuta    时间: 2013-7-17 19:02
高手,真的是高手,果然是精华帖,先MARK一下,下次再来看
作者: hyghyg1234    时间: 2013-7-17 19:17
好贴,顶起
作者: mcu_lover    时间: 2013-7-17 19:38
呵呵,不错。我已经用了好几年了。和你的方式一模一样,调用接口也一致,不同的是我把键值和按键状态封装在一个字节里。这样实际按键数量只支持16个(4bits for key value , 4 bits for key event)
如果和你那样,分开两个字节,也就和你一样,可以支持255键了。
接口:
[attach]125824[/attach]
调用方式:
[attach]125825[/attach]
方式和楼主位基本一致,就不传源码了。
作者: bbsview    时间: 2013-7-17 19:39
没有矩阵的吗?单按键口IO耗费比较大呀
作者: y574924080    时间: 2013-7-17 20:06
mcu_lover 发表于 2013-7-17 19:38
呵呵,不错。我已经用了好几年了。和你的方式一模一样,调用接口也一致,不同的是我把键值和按键状态封装在 ...

这都是傻孩子老师教的,算是培训的阶段总结吧
作者: D.lovers    时间: 2013-7-17 20:06
mark,学习

作者: y574924080    时间: 2013-7-17 20:09
bbsview 发表于 2013-7-17 19:39
没有矩阵的吗?单按键口IO耗费比较大呀

无论是独立按键还是矩阵按键,都可以使用这个模块,它们只有获取键值函数不一样,这是由用户自己编写的
作者: SNOOKER    时间: 2013-7-17 20:22
mcu_lover 发表于 2013-7-17 19:38
呵呵,不错。我已经用了好几年了。和你的方式一模一样,调用接口也一致,不同的是我把键值和按键状态封装在 ...

这代码写得好漂亮啊。

按键状态3个位不够用吗
作者: zhanghuhhhhh    时间: 2013-7-17 20:28
本帖最后由 zhanghuhhhhh 于 2013-7-17 20:36 编辑

买了傻孩子的书。发现函数和变量名,没有楼上的mcu_lover   写的好。有可能是写大的工程多了,养成的习惯,希望傻孩子,以后写书,学学杜洋或其它作者,代码写的争取小孩都明白,变量名中有的下划线,没有必要让初学者看啊。大工程有可能是必要的。



这个通用模块很好,只是如果函数名,变量名,短点,易懂点。

结构体尽量不用就更好了。
名子短,用于工程中出问题,移植者会再加长的。
作者: 一路向南    时间: 2013-7-17 20:35
学习了,楼主和mcu_lover的方法都不错,话说很期待mcu_lover博文更新。
作者: mcu_lover    时间: 2013-7-17 20:43
SNOOKER 发表于 2013-7-17 20:22
这代码写得好漂亮啊。

按键状态3个位不够用吗

按键识别如下几种状态,每种状态分别有一位标志对应:
按键按下
按键弹起
按键长按
按键连按
共需要4 bits。
作者: y574924080    时间: 2013-7-17 21:26
zhanghuhhhhh 发表于 2013-7-17 20:28
买了傻孩子的书。发现函数和变量名,没有楼上的mcu_lover   写的好。有可能是写大的工程多了,养成的习惯, ...

额,不知道如何表述,所以翻了翻代码大全

我很同意这句话:为变量命名最重要的考虑事项是,该名字要完全,准确地描述出该变量所代表的事物。

所以变量名我倒是不觉得长,倒是不易懂这的确是不够好,希望指出,我好改进

作为模块我觉得避免和以后命名冲突是很重要的,一个模块写好后就不应该随便修改

结构体我倒是觉得是一个很好的东西。。。
作者: yiming988    时间: 2013-7-17 21:38
zhanghuhhhhh 发表于 2013-7-17 20:28
买了傻孩子的书。发现函数和变量名,没有楼上的mcu_lover   写的好。有可能是写大的工程多了,养成的习惯, ...

结构体和指针是C的精华,不用的话很多东西没办法实现,只能拳打脚踢单打独斗了 呵呵。我感觉学了一点入门了以后,那些看似复杂了点的代码实际帮了我们很大的忙,看起来复杂,用起来很方便。 围观学习。。。
作者: Gorgon_Meducer    时间: 2013-7-17 21:40
zhanghuhhhhh 发表于 2013-7-17 20:28
买了傻孩子的书。发现函数和变量名,没有楼上的mcu_lover   写的好。有可能是写大的工程多了,养成的习惯, ...

谢谢你的建议,我书中的编码风格已经是五年前的东西了,现在已经完全不同了。
萝卜青菜各有所爱吧。我还是比较喜欢长命名风格。另外,所谓模块,就是写好了
原则上不会修改的,这次的模块正是我培训学员如何进行模块化封装的一个结果。
需要发生变化的部分比如按键扫瞄,各类参数的宏配置输入等等都处理好了。使用
的时候是绝对不允许修改模块中的代码。坚持这一点,你才能体会出模块化的好处,
也才能自己也进行面向接口开发的实践。
作者: zhanghuhhhhh    时间: 2013-7-17 21:47
各人面向的价层不一样吧。

杜洋的书里的代码,不用宏,不用指针,不用结构体。小学生也跟着学。淘宝双皇冠。

初学者,看你的书有点难度,估计水平高了。就好点了。

新书什么时候出来啊。
我再买来学习一下。
作者: zhanghuhhhhh    时间: 2013-7-17 21:51
发一下我的按键程序吧。

是用本坛的“三行按键”基础上改的,阿莫论坛,就是神!

互相学习,提高吧。
#include "../hfile/config.h"
uint16 GCountLongPressTime;
uint8 GTrigger=0;
uint8 GHold=0;
uint8 g_KeyValue=0x00;
uint8 g_LongPressKeyValue=0x00;
/**********************************************************************
//this routine maybe put in the mainlop.no need decrease counter.
//it take the loop as a delay!
**********************************************************************/
uint8 GetPinValue(void )
{
  uint8 KeyPort=0xff;
  uint8 Key0value=0x00;
  uint8 Key1value=0x00;
  uint8 Key2value=0x00;
  uint8 Key3value=0x00;
  //if(0==PIN(B,3))   #define PIN(m,n)
  if(
     0==(
         !(uint8)(GET_BIT(DDRB).bit3=0)&&\
           (GET_BIT(PORTB).bit3=1)&&\
             GET_BIT(PINB).bit3
               //(uint8)( GET_BIT(PORTB).bit3=1)&&\//
               )
       )
   
    Key0value=0x01;
  if(0==PIN(D,4))
    Key1value=0x02;
  if(0==PIN(D,3))
    Key2value=0x04;
  if(0==PIN(B,2))
    Key3value=0x08;
  KeyPort=KeyPort&(~Key0value)&(~Key1value)&(~Key2value)&(~Key3value);
  return KeyPort;
}
void ThreeLineKeyRead(void)//读按键
{
  uint8 virtualPinValue=GetPinValue();
  uint8 ReadData=0;
  ReadData=virtualPinValue^0xff;
  GTrigger=ReadData&(ReadData^GHold);
  GHold=ReadData;
}
void  WhichKeyIsShortPress(void)//短按键
{
  if(GTrigger&0x01)
  {
    g_KeyValue=KEYCH;//左数第1个按键
  }
  
  if(GTrigger&0x02)
  {
    g_KeyValue=KEYADD;//左数第2个按键
  }
  if(GTrigger&0x04)
  {
    g_KeyValue=KEYDEC;//左数第3个按键
  }
  if(GTrigger&0x08)
  {
    g_KeyValue=KEYSET;//左数第4个按键
  }
  if(g_KeyValue!=0)g_SystemTime60s=0;
}

void  WhichKeyIsLongPress(void) //长按键
{
  if(GHold){GCountLongPressTime++;}
  else {
    GCountLongPressTime=0;
    g_LongPressKeyValue=0;
  }  
  if(GCountLongPressTime>500)//65500
  {
    if(GHold&0x02)
    {
      g_LongPressKeyValue=longKEYADD;//左数第2个按键
    }
    if(GHold&0x04)
    {
      g_LongPressKeyValue=longKEYDEC;//左数第3个按键
    }
    if(GHold&0x08)
    {
      g_LongPressKeyValue=longKEYSET;//左数第4个按键
    }
  }
}

void KeyRead(void)
{
  ThreeLineKeyRead();//读按键
  WhichKeyIsShortPress();
  WhichKeyIsLongPress();
}

作者: liangws201    时间: 2013-7-17 21:52
不错,很工整!
作者: Gorgon_Meducer    时间: 2013-7-17 21:52
zhanghuhhhhh 发表于 2013-7-17 21:51
发一下我的按键程序吧。

是用本坛的“三行按键”基础上改的,阿莫论坛,就是神!

多谢分享
作者: zhanghuhhhhh    时间: 2013-7-17 21:54
原作者的程序


void KeyRead( void )
{
     unsigned char ReadData = P1^0xff;   // 1// 当前I/O状态(“真”或“假”)
     Trg = ReadData & (ReadData ^ Hold);      // 2   // 如果 当前状态为“真”,且当和“上次状态”不一致,则Trg为“真”
         //第二句的意思是:如果当前检测到按键按下且上次未按下,则是一次有效的触发;如果连续按下,则只视为一次有效触发。
         Up = (~ReadData) & (ReadData ^ Hold);
         //Up  = Hold&(ReadData^Hold);
     Hold = ReadData;                            // 3 // 当前状态过时,即成为“上次状态”。
         /*
         unsigned char ReadData = PINB^0xff;   // 1
     Trg = ReadData & (ReadData ^ Cont);      // 2
     Up  = Cont&(ReadData^Cont);
     Cont = ReadData;                                // 3
         
         
         ReadData= P0 & 0X1C;
         ReadData ^= 0X1C;
         */
}

作者: tuowai    时间: 2013-7-17 23:39
标记下,以后能用到!谢谢分享!
作者: chengpiaopiao    时间: 2013-7-18 00:29
标记一下,备用
作者: wcm_e    时间: 2013-7-18 00:31
学习,mark,谢谢

作者: GNMXD    时间: 2013-7-18 06:28
好东西,,,
作者: Etual    时间: 2013-7-18 07:47
阅读完了代码,很标准的状态机按键程序,初学者可以学习一下。

作者: kuki0702    时间: 2013-7-18 10:01
很工整,以后能用上的。
作者: Robin_King    时间: 2013-7-18 12:44
不错不错。好好学习下。
作者: powermeter    时间: 2013-7-18 12:52
不错。mark
作者: 411412    时间: 2013-7-18 13:11
学习,mark
作者: yklstudent    时间: 2013-7-18 14:58
mark下回去好好研读下
作者: sync765    时间: 2013-7-18 16:50
不错学习了
作者: ljt80158015    时间: 2013-7-18 17:04
不错  收藏!~
作者: lsy5110    时间: 2013-7-18 17:08
不错
作者: weimas    时间: 2013-7-23 13:22
mark 值得好好学习
作者: guer    时间: 2013-7-23 15:55
mark      收藏了
作者: foxpro2005    时间: 2013-7-28 21:32
mcu_lover 发表于 2013-7-17 19:38
呵呵,不错。我已经用了好几年了。和你的方式一模一样,调用接口也一致,不同的是我把键值和按键状态封装在 ...

有当年 农民讲习所 思想风格的程序...
作者: 3.3v    时间: 2013-7-29 14:01
这个按键方式好用
作者: zzjjhh250    时间: 2013-7-30 11:42
lz能否从顶层讲解一下,整体的流程。
这样对于看code也是有效的。
传递的更多是思想。
作者: tom_flag    时间: 2013-8-1 14:16
收藏了哈..俺是初学者..正需要这些呢.会一定加把劲好好学些的..~~~~
作者: liubuwai    时间: 2013-8-4 20:43
恩恩   学习了
作者: 一匹狼    时间: 2013-8-4 21:38
cool,绑定
作者: davidd    时间: 2013-8-4 21:53
mark一下
作者: yat    时间: 2013-8-5 17:43
mark 发一个通用按键模块,简单易用
作者: kevinchen026    时间: 2013-8-5 18:07
好贴,很强大.
作者: lihq97    时间: 2013-8-12 20:56
mark,学习了!
作者: adalim    时间: 2013-8-13 14:04
标记下,以后能用到!谢谢分享!
作者: jlian168    时间: 2013-8-14 09:05
y574924080 发表于 2013-7-17 20:09
无论是独立按键还是矩阵按键,都可以使用这个模块,它们只有获取键值函数不一样,这是由用户自己编写的 ...

Sir:
没有矩阵的吗?
Thank you.
作者: y574924080    时间: 2013-8-14 21:09
jlian168 发表于 2013-8-14 09:05
Sir:
没有矩阵的吗?
Thank you.

这个由用户自己实现来的
作者: y574924080    时间: 2013-8-14 21:10
zzjjhh250 发表于 2013-7-30 11:42
lz能否从顶层讲解一下,整体的流程。
这样对于看code也是有效的。
传递的更多是思想。 ...

迟些时候哈,最近有点忙。。
作者: 一夕nandy    时间: 2013-8-18 22:18
mark一记

作者: zlutian    时间: 2013-8-18 22:41
谢谢,学习了.
作者: lgnq    时间: 2013-8-18 22:52
学习一下,以后可能会用到
作者: zyw19987    时间: 2013-8-19 07:00
不知道配置一个按键需要多少字节RAM?
作者: ljmdzyx    时间: 2013-8-19 08:04
按键程序,继续收藏!
作者: 404710520    时间: 2013-8-19 08:07
mark                        
作者: anvy178    时间: 2013-8-31 13:34
void breath_led(void)
{
    static uint16_t s_hwCounter = 0;
    static int16_t s_nGray = 0xFF;
   
    s_hwCounter++;
    if (!(s_hwCounter & (_BV(9)-1))) {
        s_nGray++;
        if (s_nGray == 0x1FF) {
            s_nGray = 0;
        }
    }
   
    set_led_gradation(ABS(s_nGray - 0xFF));
}

看了下  你呼吸灯的程序段里  静态局部变量 s_hwCounter 就随便它一直加  ,不加任何措施 ?这也行?
作者: catwill    时间: 2013-8-31 13:58
anvy178 发表于 2013-8-31 13:34
void breath_led(void)
{
    static uint16_t s_hwCounter = 0;

会溢出,这里利用了这点
作者: anvy178    时间: 2013-8-31 14:12
catwill 发表于 2013-8-31 13:58
会溢出,这里利用了这点

溢出的 结果是 0  还行 不是的话 就惨了
作者: catwill    时间: 2013-8-31 15:06
anvy178 发表于 2013-8-31 14:12
溢出的 结果是 0  还行 不是的话 就惨了

无符号数,溢出的结果是0
作者: anvy178    时间: 2013-8-31 15:53
本帖最后由 anvy178 于 2013-8-31 15:55 编辑
catwill 发表于 2013-8-31 15:06
无符号数,溢出的结果是0


找到了
作者: bondxie3    时间: 2013-9-3 08:17
标记学习,按键经典程序.
作者: ZYBing    时间: 2013-9-11 20:07
感谢分享!
作者: jz701209李    时间: 2013-9-14 21:34
学习学习,谢谢
作者: GZZXB    时间: 2013-9-17 22:56
不错  名师出高徒
作者: sunshulin    时间: 2013-9-29 19:33

作者: Jach_cc    时间: 2013-9-29 21:25
模块化的程序便于重复移植使用。其他人如果想用,那就必须体会到这个程序的优点。
作者: amnumber    时间: 2013-9-29 21:30
初学者..正需要这些呢
作者: wtliu    时间: 2013-9-30 09:15
可以有按键连击吗?
许多情况下按键需要有按住一个键不松手,一定时间后按键会自动按一个固定时间连续输出。这个键一般用在数据的加减设置中。希望楼主能增加这个功能。
作者: y574924080    时间: 2013-9-30 09:23
wtliu 发表于 2013-9-30 09:15
可以有按键连击吗?
许多情况下按键需要有按住一个键不松手,一定时间后按键会自动按一个固定时间连续输出 ...

已经支持这个功能啊
作者: JESTER9    时间: 2013-9-30 09:31
谢谢分享
作者: caoxinkafei    时间: 2013-9-30 14:42
好贴,Mark
作者: bin060117    时间: 2013-10-1 16:32
不错,收藏了
作者: 262619890    时间: 2013-10-6 13:59
mark               
作者: 滴答滴答下雨啦    时间: 2013-10-6 23:28
学习啊学习……
作者: zy0818    时间: 2013-10-9 01:44
谢谢分享~~~
作者: fenger32600    时间: 2013-10-9 08:08
收藏一个,赞一个。
作者: gmajvfhpa    时间: 2013-10-9 08:29
mark 这个肯定会用到
作者: tacbo2012    时间: 2013-10-10 18:21
怎么弄得和系统一样,如果写简单的程序 没必要这么复杂吧
作者: Gorgon_Meducer    时间: 2013-10-10 18:37
tacbo2012 发表于 2013-10-10 18:21
怎么弄得和系统一样,如果写简单的程序 没必要这么复杂吧

不复杂……只是个简单的模块……
作者: enovo2468    时间: 2013-10-10 19:11
谢谢,看看
作者: BigWolf    时间: 2013-10-10 20:01
标记学习。               
作者: 32MCU    时间: 2013-10-10 20:33
标记一下,备用.
作者: 四轴飞行器    时间: 2013-10-10 20:44
看的很纠结  看不懂的说
作者: Gorgon_Meducer    时间: 2013-10-11 11:03
四轴飞行器 发表于 2013-10-10 20:44
看的很纠结  看不懂的说

罗马都不是一日建成的,不必纠结。首先从会用开始,你觉得如何呢?
作者: 四轴飞行器    时间: 2013-10-11 11:06
Gorgon_Meducer 发表于 2013-10-11 11:03
罗马都不是一日建成的,不必纠结。首先从会用开始,你觉得如何呢?

罗马是二日建成的?

最近搞定了按一下蜂鸣器响一下的难题,就是引用  论坛里新的按键程序  

想在弄个连按和长按功能,可惜你的看的很纠结啊
作者: JESTER9    时间: 2013-10-11 11:14
好东西,学习了
作者: Gorgon_Meducer    时间: 2013-10-11 13:00
四轴飞行器 发表于 2013-10-11 11:06
罗马是二日建成的?

最近搞定了按一下蜂鸣器响一下的难题,就是引用  论坛里新的按键程序  

这是一个无需修改代码就可以使用的模块,你应该先根据帖子的内容学会使用,然后以后再考虑看
具体的实现。
作者: kalo425    时间: 2013-10-11 16:40
Gorgon_Meducer 发表于 2013-10-11 13:00
这是一个无需修改代码就可以使用的模块,你应该先根据帖子的内容学会使用,然后以后再考虑看
具体的实现 ...

key 和  gpio 口 初始化的·····在哪里    key 值要是放在 枚举里面好不···
作者: Gorgon_Meducer    时间: 2013-10-11 18:11
kalo425 发表于 2013-10-11 16:40
key 和  gpio 口 初始化的·····在哪里    key 值要是放在 枚举里面好不··· ...

Key和GPIO的初始化是用户自己处理,用户需要的是提供一个 键盘扫描函数(我们叫做前端处理),
这个函数的作用就是根据你的实际电路完成键盘扫描,并将扫描到的按键值作为函数返回值返回。
如果你已经完成了这样一个函数,那么具体如何将其注册到模块里面,就看楼主位了。

注意,键盘扫描函数是不需要作任何去抖操作的,直接返回键值即可。

这个模块的作用是提供中期和后期处理,包括去抖,识别按键,长按,重复按键,并将检测到的
按键放到一个模块自带的键盘缓冲区里面(对用户透明),同时还能提供诸如KEY_DOWN和KEY_UP
这样的信息。


作者: justSaar    时间: 2013-10-11 18:31
不错的按键法
作者: kalo425    时间: 2013-10-11 19:48
Gorgon_Meducer 发表于 2013-10-11 18:11
Key和GPIO的初始化是用户自己处理,用户需要的是提供一个 键盘扫描函数(我们叫做前端处理),
这个函数 ...

谢谢你耐心的解答·····我看到fsm关键字了···还有个例子···参考下····你们好强···
作者: Gorgon_Meducer    时间: 2013-10-11 19:53
kalo425 发表于 2013-10-11 19:48
谢谢你耐心的解答·····我看到fsm关键字了···还有个例子···参考下····你们好强··· ...

期待你的使用感想和改进意见。
作者: ZBLAMDZ    时间: 2013-10-15 20:56
不错,学习中
作者: vpntest    时间: 2013-10-21 21:51
很想知道,那个呼吸灯是怎么实现的说
作者: ltmooner    时间: 2013-10-22 11:44
不错,很工整!
作者: vpntest    时间: 2013-10-28 21:43
请问傻子LED模块中,
static void set_led_gradation(uint16_t hwLevel)
{
    static uint16_t s_hwCounter = 0;
   
    if (hwLevel >= s_hwCounter) {
        LED2_ON();
    } else {
        LED2_OFF();
    }
   
    s_hwCounter++;
    s_hwCounter &= 0x1FF;
}

#define ABS(__N)    ((__N) < 0 ? -(__N) : (__N))
#define _BV(bit) (1<<(bit))

void breath_led(void)
{
    static uint16_t s_hwCounter = 0;
    static int16_t s_nGray = 0xFF;
   
    s_hwCounter++;
    if (!(s_hwCounter & (_BV(9)-1))) {
        s_nGray++;
        if (s_nGray == 0x1FF) {
            s_nGray = 0;
        }
    }
   
    set_led_gradation(ABS(s_nGray - 0xFF));
}



set_led_gradation(ABS(s_nGray - 0xFF));
这句应该如何理解呢,为什么?为什么要取绝对值呢,能讲解下原理吗??
作者: y574924080    时间: 2013-10-28 23:01
vpntest 发表于 2013-10-28 21:43
请问傻子LED模块中,
static void set_led_gradation(uint16_t hwLevel)
{

set_led_gradation()的参数表示的就是亮度,

取绝对值就是为了实现由小到大,再由大到小,

该实现的功能是:LED由明到暗,再由暗到明,如此循环往复
作者: Gorgon_Meducer    时间: 2013-11-6 17:33
更新数据流图到楼主位,流程图也将尽快更新
作者: tlsmf    时间: 2013-11-7 13:07
学习            
作者: xyz2008    时间: 2013-11-22 10:39
虽然没有,完全看懂,但应该是可以用了,很好,很强大




欢迎光临 amobbs.com 阿莫电子技术论坛 (https://www.amobbs.com/) Powered by Discuz! X3.4