多功能按键驱动

多功能按键驱动

该驱动是基于ARM9硬件平台,仅供自己学习和记录。该驱动包含以下功能:

  • 响应K1和K4按键中断
  • 中断分层处理
  • 使用内核处理队列
  • 按键防抖动
  • 使用定时器
  • 阻塞功能

响应K1和K4按键中断

按键k1,k4属于字符设备,主设备号为10的字符设备称为“混杂设备”。混杂设备的中断处理包括1.混杂设备文件初始化。2.注册混杂设备3.注册中断 3.硬件初始化 4 。中断处理函数编写 5.注销中断

中断分层处理–中断嵌套

系统进行响应中断的时候,如果同一时刻到达相同类型的中断,后到达的往往会被舍弃。这显然不是我们希望见到的。可采取这样一种办法。减少中断处理的时间,越短越好。在中断处理函数中,只处理和硬件有关的工作。其余的工作交给系统工作队列。系统会在系统合适的时间去处理这一项工作。
中断嵌套有三种方式1.软中断2.tasklet3.工作队列。中断处理函数是运行在中断上下文的,他的行为会受到一些限制。如不能使用可能引起阻塞的函数、不是使用可能引起调度的函数。
中断上半部分:当中断发生时,进行相应的寄存器读写。并登记该中断。
中断上半部分:在系统空闲时 对上半部登记的中断进行后续处理。

使用内核处理队列

工作队列是将一种任务推后执行的形式,他推后的任务由一个内核线程完成。这样下半部会在进程上下文执行。每个被推送的任务叫做“工作”从而组成工作队列。struct work_struct 结构

struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;//工作队列对应的执行函数
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

包括创建队列create_workqueue(一般不需要 使用系统自带队列)2.创建工作 INIT_WORK()3.提交工作 queue_work()如果提交给系统schedule_work

定时器

定时器的结构为struct timer_list key_timer;

struct timer_list {
    /*
     * All fields that change during normal runtime grouped to the
     * same cacheline
     */
    struct list_head entry;
    unsigned long expires;
    struct tvec_base *base;

    void (*function)(unsigned long);//定时器定时时间到的超时函数
    unsigned long data;

    int slack;

#ifdef CONFIG_TIMER_STATS
    int start_pid;
    void *start_site;
    char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

使用流程包括1.定义定时器变量2.初始化定时器 3。注册定时器add_timer4.启动定时器mod_timer

阻塞功能

举个例子 去读某一位置,这个位置数据还没到,我们就去读,显然这是读不到的 但是并不代表永远都没有数据,为了读到数据,我们应将其变成阻塞的。将其设置成阻塞功能包括如下几个部分:
1.定义等待队列
wait_queue_head_t my_queue;
2.初始化等待队列
init_waitqueue_head (&my_queue);
3.进入等待序列
wait_enent(queue,condition)
condition为真时立即返回。否则进程进入TASK_UNINTERRUPTIBLE模式进入睡眠, 并挂载在等待队列queue上。
wait_enent_interruptible(queue,condition)
condition为真时立即返回。否则进程进入TASK_INTERRUPTIBLE模式进入睡眠 ,并挂载在等待队列queue上。
4.从等待队列中唤醒
wake_up(q);唤醒TASK_UNINTERRUPTIBLE、TASK_INTERRUPTIBLE、TASK_KILLABLE的所有进程。
wake_up_interruptible(q);唤醒状态为TASK_INTERRUPTIBLE的所有进程。

key.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");

#define GPFCON 0X56000050
#define GPFDAT 0X56000054

struct work_struct *work1;//工作
struct timer_list key_timer;//定时器结构
unsigned int * gpio_dat = NULL; 
unsigned int key_no = 0;;
wait_queue_head_t key_q;//内核等待队列

void work1_fun()
{
    //jiffies为当前时间。1HZ 对应的时间为1s
    //启动定时器
    mod_timer(&key_timer, jiffies + HZ/10 );
}

irqreturn_t key_int(int irq, void *dev_id)
{
    //检测是否发生按键中断

    //清除按键中断

    //打印按键值

    //将工作交给系统队列    
    schedule_work(work1);

    return 0;
}
void key_hw_init()
{
    unsigned int *gpio_config;
    unsigned short data;
    printk("initial success\n");
    gpio_config = ioremap(GPFCON, 4);   
    data = readw(gpio_config);
    data &= ~0b1111;
    data |=  0b1010;
    writew(data, gpio_config);
    //物理地址转化为虚拟地址
    gpio_dat = ioremap(GPFDAT, 4);

}

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

long key_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
    return 0;
}
ssize_t key_read(struct file *filep, char __user *buf, size_t size, loff_t *whence)
{
    //初始化的key_no为0,key_no为真的是好wait_event立即返回
    wait_event(key_q, key_no);
    printk("in kernel num is %d\n", key_no);
    copy_to_user(buf, &key_no, 4);
    key_no  = 0;
    return 0;   
}
static struct file_operations key_fops =
{
    .open             = key_open,
    .read             = key_read,
    .unlocked_ioctl   = key_ioctl,  
};

static struct miscdevice key_miscdev =
{
    .minor = 200,
    .name  = "key", 
    .fops  = &key_fops,
};



void key_timer_fuc(unsigned long num)
{
    unsigned int key_value = 0;
    key_value = readw(gpio_dat) & 0x03;
    if (key_value == 2)
    {
        printk("KEY_4 interrupt\n");
        key_no = 4;
    }
    if (key_value == 1)
    {
        printk("KEY_1 interrupt\n");
        key_no = 1;
    }

    //唤醒等待进程
    wake_up(&key_q);
}
static int key1_init()
{
    int n = 3;
    //注册混杂设备
    misc_register(&key_miscdev);    
    //注册中断
    n = request_irq(IRQ_EINT0, key_int, IRQF_TRIGGER_FALLING, "key", 0);
    request_irq(IRQ_EINT1, key_int, IRQF_TRIGGER_FALLING, "key", 0);
    printk("n=%d",n);
    //按键初始化
    key_hw_init();

    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work1 ,work1_fun);

    //定时器初始化
    init_timer(&key_timer);
    key_timer.function = key_timer_fuc;
    add_timer(&key_timer);
    init_waitqueue_head(&key_q);

    return 0;   
}

static void key1_exit()
{
    //注销混杂设备
    misc_deregister(&key_miscdev);
    //注销中断
    free_irq(IRQ_EINT0, 0); 
}

module_init(key1_init);
module_exit(key1_exit);

key_app.c

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int fd = 0;
   int key_num = 0;
   fd = open("/dev/2440key", 0);

   read(fd, &key_num, 4);
   printf("key is %d",key_num); 
   return 0;

}

Makefile

obj-m := key.o
KDIR := /lesson1/linux-tq2440
all:
    make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
    rm -f *.ko *.mod.o *.mod.c *.symvers *.bak *.order *.o
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值