请选择 进入手机版 | 继续访问电脑版

安富莱电子论坛

 找回密码
 立即注册

扫一扫,访问微社区

查看: 359|回复: 4
收起左侧

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

[复制链接]

3

主题

35

帖子

41

积分

新手上路

积分
41
发表于 2018-11-5 20:16:20 | 显示全部楼层 |阅读模式
本帖最后由 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_t  trig;        //按键按下触发,不释放永远为0,释放也为0(上升沿检测)
    uint8_t  oldv;        //按键值的拷贝
    uint8_t  pfilt;       //按下触发滤波
    uint8_t  click;       //trig发出的次数,实际就是按下的次数,每10ms检测一次
    uint16_t prcnt;       //按下时计数
    uint16_t popcn;       //按键释放时计数
}BUTTON_REG;


BUTTON_REG  button_key[10];   //区别密钥变量,不至于混淆

/*
*********************************************************************************************************
*        函 数 名: 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[id].trig  = 0;
    button_key[id].oldv  = 0;
    button_key[id].pfilt = 0;
    button_key[id].click = 0;
    button_key[id].prcnt = 0;
    button_key[id].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[0].butt_chk_func = butt_chk1;
    button_key[1].butt_chk_func = butt_chk2;
    button_key[2].butt_chk_func = butt_chk3;
    button_key[3].butt_chk_func = butt_chk4;
    button_key[4].butt_chk_func = butt_chk5;
    button_key[5].butt_chk_func = butt_chk6;
    button_key[6].butt_chk_func = butt_chk7;
    button_key[7].butt_chk_func = butt_chk8;

    button_key[8].butt_chk_func = butt_chk9;
    button_key[9].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[id].butt_chk_func();

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

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

    if(button_key[id].oldv==0 && button_key[id].click)         
        button_key[id].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)
                }
                else  if(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);






回复

使用道具 举报

4

主题

45

帖子

53

积分

初级会员

积分
53
发表于 2018-11-17 23:16:48 | 显示全部楼层
学习一下,感觉很好玩
回复

使用道具 举报

4

主题

45

帖子

53

积分

初级会员

积分
53
发表于 2018-12-30 17:22:27 | 显示全部楼层
请问一下楼主 如何 实现键值缓存
回复

使用道具 举报

19

主题

1208

帖子

1246

积分

至尊会员

积分
1246
发表于 2019-1-5 23:57:33 | 显示全部楼层
不错,mark
回复

使用道具 举报

0

主题

7

帖子

7

积分

新手上路

积分
7
发表于 4 天前 | 显示全部楼层
感谢,按键N击程序,mark
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|手机版|安富莱电子论坛 ( 鄂ICP备09023347号,公安机关备案号42010602000201 )

GMT+8, 2019-2-19 13:00 , Processed in 0.171481 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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