<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[新唐 N76E003 按键 单击&#x2F;双击&#x2F;长按 demo程序]]></title><description><![CDATA[<pre><code>#include "N76E003.h"
#include "Common.h"
#include "Delay.h"
#include "SFR_Macro.h"
#include "Function_define.h"


#define TIMER1_INIT        (6663 * 2)

UINT8 u8TH1_Tmp,u8TL1_Tmp;
UINT8 time_10ms_ok;

unsigned char key;

#define IO_KEY_INPUT    P10    // 按键输入口
#define IO_BEEP         P30

#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
#define key_state_3         3

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 = IO_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 &gt;= 100)     // 继续按下，计时加10ms（10ms为本函数循环执行间隔）
        {
             key_return = L_key;        // 按下时间&gt;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)             // 又一次单击（间隔肯定&lt;500ms）
            {
                 key_return = D_key;           // 返回双击键事件，回初始状态
                 key_m = key_state_0;
            }
            else                                
            {                                  // 这里500ms内肯定读到的都是无键事件，因为长键&gt;1000ms，在1s前低层返回的都是无键
                 if(++key_time_1 &gt;= 50)
                 {
                      key_return = S_key;      // 500ms内没有再次出现单键事件，返回上一次的单键事件
                      key_m = key_state_0;     // 返回初始状态
                 }
             }
             break;
    }
    return key_return;
}     

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

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

测试代码环境如下：  
*/
void Timer1_ISR (void) interrupt 3  // timer1定时器10ms中断服务
{
    TH1 = u8TH1_Tmp;
    TL1 = u8TL1_Tmp;   

    P06 = ~P06;                     //P0.3 toggle when interrupt
    
    time_10ms_ok = 1;
}

main(void)  
{  
    Set_All_GPIO_Quasi_Mode;

    TIMER1_MODE1_ENABLE; //定时器1， 模式1, 16bit定时器, 定时器值满 0xFFFF -&gt; 0x0000 产生中断。

    clr_T1M;    //T1M = 0，兼容传统 8051， TIMER1时钟 = Fsys/12 = 16M /12
    //set_T1M;  //T1M = 1，               TIMER1时钟 = Fsys    = 16M 

    u8TH1_Tmp = (65536 - TIMER1_INIT)/256;
    u8TL1_Tmp = (65536 - TIMER1_INIT)%256;

    TH1 = u8TH1_Tmp;
    TL1 = u8TL1_Tmp;
    
    set_ET1;                                    //enable Timer1 interrupt
    set_EA;                                     //enable interrupts
    set_TR1;                                    //Timer1 run
    
    while (1) 
    {  
        if (time_10ms_ok)            //每10ms执行一次，  
        {  
             time_10ms_ok =0;  
             key = key_read();       //《====== 10ms一次调用按键中间层函数，根据返回键值，点亮不同的LED灯，全面测试按键操作是否正常  
             
             if(key == S_key) //短按
             {
                 IO_BEEP = 0;
                 Timer0_Delay1ms(10);
                 IO_BEEP = 1;
             }
             else if(key == D_key) //双击
             {
                 IO_BEEP = 0;
                 Timer0_Delay1ms(50);
                 IO_BEEP = 1;
             }
             else if (key == L_key) //长按
             {
                 IO_BEEP = 0;
                 Timer0_Delay1ms(150);
                 IO_BEEP = 1;
             }
         }  
     }  
}
</code></pre>
<p dir="auto"><img src="/assets/uploads/files/1626318460538-img_20210715_110218.jpg" alt="IMG_20210715_110218.jpg" class=" img-responsive img-markdown" width="836" height="659" /></p>
]]></description><link>https://bbs.aw-ol.com/topic/206/新唐-n76e003-按键-单击-双击-长按-demo程序</link><generator>RSS for Node</generator><lastBuildDate>Sat, 16 May 2026 18:04:45 GMT</lastBuildDate><atom:link href="https://bbs.aw-ol.com/topic/206.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 15 Jul 2021 03:07:46 GMT</pubDate><ttl>60</ttl></channel></rss>