嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十五)驱动程序基石

文章目录

    • 1.1 休眠与唤醒
      • 1.1.1 适用场景
      • 1.1.2 内核函数
        • 1.1.2.1 休眠函数
        • 1.1.2.2 唤醒函数
      • 19.1.3 驱动框架
      • 1.1.4 编程
        • 1.1.4.1 驱动程序关键代码
        • 1.1.4.2 应用程序
      • 1.1.5 上机实验
      • 1.1.6 使用环形缓冲区改进驱动程序
    • 1.2 POLL机制
      • 1.2.1 适用场景
      • 1.2.2 使用流程
      • 1.2.3 驱动编程
      • 1.2.4 应用编程
      • 1.2.5 现场编程
      • 1.2.6 上机实验
      • 1.2.7 POLL机制的内核代码详解
      • 1.2.7.1 sys_poll函数
        • 1.2.7.2 do_sys_poll函数
        • 1.2.7.3 do_poll函数
    • 1.3 异步通知
      • 1.3.1 适用场景
      • 1.3.2 使用流程
      • 1.3.3 驱动编程
      • 1.3.4 应用编程
      • 1.3.5 现场编程
      • 1.3.6 上机编程
      • 1.3.7 异步通知机制内核代码详解
    • 1.4 阻塞与非阻塞
      • 1.4.1 应用编程
      • 1.4.2 驱动编程
      • 1.4.3 驱动开发原则
    • 1.5 定时器
      • 1.5.1 内核函数
      • 1.5.2 定时器时间单位
      • 1.5.3 使用定时器处理按键抖动
      • 1.5.4 现场编程、上机
      • 1.5.5 深入研究:定时器的内部机制
      • 1.5.6 深入研究:找到系统滴答
    • 1.6 中断下半部tasklet
      • 1.6.1 内核函数
        • 1.6.1.1 定义tasklet
        • 1.6.1.2 使能/禁止tasklet
        • 1.6.1.3 调度tasklet
        • 1.6.1.4 kill tasklet
      • 19.6.2 tasklet使用方法
      • 19.6.3 tasklet内部机制
    • 1.7 工作队列
      • 1.7.1 内核函数
        • 1.7.1.1 定义work
        • 1.7.1.2 使用work:schedule_work
        • 1.7.1.3 其他函数![在这里插入图片描述](https://img-blog.csdnimg.cn/2734a5e0a7db44fe992e6f77eeb604b2.png” />
      • 1.7.2 编程、上机
      • 1.7.3 内部机制
        • 1.7.3.1 Linux 2.x的工作队列创建过程
        • 1.7.3.2 Linux 4.x的工作队列创建过程
    • 1.8 中断的线程化处理
      • 1.8.1 内核机制
        • 1.8.1.1 调用request_threaded_irq后内核的数据结构
        • 1.8.1.2 request_threaded_irq
        • 1.8.1.3 中断的执行过程
      • 1.8.2 编程、上机
    • 1.9 mmap
      • 1.9.1 内存映射现象与数据结构
      • 1.9.2 ARM架构内存映射简介
        • 1.9.2.1 一级页表映射过程
        • 1.9.2.2 二级页表映射过程
      • 1.9.3 怎么给APP新建一块内存映射
        • 1.9.3.1 mmap调用过程
        • 1.9.3.2 cache和buffer
        • 1.9.3.3 驱动程序要做的事
      • 1.9.4 编程
        • 1.9.4.1 APP编程
        • 1.9.4.2 驱动编程
        • 1.9.4.3 上机测试

1.1 休眠与唤醒

1.1.1 适用场景

在前面引入中断时,我们曾经举过一个例子:

1.1.2.2 唤醒函数

参考内核源码:includelinuxwait.h。

1.1.4 编程

使用GIT命令载后,源码位于这个目录下:

03_read_key_irq_circle_buffer使用了环型缓冲区,可以避免按键丢失。

1.1.4.1 驱动程序关键代码

02_read_key_irqgpio_key_drv.c中,要先定义“wait queue”:

在驱动的读函数里调用wait_event_interruptible:

第49行并不一定会进入休眠,它会先判断g_key是否为TRUE。
执行到第50行时,表示要么有了数据(g_key为TRUE),要么有信 等待处理(本节课程不涉及信 )。

假设g_key等于0,那么APP会执行到上述代码第49行时进入休眠状态。它被谁唤醒控制的中断服务程序:

上述代码中,第72行确定按键值g_key,g_key也就变为TRUE了。
然后在第73行唤醒gpio_key_wait中的第1个线程。
注意这2个函数,一个没有使用“&”,另一个使用了“&”:

1.1.4.2 应用程序

应用程序并不复杂,调用open、read即可,代码在button_test.c中:

在33行~38行的循环中,APP基本上都是休眠状态。你可以执行top命令查看CPU占用率。

1.1.5 上机实验

跟上一节视频类似,需要先修改设备树,请使用上一节视频的设备树文件。
然后安装驱动程序,运行测试程序。

1.1.6 使用环形缓冲区改进驱动程序

使用GIT命令载后,源码位于这个目录下:

使用环形缓冲区,可以在一定程序上避免按键数据丢失,关键代码如下:

使用休眠-唤醒的方式等待某个事件发生时,有一个缺点:等待的时间可能很久。我们可以加上一个超时时间,这时就可以使用poll机制。
① APP不知道驱动程序中是否有数据,可以先调用poll函数查询一下,poll函数可以传入超时时间;
② APP进入内核态,调用到驱动程序的poll函数,如果有数据的话立刻返回;
③ 如果发现没有数据时就休眠一段时间;
④ 当有数据时,比如当按下按键时,驱动程序的中断服务程序被调用,它会记录数据、唤醒APP;
⑤ 当超时时间到了之后,内核也会唤醒APP;
⑥ APP根据poll函数的返回值就可以知道是否有数据,如果有数据就调用read得到数据

1.2.2 使用流程

妈妈进入房间时,会先看小孩醒没醒,闹钟响之后走出房间之前又会再看小孩醒没醒。
注意:看了2次小孩!
POLL机制也是类似的,流程如下:

嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十五)驱动程序基石
函数执行流程如上图①~⑧所示,重点从③开始看。假设一开始无按键数据:
③ APP调用poll之后,进入内核态;
④ 导致驱动程序的drv_poll被调用:
注意,drv_poll要把自己这个线程挂入等待队列wq中;假设不放入队列里,那以后发生中断时,中断服务程序去哪里找到你嘛br> drv_poll还会判断一下:有没有数据啊回这个状态。
⑤ 假设当前没有数据,则休眠一会;
⑥ 在休眠过程中,按下了按键,发生了中断:
在中断服务程序里记录了按键值,并且从wq中把线程唤醒了。
⑦ 线程从休眠中被唤醒,继续执行for循环,再次调用drv_poll:
drv_poll返回数据状态
⑧ 哦,你有数据,那从内核态返回到应用态吧
⑨ APP调用read函数读数据
如果一直没有数据,调用流程也是类似的,重点从③开始看,如下:
③ APP调用poll之后,进入内核态;
④ 导致驱动程序的drv_poll被调用:
注意,drv_poll要把自己这个线程挂入等待队列wq中;假设不放入队列里,那以后发生中断时,中断服务程序去哪里找到你嘛br> drv_poll还会判断一下:有没有数据啊回这个状态。
⑤ 假设当前没有数据,则休眠一会;
⑥ 在休眠过程中,一直没有按下了按键,超时时间到:内核把这个线程唤醒;
⑦ 线程从休眠中被唤醒,继续执行for循环,再次调用drv_poll:
drv_poll返回数据状态
⑧ 哦,你还是没有数据,但是超时时间到了,那从内核态返回到应用态吧
⑨ APP不能调用read函数读数据

注意几点:
① drv_poll要把线程挂入队列wq,但是并不是在drv_poll中进入休眠,而是在调用drv_poll之后休眠
② drv_poll要返回数据状态
③ APP调用一次poll,有可能会导致drv_poll被调用2次
④ 线程被唤醒的原因有2:中断发生了去队列wq中把它唤醒,超时时间到了内核把它唤醒
⑤ APP要判断poll返回的原因:有数据,还是超时。有数据时再去调用read函数。

1.2.3 驱动编程

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年2月13日
下一篇 2022年2月13日

相关推荐