按键驱动-使用混杂设备驱动模型

一.混杂设备:字符设备的一种。当主设备号相同(10),次设备号为不同为混杂设备。

1.混杂设备的描述符:设备操作都需要通过一个设备描述符来对它进行操作。

混杂设备描述符:主设备号是10

struct miscdevice {

int minor; /* 次设备号*/

const char *name; /* 设备名*/

const struct file_operations *fops; /*文件操作*/

struct list_head list;

struct device *parent;

struct device *this_device;

};

扫描二维码关注公众号,回复: 13141762 查看本文章

2.所有的混杂设备会形成一个链表,通过次设备号来进行操作。

3.混杂设备注册函数:int misc_register(struct miscdevice * misc)

将一个初始化好的设备描述符misc注册到系统中。

二.按键驱动的初始化,注册与注销:混杂设备描述符管理

1.给按键设备描述符的minor,name,fops三个变量进行初始化赋值

例,具体数值无要求:struct miscdevice key_miscdev = {

    .minor = 200,

    .name = "key",

    .fops = &key_fops,

};

2.调用函数misc_register将初始化好的描述符misc进行注册

3.注销混杂设备:int misc_deregister(struct miscdevice * misc)

三.中断函数的初始化,注册,处理,注销:

1.初始函数:int request_irq(

unsigned int irq,  //由硬件说明书来获取对应拐角的中断号,进行外部中断号和中断处理函数的映射

void (*handler)(int, void*, strupt_regs *), //中断处理函数

unsigned long flags, //中断类型标志,不同标识处理的时机和方式不同

const char *devname, //设备名,自己取名字就好

void *dev_id)  //共享中断信号线

//返回0表示成功,或者返回一个错

2.中断处理函数:irqreturn_t key_int(int irq, void *dev_id) 没有第三个参数,

3.中断注销:void free_irq(unsigned int irq, void *dev_id)注销对应的中断号和中断信号线。

四.中断分层:工作队列,硬件操作在中断处理函数中,其他操作放工作队列

1.创建一个工作队列描述符并初始化:struct workqueue_struct *my_wq;

my_wq=create_workqueue("my_que");//工作队列描述符如下

struct workqueue_struct {

struct cpu_workqueue_struct *cpu_wq;

struct list_head list;

const char *name; /*workqueue name*/

int singlethread;

int freezeable; /* Freeze threads during suspend */

int rt;};

2.创建一个工作项,并初始化:struct work_struct *work1;

work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);//第二个参数为分配空间标志

    INIT_WORK(work1, work1_func);//处理函数加载到工作队列节点

3.将工作节点挂载到工作队列中: queue_work(my_wq,work1);//第一个参数为工作队列,第二个为工作节点项

注:可以不用第一步创建工作队列,省略第一步,将创建的工作项挂载到内核已有的工作队列中,第三步改成挂载函数为schedule_work(work1);

五.按键定时器去抖动:

1.定义一个定时器变量:struct timer_list buttons_timer;

struct timer_list {

struct list_head entry;

struct list_head entry;

unsigned long expires;//定时时间

void (*function)(unsigned long);//定时需要的执行的函数

unsigned long data;

struct tvec_base *base;

};

2.初始化:初始化,给功能函数赋值

init_timer(&buttons_timer);   

buttons_timer.function  = buttons_timer_function; //执行函数名

3.向内核注册一个定时器:

add_timer(&buttons_timer);  

4.在需要的地方开启定时回调函数:

mod_timer(&buttons_timer, jiffies + (HZ /10));

//jiffies表示当前时间,HZ表示一秒,一百毫秒超时回调

六.多按键实现:在注册中断处理函数时,结合硬件中断号再注册一遍处理函数,

在处理函数中检测硬件寄存器看是哪个按键被触发。

七.阻塞型驱动:

1.定义一个等待队列:wait_queue_head_t  key_q;

2.初始化队列:init_waitqueue_head(&my_queue);

3.挂起函数wait_event(key_q,condition);当condition为假阻塞当前进程。

4.唤醒函数wake_up(&key_q);在合适的地方(中断函数)调用此函数,唤醒阻塞的进程,使那个阻塞函数继续运行。

猜你喜欢

转载自blog.csdn.net/qq_43706825/article/details/103717396