[国嵌攻略][122][按键定制器去抖]

按键抖动

按键所用的开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,开关不会马上稳定地接通或断开。因而在闭合及断开的瞬间总伴随有一连串的抖动。

按键去抖动的方法主要有两种,一种是硬件电路去抖;另一种就是软件延时去抖。而延时去抖一般又分为两种,一种是for循环等待,另一种是定时器延时。在操作系统中,由于效率方面的原因,一般不允许使用for循环来等待,只能使用定时器。

内核定时器

Linux内核使用struct timer_list来描述一个定时器:

struct timer_list{

struct list_head entry;

unsigned long expires;   //定时时间

void (*function)(unsigned long);   //处理函数

unsigned long data;

struct tvec_base *base;

};

定时器使用流程

1.定义定时器

2.初始化定时器

2.1.init_timer初始化

2.2.设置超时函数

3.add_timer注册定时器

4.启动定时器器

4.1.设置超时时间

4.2.mod_timer启动定时器

jiffies表示系统当前的时间,单位是嘀嗒数,一秒钟有1000个嘀嗒。HZ表示一秒。

keydev.c

/********************************************************************
*头文件
*********************************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>

/********************************************************************
*宏定义
*********************************************************************/
#define GPGCON 0x56000060   //按键控制寄存器地址
#define GPGDAT 0x56000064   //按键数据寄存器地址

/********************************************************************
*全局变量
*********************************************************************/
unsigned int *keycon;       //按键控制指针
unsigned int *keydat;       //按键数据指针

struct work_struct *work;   //按键工作
struct timer_list timer;    //按键定时

/********************************************************************
*定时处理
*********************************************************************/
//设备定时
void key_timer(unsigned long data){
    //读取按键状态
    unsigned short keyTmp;
    int level;

    keyTmp = readw(keydat);    //获取GPGDAT值
    level = keyTmp & 0x0001;   //获取按键电平

    //判断按键状态
    if(level == 0){
        printk("key down!\n");
    }
}

/********************************************************************
*中断处理
*********************************************************************/
//设备中断下部
void key_work(struct work_struct *work){
    //启动定时设备
    mod_timer(&timer, jiffies + HZ/100);   //jiffies表示系统当前嘀嗒数,1HZ = 1s = 1000jiffies
}

//设备中断上部
irqreturn_t key_irq(int irq, void *dev_id){
    //处理硬件相关

    //提交硬件无关
    schedule_work(work);

    return 0;
}

/********************************************************************
*设备方法
*********************************************************************/
//设备打开
int key_open(struct inode *node, struct file *filp){
    return 0;
}

//设备关闭
int key_close(struct inode *node, struct file *filp){
    return 0;
}

//设备方法
struct file_operations key_fops = {
    .open      = key_open,
    .release   = key_close
};

/********************************************************************
*模块安装
*********************************************************************/
//混杂设备
struct miscdevice misdev = {
    .minor = 200,        //次设备号
    .name  = "keydev",   //设备名称
    .fops  = &key_fops   //设备方法
};

//注册硬件
void handware_init(void){
    //初始化按键控制寄存器
    unsigned int keyTmp;

    keycon = ioremap(GPGCON, 4);   //虚拟地址映射

    keyTmp = readl(keycon);   //获取GPGCON值
    keyTmp &= ~(0x3<<0);      //GPG0[1:0]:00
    keyTmp |=  (0x2<<0);      //GPG0[1:0]:EINT[8]
    writel(keyTmp, keycon);   //设置GPGCON值

    //初始化按键状态寄存器
    keydat = ioremap(GPGDAT, 2);   //虚拟地址映射
}

//安装模块
static int key_init(void){
    //注册混杂设备
    misc_register(&misdev);

    //注册硬件设备
    handware_init();

    //注册中断处理
    request_irq(IRQ_EINT8, key_irq, IRQF_TRIGGER_FALLING, "keyirq", 0);   //下降沿触发,IRQ_EINT8定义在irqs.h文件中

    //注册工作队列
    work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
    INIT_WORK(work, key_work);

    //注册定时设备
    init_timer(&timer);           //初始化定时器
    timer.function = key_timer;   //添加定时函数
    add_timer(&timer);            //添加定时设备

    return 0;
}

//卸载模块
static void key_exit(void){
    //注销混杂设备
    misc_deregister(&misdev);

    //注销中断处理
    free_irq(IRQ_EINT8, 0);
}

/********************************************************************
*模块声明
*********************************************************************/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("D");
MODULE_DESCRIPTION("");
MODULE_VERSION("v1.0");

module_init(key_init);
module_exit(key_exit);
时间: 2024-10-13 12:58:44

[国嵌攻略][122][按键定制器去抖]的相关文章

[国嵌攻略][050][2440按键中断编程]

程序结构优化 1.把不同的功能放到不同的文件里面 2.一般在bootloader中不使用MMU,所以关掉MMU 按键初始化 1.打开开发板的原理图,找到按键 2.把对应的GPIO配置成中断,对中断源进行初始化 初始化中断控制器 1.SUBMASK和MASK必须要保证没有屏蔽中断,GPIO是不属于子中断,所以不用设置SUBMASK,MODE和Priority保存默认值即可,设置INTMSK(0x4A000008) 2.EINT4_7对应4个中断,分开设置需要设置EINTMASK(0x560000A

[国嵌攻略][164][USB驱动程序设计]

USB驱动模型 1.USB host controller driver(主控器驱动):为USB主控制器提供驱动程序 2.USB core(USB核心):连接USB主控制器驱动和USB设备驱动 3.USB client driver:为USB外部设备提供驱动程序 USB设备模型 device(设备)->config(配置)->interface(接口)->endpoint(端点),构成了USB设备的四个层次.一个usb driver(驱动)对应的是一个interface(接口).一个接口

[国嵌攻略][044][初始化Bss段]

BSS段的作用 1.变量存储的空间 初始化的全局变量:数据段 未初始化的全局变量:BSS段 局部变量:栈 动态分配变量:堆 2.为什么要对BSS段初始化 未初始化的全局变量在使用时才被赋值,未了避免在使用时忘记赋值,导致使用随机值.由系统工程师在系统启动时,把BBS清零. 初始化BSS段 找到BSS段起始地址和结束地址向其中填零,BSS段地址记录在链接器脚本中 /* *名称:init_bss *功能:初始化BSS段 */ init_bss: ldr r1, =bss_start ldr r2,

[国嵌攻略][140][触摸屏驱动分析]

触摸屏驱动分析 初始化 1.使能ADC时钟 2.将物理地址转化为虚拟地址 3.让触摸屏进入等待中断模式 4.分配输入设备结构 5.设置可能上报的事件类型和按键类型 6.为TC和ADC中断注册处理函数 7.注册输入型设备 按下处理 1.判断按下或弹起 2.如果是按下情况,那么启动XY坐标的AD转换 3.进行4次ADC转换,获取4次XY坐标值 4.计算4次采集的平均值,并上报给内核

[国嵌攻略][155][I2C用户态驱动设计]

用户态驱动模型 用户态驱动模型首先是一个应用程序,其次是在这个用户程序中通过内核调用来驱动设备. IIC通用驱动代码 IIC通用驱动程序的代码在/drivers/i2c/i2c-dev.c中.一次读操作或者一次写操作就是一条消息. EEPROM用户态驱动 IIC通用设备对应/dev/i2c-0设备文件. 1.打开通用设备驱动 2.构造写数据到eeprom的消息 3.使用ioctl写入数据 4.构造从eeprom读数据的消息 5.使用ioctl读出数据 6.关闭设备 配置IIC驱动 make me

[国嵌攻略][057][串口控制台建立]

控制台分类 1.菜单型控制台,通过选择菜单来执行命令 2.解析型控制台,通过输入命令来执行命令 printf().scanf()函数移植 1.函数采用变参 2.打印信息到串口 3.关键在于把变参转换成字符串,相关函数需要从Linux内核或标准C库中移植 va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); /***********************************************

[国嵌攻略][117][LED驱动程序设计]

LED程序设计 1.编写内核模块 2.搭建字符驱动框架 3.实现设备方法 头文件 <linux/io.h> writel() leddev.h //设备命令 #define LED_MAGIC 'L' //LED幻数 #define LED_ON _IO(LED_MAGIC, 0) //打开LED #define LED_OFF _IO(LED_MAGIC, 1) //关闭LED leddev.c /***********************************************

[国嵌攻略][112][使用字符设备驱动]

编译/安装驱动程序 在Linux系统中,驱动程序通常采用内核模块的程序结构来进行编码.因此编译/安装一个驱动程序,其实就是编译/安装一个内核模块. 示例: make insmod memdev.ko 创建设备文件 应用程序->设备文件->驱动程序 通过字符设备文件,应用程序可以使用相应的字符设备驱动程序来控制字符设备.创建字符设备文件的方法一般有两种: 1.使用mknod命令 mknod /dev/文件名 c 主设备号 次设备号 驱动程序通过主设备号与字符设备文件一一对应,驱动程序的主设备号可

[国嵌攻略][102][内核驱动开发环境搭建]

服务器环境搭建 1.配置tftp服务器 1.1.设置tftp目录 vim /etc/xinetd.d/tftp server_args             = -s .../tftp disable                 = no 1.2.启动tftp服务 /etc/init.d/xinetd restart 2.配置nfs服务器 2.1.设置nfs目录 vim /etc/exports .../nfs *(rw,sync,no_root_squash) 2.2.启动nfs服务 /