passteen 发表于 2018-11-5 20:16:20

按键单击,双击,N击,长按程序

本帖最后由 passteen 于 2018-11-5 22:40 编辑

rt-thread是个非常好的国产操作系统,个人认为有linux的影子,在嵌入式操作系统中具备先天优势。
这个程序就是在rt-thread操作系统下验证的,但是这个程序不局限于他。
一般按键检测用于单击应用,对于双击,连发等功能其实是比较头疼的,这时候大多使用状态机处理了,
这里贴出一个变种程序,非常简单。事实上都可以不叫状态机了。

贴代码:

/********************************************************************************************************/
/*      文 件 名: app_button.c   (变种按键状态机程序)                                                    */
/*      功    能: 实现按键的长按,单击,双击,n连发功能(最多255)。                                       */
/*      注意事项: 该代码基于STM-MCU,10ms调用一次。如时间更短则精度更好,但是需要修改判定参数。            */
/*      禁    忌: 无。                                                */
/*环    境: 基于安富莱V6及rt-thread操作系统。                                                                  */
/********************************************************************************************************/

typedef struct
{
    uint8_t (*butt_chk_func)(void);    //读键值函数
    uint8_ttrig;      //按键按下触发,不释放永远为0,释放也为0(上升沿检测)
    uint8_toldv;      //按键值的拷贝
    uint8_tpfilt;       //按下触发滤波
    uint8_tclick;       //trig发出的次数,实际就是按下的次数,每10ms检测一次
    uint16_t prcnt;       //按下时计数
    uint16_t popcn;       //按键释放时计数
}BUTTON_REG;


BUTTON_REGbutton_key;   //区别密钥变量,不至于混淆

/*
*********************************************************************************************************
*      函 数 名: butt_chkx
*      功能说明: 判断按键是否按下
*      形    参: 无
*      返 回 值: 返回值1 表示按下,0表示未按下
*********************************************************************************************************
*/

                              /* 安富莱 STM32-V5 开发板 */
static uint8_t butt_chk1(void) {if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) == 0) return 1;else return 0;}
static uint8_t butt_chk2(void) {if ((GPIO_PORT_K2->IDR & GPIO_PIN_K2) == 0) return 1;else return 0;}
static uint8_t butt_chk3(void) {if ((GPIO_PORT_K3->IDR & GPIO_PIN_K3) == 0) return 1;else return 0;}
static uint8_t butt_chk4(void) {if ((GPIO_PORT_K4->IDR & GPIO_PIN_K4) == 0) return 1;else return 0;}
static uint8_t butt_chk5(void) {if ((GPIO_PORT_K5->IDR & GPIO_PIN_K5) == 0) return 1;else return 0;}
static uint8_t butt_chk6(void) {if ((GPIO_PORT_K6->IDR & GPIO_PIN_K6) == 0) return 1;else return 0;}
static uint8_t butt_chk7(void) {if ((GPIO_PORT_K7->IDR & GPIO_PIN_K7) == 0) return 1;else return 0;}
static uint8_t butt_chk8(void) {if ((GPIO_PORT_K8->IDR & GPIO_PIN_K8) == 0) return 1;else return 0;}

static uint8_t butt_chk9(void) {if (butt_chk1() && butt_chk2()) return 1;else return 0;}
static uint8_t butt_chk10(void) {if (butt_chk1() && butt_chk2()) return 1;else return 0;}


/*
*********************************************************************************************************
*      函 数 名: button_gpio_init
*      功能说明: 配置按键对应的GPIO
*      形    参: 无
*      返 回 值: 无
*********************************************************************************************************
*/
static void button_gpio_init(void)
{
      GPIO_InitTypeDef GPIO_InitStructure;

      /* 第1步:打开GPIO时钟 */
      __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();
    __HAL_RCC_GPIOH_CLK_ENABLE();
    __HAL_RCC_GPIOI_CLK_ENABLE();

      /* 第2步:配置所有的按键GPIO为浮动输入模式(实际上CPU复位后就是输入状态) */
      GPIO_InitStructure.Mode = GPIO_MODE_INPUT;                /* 设为输入口 */
      GPIO_InitStructure.Pull = GPIO_NOPULL;                /* 无需上下拉电阻 */
      GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;      /* IO口最大速度 */

      GPIO_InitStructure.Pin = GPIO_PIN_K1;
      HAL_GPIO_Init(GPIO_PORT_K1, &GPIO_InitStructure);

      GPIO_InitStructure.Pin = GPIO_PIN_K2;
      HAL_GPIO_Init(GPIO_PORT_K2, &GPIO_InitStructure);

      GPIO_InitStructure.Pin = GPIO_PIN_K3;
      HAL_GPIO_Init(GPIO_PORT_K3, &GPIO_InitStructure);

      GPIO_InitStructure.Pin = GPIO_PIN_K4;
      HAL_GPIO_Init(GPIO_PORT_K4, &GPIO_InitStructure);

      GPIO_InitStructure.Pin = GPIO_PIN_K5;
      HAL_GPIO_Init(GPIO_PORT_K5, &GPIO_InitStructure);

      GPIO_InitStructure.Pin = GPIO_PIN_K6;
      HAL_GPIO_Init(GPIO_PORT_K6, &GPIO_InitStructure);

      GPIO_InitStructure.Pin = GPIO_PIN_K7;
      HAL_GPIO_Init(GPIO_PORT_K7, &GPIO_InitStructure);

      GPIO_InitStructure.Pin = GPIO_PIN_K8;
      HAL_GPIO_Init(GPIO_PORT_K8, &GPIO_InitStructure);
}

/*
*********************************************************************************************************
*      函 数 名: button_reg_clear
*      功能说明: 将按键结构体变量(寄存器组)清零
*      参    数: 按键编号,用于扫描按键。
*      返 回 值: 无
*********************************************************************************************************
*/
static void button_reg_clear(uint8_t id)
{
    button_key.trig= 0;
    button_key.oldv= 0;
    button_key.pfilt = 0;
    button_key.click = 0;
    button_key.prcnt = 0;
    button_key.popcn = 0;
}

/*
*********************************************************************************************************
*      函 数 名: button_init
*      功能说明: 配置按键对应的GPIO
*      形    参: 无
*      返 回 值: 无
*   注      :rt-thread导入函数需要int类型,因此....别忘了加return
*********************************************************************************************************
*/
static int button_init(void)
{
    uint8_t i;

    button_gpio_init();

    for(i=0;i<10;i++)
    {
      button_reg_clear(i);
    }
    button_key.butt_chk_func = butt_chk1;
    button_key.butt_chk_func = butt_chk2;
    button_key.butt_chk_func = butt_chk3;
    button_key.butt_chk_func = butt_chk4;
    button_key.butt_chk_func = butt_chk5;
    button_key.butt_chk_func = butt_chk6;
    button_key.butt_chk_func = butt_chk7;
    button_key.butt_chk_func = butt_chk8;

    button_key.butt_chk_func = butt_chk9;
    button_key.butt_chk_func = butt_chk10;

    return 0;
}
INIT_BOARD_EXPORT(button_init);

/*
*********************************************************************************************************
*      函 数 名: button_detect
*      功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
*      参    数: 按键编号,用于扫描按键。
*      返 回 值: 无
*********************************************************************************************************
*/
void button_detect(uint8_t id)
{
    uint8_t key_val;
    key_val = button_key.butt_chk_func();

    button_key.trig = key_val&(key_val ^ button_key.oldv);   //核心算法,万能网有介绍
    button_key.oldv = key_val;                                 //不得与上一语句顺序颠倒

    if(button_key.trig)
    {               button_key.popcn=0;                                    //有按下则弹出计数器清零
         button_key.click += button_key.trig;             //按下次数,有效滤波
    }
    button_key.prcnt += button_key.oldv;                     //如果按下记录有效,按下次数累加

    if(button_key.oldv==0 && button_key.click)         
      button_key.popcn++;                                    //按键已释放,此时如果按下次数不为0则释放次数累加
}

/*
*********************************************************************************************************
*      函 数 名: button_entry
*      功能说明: rt-thread进程入口函数,判断按键结果。
*      参    数: rt-thread内核的要求,具体暂时不明。
*      返 回 值: 无
*********************************************************************************************************
*/
static void button_entry(void* parameter)
{
    uint8_t i;

    while(1)
    {
      for(i=0;i<10;i++)
      {
            if(button_key.click)          //如果有按键按下
         {
                if(button_key.popcn>20)   //如果释放时间(计数)超过20次(200ms),注意该值不要太小,否则会有误触发,这与按键动作的速度相关
                {
                        if(button_key.prcnt<100)
                               rt_kprintf("clicks: %d \n",button_key.click);
                        button_reg_clear(i)
                }
                elseif(button_key.click==1 && button_key.prcnt>99)      //首先要判断长按
                {
                        rt_kprintf("press long \n");                           
                }
            }
      }
      rt_thread_delay(10);
    }
}

/*
*********************************************************************************************************
*函 数 名: button_scan
*      功能说明: rt-thread进程创建,导入APP应用,不需要加入main函数,有利于功能模块独立
*      参    数: 无
*      返 回 值: rt-thread要求有返回值
*********************************************************************************************************
*/
static int button_scan(void)
{
    rt_thread_t button_tid=rt_thread_create("button",button_entry,
                                          RT_NULL, 256,10,10);
    if(button_tid != RT_NULL)
      rt_thread_startup(button_tid);

    return 0;
}
INIT_APP_EXPORT(button_scan);






王海靖 发表于 2018-11-17 23:16:48

学习一下,感觉很好玩

王海靖 发表于 2018-12-30 17:22:27

请问一下楼主 如何 实现键值缓存

sanit 发表于 2019-1-5 23:57:33

不错,mark

WendellWang 发表于 2019-2-15 15:50:11

感谢,按键N击程序,mark

alana89 发表于 2020-4-22 22:33:55

学习一下,感觉很好玩 https://apkafe.com/product/cotomovies/
页: [1]
查看完整版本: 按键单击,双击,N击,长按程序