Linux key driver design (5) - Key Debounce

1. Key Jitter

  • The key switch is a mechanical switch elasticity, when the mechanical contact is opened, closed, due to the elasticity of the mechanical contacts, the switch will not immediately turn on or off stably. Thus closing off the moment and is always accompanied by a series of jitter.

2. The method Debounced

  • Key to shake method are mainly two kinds:
    • One is hardware debounced.
    • Another is the software delay to shake . The delay has generally divided into two kinds, one is for loop to wait , the other is the timer delay . In the operating system, for reasons of efficiency, generally are not allowed to wait for the cycle, only use customizer.

3. Core Timer

  • Linux kernel uses to describe a struct timer_list timer:
struct timer_list {
    struct list_head entry;
    unsigned long expires;
    void (*function)(unsigned long);
    unsigned long data;
    struct tvec_base *base;
};
  • Here the most important parameters are two, the Expires timeout, that is, the timer delay time, function function after a specified time-out what you need to perform the operation.
  • Use the timer process:
    • 1, the definition of a timer variable: struct timer_list key_timer;
    • 2, initialize the timer, use init_timer , and set timeout function:
      • key_timer.function = key_timer_function;init_timer(&key_timer);
    • 3, registration timer, use add_timer: add_timer (& key_timer);
    • 4, start the timer, use mod_timer
      • jiffies is in the Linux kernel a number of global tick, which holds the current number of ticks, ticks one second 1000,
      • mod_timer(&key_timer, jiffies + (HZ /10)); 
      • HZ represents a second time, in addition to 10 is 100ms, therefore timeout is set to 100ms from the beginning of the current

4. Use key debounce timer

  • Add a timer function in the previous basis.
    • 1, add a kernel timer
    • 2, initialization and registration timer in the module initialization function
    • 3, starts the timer interrupt service function in the key, start by working queue
    • 4, after starting the timer, if said set 100ms, the function calls the function after a timeout, the state of which determines the level of the key, then the corresponding operation.
  • Myth: Some students see the data sheet found, mini2440 IO port key storage levels are 16-bit registers, then when mapping a virtual address and read data should also be 16, but do not actually need to change the mapping of 16 and reading, with the same function can be 32-bit. Otherwise the kernel will crash.
  • key.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>

#define GPGCON 0x56000060
#define GPGDAT 0x56000064

struct work_struct *key_work; 
struct timer_list key_timer;

unsigned int *gpio_data;

// 工作队列函数
void key_work_func(struct work_struct *work)
{
    mod_timer(&key_timer, jiffies + HZ/10);	// 启动定时器,在当前时间100ms后超时
}

// 判断按键是否真正按下
void key_timer_func(unsigned long data)
{
    unsigned short key_vall;
    key_vall = readw(gpio_data) & 0x01;
    
    if (key_vall == 0)
        printk("key down!\n");    
}

// 中断处理函数
irqreturn_t key_int(int irq, void *dev_id)
{
    // 1.检测是否发生了按键中断	
    
    // 2.清除已经发生的按键中断
    
    // 3.提交下半部分
    schedule_work(key_work);
    
    return 0;
}

// 按键的初始化函数
void key_hw_init()
{
    unsigned int *gpio_config;
    unsigned int data;
    
    gpio_config = ioremap(GPGCON, 4);	
    data = readl(gpio_config);
    data &= ~0b11;
    data |= 0b10;
    writel(data, gpio_config);	
    
    gpio_data = ioremap(GPGDAT, 4);	
}                                                

int key_open(struct inode *node, struct file *filp)
{
    return 0;
}

const struct file_operations key_fops = 
{	
    .open = key_open,		
};

// 初始化miscdevice
struct miscdevice key_miscdev = 
{
    .minor = 200,
    .name = "mykey",
    .fops = &key_fops,
};	

static int button_init()
{
    // 注册miscdevice
    misc_register(&key_miscdev);
    
    // 注册中断处理程序
    request_irq(IRQ_EINT8, key_int, IRQF_TRIGGER_FALLING, "mykey", 0);
    
    // 按键初始化
    key_hw_init();
    
    // 创建工作1
    key_work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
    INIT_WORK(key_work, key_work_func);
    
    // 初始化定时器
    init_timer(&key_timer);
    key_timer.function = key_timer_func;  // 设置超时函数
    
    // 注册定时器
    add_timer(&key_timer);
    
    return 0;		
}

static void button_exit()
{	
     // 注销miscdevice
    misc_deregister(&key_miscdev);  
    
    // 注销中断处理程序
    free_irq(IRQ_EINT8, 0);
}

MODULE_LICENSE("GPL");

module_init(button_init);
module_exit(button_exit);

 

 

Guess you like

Origin blog.csdn.net/qq_22847457/article/details/91411002