• 第四季-专题12-按键驱动程序设计


    专题12-按键驱动程序设计

    第1课-混杂设备驱动模型

    1. 混杂设备描述

    在Linux系统中,存在一类字符设备,它们拥有相同的主设备号(10),但次设备号不同,我们称这类设备为混杂设备(miscdevice)。所有的混杂设备形成一个链表,对设备访问时内核根据混杂设备形成一个链表,对设备访问时内核根据次设备号查找到相应的混杂设备。

    Linux中使用struct miscdevice来描述一个混杂设备。

    struct miscdevice {

    int minor; /* 次设备号*/

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

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

    struct list_head list;

    struct device *parent;

    struct device *this_device;

    };

    1. 混杂设备注册

    Linux中使用misc_register函数来注册一个混杂设备驱动。

    int misc_register(struct miscdevice * misc)。

    1. 范例分析

    初始化miscdevice:minor,name,fops

    注册miscdevice:misc_register

    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 GPFCON 0x56000050

    irqreturn_t key_int(int irq, void *dev_id)

    {

        //1. 检测是否发生了按键中断

        //2. 清除已经发生的按键中断

        //3. 打印按键值

        printk("key down! ");

       

        return 0;   

    }

    void key_hw_init()

    {

        unsigned short data;

        unsigned int *gpio_config;

        gpio_config = ioremap(GPFCON,4);

        data = readw(gpio_config);

        data &= ~0b11;

        data |= 0b10;

        writew(data,gpio_config);

    }

    int key_open(struct inode *node,struct file *filp)

    {

        return 0;

    }

    struct file_operations key_fops =

    {

        .open = key_open,

    };

    struct miscdevice key_miscdev = {

        .minor = 200,

        .name = "tq2440key",

        .fops = &key_fops,

    };

    static int button_init()

    {

        misc_register(&key_miscdev);

        //按键硬件初始化

        key_hw_init();

        //注册中断处理程序

        request_irq(IRQ_EINT0,key_int,IRQF_TRIGGER_FALLING,"tq2440key",0);

        return 0;

    }

    static void button_exit()

    {

        misc_deregister(&key_miscdev);

    }

    module_init(button_init);

    module_exit(button_exit);

    第2课-Linux中断处理

    1. 逻辑中断处理程序回顾

    中断有个统一的入口,事先要注册中断处理程序,根据中断源编号调用中断处理程序。

    1. Linux中断处理流程分析

     

    1. Linux中断处理程序设计

    (1)       注册中断

    request_irq函数用于注册中断

    int request_irq(unsigned int irq,

    void (*handler)(int, void*, struct

    pt_regs *),

    unsigned long flags,

    const char *devname,

    void *dev_id)

    返回0表示成功,或者返回一个错误码。

    unsigned int irq:中断号

    v void (*handler)(int,void *):中断处理函数。

    v unsigned long flags:与中断管理有关的各种选项。

    v const char * devname:设备名

    v void *dev_id:共享中断时使用。

    在flags参数中,可以选择一些与中断管理有关的选项,如:

    IRQF_DISABLED(SA_INTERRUPT)

    如果设置该位,表示是一个“快速”中断处理程序;如果没有设置这位,那么是一个“慢速”中断处理程序。

    IRQF_SHARED(SA_SHIRQ)

    该位表明该中断号是多个设备共享的。

    快/慢速中断的主要区别在于:快速中断保证中断处理的原子性(不被打断),而慢速中断则不保证。换句话说,也就是“开启中断”标志位(处理器IF)在运行快速中断处理程序时是关闭的,因此在服务该中断时,不会被其他类型的中断打断;而调用慢速中断处理时,其它类型的中断仍可以得到服务。

    (2)       中断处理

    中断处理程序的特别之处是在中断上下文中运行的,它的行为受到某些限制:不能使用可能引起阻塞的函数;不能使用可能引起调度的函数。

             检查设备是否产生了中断,清除中断产生标志,相应的硬件操作。

    (3)       注销处理

    当设备不再需要使用中断时(通常在驱动卸载时), 应当把它们注销, 使用函数:

    void free_irq(unsigned int irq, void *dev_id)

    (4)程序

    第3课-按键驱动硬件操作实现

    第4课-中断分层处理

    1. 中断嵌套

     

    1. 中断分层处理

    上半部:当中断发生,它进行相应地硬件读写,并“登记”该中断。通常由中断处理程序充当上半部。

    下半部:在系统空闲的时候对上半部“登记”的中断进行后续处理

    (1)       软中断

    (2)       tasklet

    (3)       工作队列

    工作队列是一种将任务推后执行的形式,他把推后的任务交由一个内核线程去执行。这

    样下半部会在进程上下文执行,它允许重新调度甚至睡眠。每个被推后的任务叫做“工作”,由这些工作组成的队列称为工作队列。

     

    l  Linux内核使用struct work_struct来描述一个工作队列:

    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;

    };

    l  Linux内核使用struct work_struct来描述一个工作项:

    struct work_struct {

    atomic_long_t data;

    struct list_head entry;

    work_func_t func;

    };

    typedef void (*work_func_t)(struct work_struct *work);

    l  工作队列创建步骤:

    step1. 创建工作队列

    create_workqueue

    step2. 创建工作

    INIT_WORK

    step3. 提交工作

    queue_work

    1. 使用工作队列实现分层

    在大多数情况下, 驱动并不需要自己建立工作队列,只需定义工作, 然后将工作提交到内核已经定义好的工作队列keventd_wq中。

    提交工作到默认队列:schedule_work

    第5课-按键定时器去抖

    1. 按键去抖

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

    按键去抖动的方法主要有二种,一种是硬件电路去抖动;另一种就是软件延时去抖。而延

    时又一般分为二种,一种是for循环等待,另一种是定时器延时。在操作系统中,由于效率方面的原因,一般不允许使用for循环来等待,只能使用定制器。

    1. 内核定时器

    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)       init_timer初始化

    (2)       设置超时函数

    1. add_timer注册定时器
    2. mod_timer启动定时器

    第6课-驱动支持多按键优化

    第7课-阻塞型驱动设计

    1. 阻塞必要性

    当一个设备无法立刻满足用户的读写请求时应当如何处理? 例如:调用read时,设备没有数据提供, 但以后可能会有;或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据。当上述情况发生的时候,驱动程序应当(缺省地)阻塞进程,使它进入等待(睡

    眠)状态,直到请求可以得到满足。

    2.内核等待队列

    在实现阻塞驱动的过程中,也需要有一个“候车室”来安排被阻塞的进程“休息”,当唤醒它们的条件成熟时,则可以从“候车室”中将这些进程唤醒。而这个“候车室”就是等待队列。

    (1)定义等待队列:wait_queue_head_t my_queue

    (2)初始化等待队列:init_waitqueue_head(&my_queue)

    (3)定义+初始化等待队列:DECLARE_WAIT_QUEUE_HEAD(my_queue)

    (4)进入等待队列,睡眠

    wait_event(queue,condition)

    当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在queue参数所指定的等待队列上。

    wait_event_interruptible(queue,condition)

    当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_INTERRUPTIBLE的睡眠,并挂在queue参数所指定的等待队列上。

    int wait_event_killable(queue, condition)

    当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。

    (5)从等待队列中唤醒进程

    wake_up(wait_queue_t *q)

    从等待队列q中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE 的所有进程。

    wake_up_interruptible(wait_queue_t *q)

    从等待队列q中唤醒状态为TASK_INTERRUPTIBLE 的进程

    3. 阻塞驱动优化

             对按键驱动进行阻塞型改造

  • 相关阅读:
    js append()和appendChild()和insertBefore()的区别
    webpack打包工具简单案例
    Vue $ref 的用法
    Vue学习笔记-作用域插槽
    Vue学习笔记-插槽基本使用
    Vue学习笔记-父子通信案例
    Echarts案例-折线图
    Echarts案例-柱状图
    软件构造实验三-递归下降分析分析法
    软件构造实验二-拷贝一个c文件 将其中的关键字int替换成float
  • 原文地址:https://www.cnblogs.com/free-1122/p/11452261.html
Copyright © 2020-2023  润新知