Linux驱动开发——去除按键抖动问题

简介: Linux驱动开发——去除按键抖动问题

文章目录

Linux内核按键去抖动问题

按键抖动

按键驱动的方法

Linux内核去除抖动的原理

代码示例

总结


Linux内核按键去抖动问题

按键抖动

按键抖动表现出来的现象其实就是明明只是按下去一次按键,但是却总是触发多次中断,本质其实是因为按键属于机械结构,所以不可避免会在按下和松开时产生意外的中断触发。


按键驱动的方法

按键去抖动的方法有两种:


  • 硬件去抖动:硬件工程师只需要添加一个滤波电路等方式即可去除不规整的波形导致的抖动产生,但是这样会提高产品的成本,但是去除抖动效果最好。

  • 软件去抖动:传统的单片机利用循环延时去除抖动,成本低,能够明显去除抖动,但是效果不如硬件去抖动好。


Linux内核去除抖动的原理


202001022019318.png

202001022019318.png

从图中可以看到,每次中断触发后,先触发一个定时器(抖动时间差不多5~10ms),所以设定一个10ms的定时器,在这期间的多次中断触发也仅仅是刷新定时器重新开始计时,而当10ms的定时器时间到期后会触发真正的按键处理事件。也就是说当信号稳定10ms时才能算是真正准确的信号,而不是抖动产生的中断信号。以这种方式屏蔽抖动产生的错误中断信号是经常使用的方式(类似于单片机上采用的循环延时方式)


代码示例

将之前进程读取按键状态进入休眠等待队列,按键按下后触发中断唤醒进程任务读取硬件状态的程序进行去除抖动处理,修改后如下:


  • btn_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/uaccess.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sched.h> //TASK_INTERRUPTIBLE等
#include <linux/input.h>
#include <linux/timer.h>
//声明描述按键信息的数据结构
struct btn_resource {
    int gpio; //按键对应的GPIO编号
    char *name;//按键名称
    int code;//按键值
};
//声明上报按键信息的数据结构
struct btn_event {
    int state; //上报按键的状态:1:按下;0:松开
    int code;  //上报按键值
};
//定义初始化四个按键的硬件信息对象
static struct btn_resource btn_info[] = {
    {
        .gpio = PAD_GPIO_A + 28,
        .name = "KEY_UP",
        .code = KEY_UP
    }
};
//分配内核缓冲区,记录当前操作的按键信息
static struct btn_event kbtn;
static int ispressed; //指示按键是否有操作
                      //如果有操作:ispressed=1
                      //如果无操作:ispressed=0
//定义定时器对象
static struct timer_list btn_timer;
//定义一个等待队列头对象(造鸡妈妈)
static wait_queue_head_t rwq;
static ssize_t btn_read(struct file *file,
                        char __user *buf,
                        size_t count,
                        loff_t *ppos)
{
    //1.如果按键无操作,进程直接进入休眠状态
    //如果按键有操作,进程立即返回
    //进程休眠时ispressed=0
    //进程一旦被唤醒,此时ispressed=1
    wait_event_interruptible(rwq, ispressed);
    ispressed = 0;//为了下一次read能够进行休眠操作
    //2.此时的kbtn已经被中断处理函数进行赋值操作
    copy_to_user(buf, &kbtn, sizeof(kbtn));
    return count;
}
//记录当前操作的按键的硬件信息,全局化
static  struct btn_resource *pdata;
//定时器超时处理函数
static void btn_timer_function(unsigned long data)
{
    //1.获取按键的状态和按键值保存在全局变量中
    kbtn.state = gpio_get_value(pdata->gpio);
    kbtn.code = pdata->code;
    //2.一旦有按键操作,硬件上势必产生中断
    //也就说明按键有操作,应该唤醒read进程读取按键的信息
    ispressed = 1;//为了唤醒进程以后,能够让进程从
                  //wait_event_interruptible中返回
                  //否则进程永远在此函数中出不来
    wake_up(&rwq);
}
//中断处理函数
static irqreturn_t button_isr(int irq, void *dev)
{
    //1.获取当前操作的按键的硬件信息
    pdata = dev;
    //2.删除之前的定时器,指定一个新的超时时间
    //并且向内核重新添加定时器
    mod_timer(&btn_timer, 
            jiffies + msecs_to_jiffies(10));
    return IRQ_HANDLED; //中断返回有可能才轮到read进程执行
}
//定义初始化硬件操作接口对象
static struct file_operations btn_fops = {
    .owner = THIS_MODULE,
    .read = btn_read,
};
//定义初始化混杂设备对象
static struct miscdevice btn_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "mybtn",
    .fops = &btn_fops
};
static int btn_init(void)
{
    int i;
    //注册混杂设备对象
    misc_register(&btn_misc);
    //初始化等待队列头(武装鸡妈妈)
    init_waitqueue_head(&rwq);
    //申请GPIO资源
    //申请中断资源,注册中断处理函数
    for(i = 0; i < ARRAY_SIZE(btn_info); i++) {
        int irq = gpio_to_irq(btn_info[i].gpio);
        gpio_request(btn_info[i].gpio,
                        btn_info[i].name);
        request_irq(irq, button_isr,
            IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
            btn_info[i].name, &btn_info[i]);
    }
    //初始化定时器对象
    init_timer(&btn_timer);
    //指定定时器的超时处理函数
    btn_timer.function = btn_timer_function;
    return 0;
}
static void btn_exit(void)
{
    int i;
    //各种释放
    for(i = 0; i < ARRAY_SIZE(btn_info); i++) {
        int irq = gpio_to_irq(btn_info[i].gpio);
        gpio_free(btn_info[i].gpio);
        free_irq(irq, &btn_info[i]);
    }
    //卸载混杂设备对象
    misc_deregister(&btn_misc);
    //删除定时器
    del_timer(&btn_timer);
}
module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");


  • btn_test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//声明按键信息数据结构
struct btn_event {
    int state; //按键状态:1:松开;0:按下
    int code; //按键值
};
int main(int argc, char *argv[])
{
    int fd;
    struct btn_event btn; //分配用户缓冲区,记录按键的信息
    //打开设备
    fd = open("/dev/mybtn", O_RDWR);
    if (fd < 0) {
        printf("打开设备失败!\n");
        return -1;
    }
    while(1) {
        read(fd, &btn, sizeof(btn));
        printf("按键[%d]的状态为[%s]\n",
                btn.code, btn.state ?"松开":"按下");
    }
    //关闭设备
    close(fd);
    return 0;
}


  • Makefile
obj-m += btn_drv.o
all:
  make -C /opt/kernel SUBDIRS=$(PWD) modules
clean:
  make -C /opt/kernel SUBDIRS=$(PWD) clean


  • 执行结果:


20200102205423227.png

20200102205423227.png

总结

在Linux内核中去除按键抖动其实原理同单片机下的延时去除方式类似,只不过Linux内核调用自己的延时机制来触发按键中断响应。效果还是很好的。

相关文章
|
1天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
51 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
1天前
|
安全 Linux API
Linux设备模型统一:桥接硬件多样性与应用程序开发的关键
在Linux的宏大世界中,各种各样的硬件设备如星辰般繁多。从常见的USB设备到复杂的网络接口卡,从嵌入式设备到强大的服务器,Linux需要在这些差异极大的硬件上运行。这就引出了一个问题:Linux是如何统一这些不同硬件的设备模型的呢?本文将探讨Linux是如何针对不同的硬件统一设备模型的,这一统一的设备模型对于应用程序开发人员来说又有何意义。让我们一探究竟🕵️‍♂️。
Linux设备模型统一:桥接硬件多样性与应用程序开发的关键
|
1天前
|
算法 Linux 测试技术
Linux C++开发中的代码优化之道:把握时机与策略
Linux C++开发中的代码优化之道:把握时机与策略
51 0
|
1天前
|
Unix Linux Shell
FFmpeg开发笔记(八)Linux交叉编译Android的FFmpeg库
在Linux环境下交叉编译Android所需的FFmpeg so库,首先下载`android-ndk-r21e`,然后解压。接着,上传FFmpeg及相关库(如x264、freetype、lame)源码,修改相关sh文件,将`SYSTEM=windows-x86_64`改为`SYSTEM=linux-x86_64`并删除回车符。对x264的configure文件进行修改,然后编译x264。同样编译其他第三方库。设置环境变量`PKG_CONFIG_PATH`,最后在FFmpeg源码目录执行配置、编译和安装命令,生成的so文件复制到App工程指定目录。
51 9
FFmpeg开发笔记(八)Linux交叉编译Android的FFmpeg库
|
1天前
|
Linux C语言
|
1天前
|
安全 Linux Android开发
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
该文介绍了如何在Linux服务器上交叉编译Android的FFmpeg库以支持HTTPS视频播放。首先,从GitHub下载openssl源码,解压后通过编译脚本`build_openssl.sh`生成64位静态库。接着,更新环境变量加载openssl,并编辑FFmpeg配置脚本`config_ffmpeg_openssl.sh`启用openssl支持。然后,编译安装FFmpeg。最后,将编译好的库文件导入App工程的相应目录,修改视频链接为HTTPS,App即可播放HTTPS在线视频。
28 3
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
|
1天前
|
前端开发 Linux iOS开发
【Flutter前端技术开发专栏】Flutter在桌面应用(Windows/macOS/Linux)的开发实践
【4月更文挑战第30天】Flutter扩展至桌面应用开发,允许开发者用同一代码库构建Windows、macOS和Linux应用,提高效率并保持平台一致性。创建桌面应用需指定目标平台,如`flutter create -t windows my_desktop_app`。开发中注意UI适配、性能优化、系统交互及测试部署。UI适配利用布局组件和`MediaQuery`,性能优化借助`PerformanceLogging`、`Isolate`和`compute`。
【Flutter前端技术开发专栏】Flutter在桌面应用(Windows/macOS/Linux)的开发实践
|
1天前
|
编解码 Linux
FFmpeg开发笔记(十二)Linux环境给FFmpeg集成libopus和libvpx
在《FFmpeg开发实战》一书中,介绍了如何在Linux环境下为FFmpeg集成libopus和libvpx,以支持WebM格式的Opus和VP8/VP9编码。首先,下载并安装libopus。接着,下载并安装libvpx。最后,在FFmpeg源码目录下,重新配置FFmpeg,启用libopus和libvpx,编译并安装。通过`ffmpeg -version`检查版本信息,确认libopus和libvpx已启用。
31 1
FFmpeg开发笔记(十二)Linux环境给FFmpeg集成libopus和libvpx
|
1天前
|
编解码 Linux
FFmpeg开发笔记(十)Linux环境给FFmpeg集成vorbis和amr
在Linux环境下,为FFmpeg添加对AAC、MP3、OGG和AMR音频格式的支持,需安装libogg、libvorbis和opencore-amr库。首先,从官方源下载各库的最新源码,如libogg-1.3.5、libvorbis-1.3.7和opencore-amr-0.1.6,然后解压并依次执行`./configure`、`make`和`make install`进行编译安装。接着,在FFmpeg源码目录中,使用`./configure`命令重新配置,并重新编译安装FFmpeg。最后,验证FFmpeg版本信息确认已启用ogg和amr支持。
24 0
FFmpeg开发笔记(十)Linux环境给FFmpeg集成vorbis和amr
|
1天前
|
Linux API C语言
FFmpeg开发笔记(一)搭建Linux系统的开发环境
本文指导初学者如何在Linux上搭建FFmpeg开发环境。首先,由于FFmpeg依赖第三方库,可以免去编译源码的复杂过程,直接安装预编译的FFmpeg动态库。推荐网站<https://github.com/BtbN/FFmpeg-Builds/releases>提供适用于不同系统的FFmpeg包。但在安装前,需确保系统有不低于2.22版本的glibc库。详细步骤包括下载glibc-2.23源码,配置、编译和安装。接着,下载Linux版FFmpeg安装包,解压至/usr/local/ffmpeg,并设置环境变量。最后编写和编译简单的C或C++测试程序验证FFmpeg环境是否正确配置。
50 8
FFmpeg开发笔记(一)搭建Linux系统的开发环境