编写Linux内核模块——第三部分:按键和发光二极管
【编者的话】了解了基本的内核模块开发、内核空间和用户空间交互之后,终于要开始和硬件设备直接交互了。Linux内核提供了对通用输入输出接口、中断请求等的封装,让驱动开发者可以利用中断来控制硬件线路上的设备。本文来自Derek Molloy的博客,通过三个示例,讲解了通用输入输出接口和中断编程,内核对象和内核线程的使用。 \
前言
\
在本系列文章中,我们已经知道了如何为嵌入式Linux设备编写可加载内核模块(loadable kernel module,LKM)。这是本系列的第三篇文章,在阅读本文之前,请先阅读: \
- 编写Linux内核模块——第一部分:前言 \
- 编写Linux内核模块——第二部分:字符设备
这两篇文章介绍了如何构建、加载和卸载可加载内核模块和字符设备驱动。这些细节不会在本文中重复。 \
本文描述了如何编写内核代码,使之能够和连接到嵌入式Linux系统通用输入输出接口(GPIO)的定制电子电路交互。同时也将介绍如何为这种嵌入式Linux系统中默认不支持的设备添加高级功能。本文使用BeagleBone作为部署平台,为了完成本文实验,使用BeagleBone是理想的方案,但不是必须的。 \
本文的重点是教学和实践,而不是解决一个深入实际的需求。但我相信如果读者能够阅读按键和发光二极管闪烁实验,那么也可以实现和大部分电子设备交互。因此,本文描述了三个不同的可加载内核模块,每个都有它自己独立的目的和需求: \
- 示例1:按钮按下,发光二极管亮起:在本例中,当按钮按下时,发光二极管将会亮起,非常简单(不,并不是)。为了完成这个任务,将会介绍内核中断相关概念,并且使用linux/gpio.h头文件提供的库来实现。本例用于测试中断性能。 \
- 示例2:增强的按键通用输入输出接口驱动:本例用于介绍kobjects对象和在sysfs中添加自定义项的机制。这将允许用户在运行时和可加载内核模块收发数据。本例同时介绍了内核代码中计时功能的使用。 \
- 示例3:增强的发光二极管通用输入输出接口驱动:本例用于让发光二极管闪烁,同时介绍了Linux内核线程。本质上,发光二极管通过可以在用户空间控制的内核模块,以一定频率闪烁。
和按钮、闪烁的发光二极管交互有其他简单的方式,但是这些示例介绍了一些对于处理复杂内核任务编程来说至关重要的概念。 \
视频演示
\
这里提供了一段YouTube上的短片,它呈现了本文开发的可加载内核模块的功能概述。 \
https://youtu.be/ltCZydX_zmk
视频1:本文描述的可加载内核模块的功能 \
电路
\
本文使用的唯一电路如图1所示。这是和《Exploring BeagleBone》书第六章相同的图,为了方便直接在这里使用。作为书中详细介绍的,使用了一个场效应管(Field Effect Transistor,FET)(小信号晶体管)作为发光二极管门电路,它确保了点亮发光二极管的电流不会损坏BeagleBone。可以通过串联一个大限流电阻,直接将发光二极管连接到通用输入输出接口上,但这是不建议的。图中按钮没有使用上拉或者下拉电阻,这是因为BeagleBone的P9_27端口默认配置了一个内置的下拉电阻。在连接类似电路的时候总是需要非常的小心,任何一个失误都可能导致BeagleBone损坏! \
(点击放大图像)
\
\\
图1:本文描述的发光二极管和按键电路 \
本次讨论的源码
\
本次讨论的所有代码都在为《Exploring BeagleBone》准备的GitHub仓库上。代码可以在ExploringBB GitHub仓库内核工程目录中公开查看,或者也可以将代码复制到BeagleBone(或者其他Linux设备):
molloyd@beaglebone:~$ sudo apt-get install git\molloyd@beaglebone:~$ git clone https://github.com/derekmolloy/exploringBB.git
\
代码中/extras/kernel/目录中的gpio_test、button和led是本文最重要的几个目录。为这些示例代码自动生成的Doxygen文档有HTML格式和PDF格式。 \
示例1:按钮按下,发光二极管亮起可加载内核模块
\
当在嵌入式Linux设备中和电子电路进行交互的时候,开发者立即需要面对sysfs文件系统,同时需要使用低级别文件操作。这种方式可能会导致效率低下(特别是对传统嵌入式系统有开发经验时),然而,这些文件项会使用内存映射,对于大部分应用程序性能来说是足够的。在我的书中,我已经证明了,通过使用pthread库、回调函数和sys/poll.h,在忽略CPU开销后,可以实现响应时间大约在三分之一毫秒。 \
和Linux用户空间不同,Linux内核空间支持中断。本文第一个示例演示了如何编写一个使用通用输入输出接口和中断的可加载内核模块,实现比在用户空间更快的响应时间。我不建议开发者将所有通用输入输出接口代码都在内核空间实现,但是这些示例可能可以提供一些启发,开发者可以在内核空间执行独立的任务,高级别代码仍然可以在Linux用户空间编写。 \
通用输入输出接口和内核
\
通用输入输出接口(General Purpose Input/Outputs,GPIOs)在书中第6章和前文/视频中有详细描述。这些软件控制的输入输出接口,能够在Linux用户空间使用通用输入输出接口的sysfs接口控制(直接使用Linux shell或者在可执行程序中操作),这些接口能够让开发者激活通用输入输出接口或者设置它的状态。比如,要通过sysfs激活图1中的发光二极管并开启/关闭发光二极管,可以用如下步骤实现(使用超级用户权限):
root@beaglebone:/sys/class/gpio# ls\export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport\root@beaglebone:/sys/class/gpio# echo 49 \u0026gt; export\root@beaglebone:/sys/class/gpio# ls\export gpio49 gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport\root@beaglebone:/sys/class/gpio# cd gpio49\root@beaglebone:/sys/class/gpio/gpio49# ls\active_low direction edge power subsystem uevent value\root@beaglebone:/sys/class/gpio/gpio49# echo out \u0026gt; direction\root@beaglebone:/sys/class/gpio/gpio49# echo 1 \u0026gt; value\root@beaglebone:/sys/class/gpio/gpio49# echo 0 \u0026gt; value\root@beaglebone:/sys/class/gpio/gpio49# cd ..\root@beaglebone:/sys/class/gpio# echo 49 \u0026gt; unexport\root@beaglebone:/sys/class/gpio# ls\export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport
\
有趣的是,在Linux内核空间控制通用输入输出接口的步骤和上述步骤非常类似。Linux通用输入输出接口,可以通过linux/gpio.h(3.8.x版本)头文件中定义的函数在内核空间方便的访问和控制。这里列举了包含在该头文件中的一些重要的函数:
static inline bool gpio_is_valid(int number) // 检查通用输入输出接口号是否有效(在BeagleBone开发版上最大值是127)\static inline int gpio_request(unsigned gpio, const char *label) // 分配通用输入输出接口号和提供给sysfs的标签\static inline int gpio_export(unsigned gpio, bool direction_may_change) // 通过sysfs激活接口,并且决定端口方向,可以从输入变成输出,反之亦然\static inline int gpio_direction_input(unsigned gpio) \// 设置为输入方向(和通常一样,成功返回0)\static inline int gpio_get_value(unsigned gpio) \// 获取输入输出接口设置的值(方向)\static inline int gpio_direction_output(unsigned gpio, int value) // 值是当前状态\static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) \// 以毫秒为单位设置去抖动时间(平台相关)\static inline int gpio_sysfs_set_active_low(unsigned gpio, int value) \// 设置低电平有效(反转运行状态)\static inline void gpio_unexport(unsigned gpio) \// 从sysfs移除\static inline void gpio_free(unsigned gpio) \// 释放通用输入输出端口\static inline int gpio_to_irq(unsigned gpio) \// 与中断请求关联
\
重要的是,通过上述列表中的最后一个函数,可以将一个中断请求(interrupt request,IRQ)关联到通用输入输出接口上。中断请求允许开发者构建有效率、高性能的代码来检测输入状态改变。我们将在下面讨论中断和在Linux操作系统上的使用。有关Linux系统上使用输入输出接口使用的更多信息,参阅: \
- 为用户空间提供的通用输入输出接口sysfs接口 \
- 通用输入输出接口(内核空间)
中断
\
中断是从连接的硬件设备、软件应用程序或者电路发送给微处理器的信号,该信号标示了有需要关注的事件发生。中断是高优先级条件,它大致的含义是“中断当前正在做的事情,做一些其他事情”。处理器挂起当前活动,保存当前状态并且执行中断处理函数,这也被成为中断服务例程(interrupt service routine,ISR)。一旦处理函数执行完毕,处理器重新加载之前的状态,继续进行之前的活动。 \
可加载内核模块驱动必须注册中断的处理函数,它定义了当中断发生时需要执行的操作。在本例中处理函数命名为ebbgpio_irq_handler(),它的形式如下:
static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs) { \ // 中断发生时需要执行的操作\ … }
\
处理函数然后通过request_irq()函数注册为中断请求(IRQ),步骤如下:
result = request_irq(irqNumber, \// 注册的中断号 \ (irq_handler_t) ebbgpio_irq_handler, \// 指向中断处理函数的指针(如上所示)\ IRQF_TRIGGER_RISING, \// 中断在上升沿(图1中按键按下)\ \"ebb_gpio_handler\
编写Linux内核模块——第三部分:按键和发光二极管相关推荐
- 编写Linux内核模块——第一部分:前言
[编者的话]Linux内核模块作为Linux内核的扩展手段,可以在运行时动态加载和卸载.它是设备和用户应用程序之间的桥梁,可以通过标准系统调用,为应用程序屏蔽设备细节.本文来自Derek Molloy ...
- linux内核模块编写,Linux内核模块编程
1 总体设计思路 Linux内核是单体式结构,相对于微内核结构而言,其运行效率高,但是系统的可维护性和可扩展性较差.为此,Linux提供了内核模块(module)机制,它不仅可以弥补单体式内核相对于微 ...
- Linux内核模块学习笔记(转载)
Linux内核模块 Linux设备驱动会以内核模块的形式出现,因此学会编写Linux内核模块编程是学习linux设备驱动的先决条件. 1.1linux内核模块简介 Linux内核的整体结构非常庞 ...
- Linux内核模块的概念和基本的编程方法
Linux内核模块的概念和基本的编程方法 标签: Linux内核模块 2013-06-14 18:29 1864人阅读 评论(0) 收藏 举报 分类: linux内核(34) 版权声明:本文为博主原创 ...
- 编写一个 Linux 内核模块
编写一个 Linux 内核模块 作者:解琛 时间:2020 年 8 月 16 日 编写一个 Linux 内核模块 一.实验环境 二.Linux 内核模块相关命令 三.程序架构 四.编写一个内核模块 4 ...
- 编写函数实现员工信息录入和输出_编写我的第一个Linux 内核模块“hello_module”...
前言: Linux 内 核 模 块 全 称 为 " 动 态 可 加 载 内 核 模 块 (Loadable Kernel Module,LKM)",是系统内核向外部提供的功能插口. ...
- linux python开发环境sql数据迁移到mysql_运用Python语言编写获取Linux基本系统信息(三):Python与数据库编程,把获取的信息存入数据库...
运用Python语言编写获取Linux基本系统信息(三):Python与数据库编程 有关前两篇的链接: 一.实验环境: Python2.7.10.pycharm.VM虚拟机.CentOS6.3.mys ...
- Linux内核模块编写详解
内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了.Linux内核和它的用户空间是大不相同的:抛开漫不经心,你必须小心翼翼,因为你编程中的一个bug就会影响到整个系统,本文给大家介 ...
- Linux内核模块编程系列1-极简内核模块编写
1.准备工作 使用如下命令查看自己Linux的内核版本 uname -a 结果如下: Linux VM-73-203-debian 4.9.0-6-amd64 #1 SMP Debian 4.9.88 ...
最新文章
- ubuntu16.04开机卡死解决方案
- Mybatis解析动态sql原理分析
- Spring JDBC-Spring事务管理之数据库事务基础知识
- Android Bundle类,通过bundle实现在两个activity之间的通讯
- [Java基础]并发修改异常
- python百度翻译爬虫_Python爬虫教程-05-python爬虫实现百度翻译
- pycharm无法导入Pillow
- 20. Magento 创建新闻模块(1)
- 解决啦啦外卖小程序无法生成海报推广码
- 计算机地图制图符号制作的心得,计算机地图制图.docx
- android 模拟器 pubg,雷电安卓模拟器怎么玩绝地求生刺激战场 PC端带你愉快吃鸡...
- 洛谷 P2342 叠积木 题解
- Android补间动画之旋转动画
- 堡垒机Windows远程桌面连接服务器黑屏解决
- 理解什么是DTO?什么是AutoMapper?
- 徽章系列3: Travis CI 的使用
- 多目标优化(一)简单的 NSGA-Ⅱ
- python之pypinyin
- coreldraw2019天气滤镜_coreldraw2019为什么值得下载
- 一种基于Harris-Laplace算法的角点检测方法(Matlab代码实现)
热门文章
- matlab学习——1.基本操作
- Visual Studio 2012 调试程序加载缓慢,提示正在下载符号
- pyinstaller深入使用,打包指定模块,打包静态文件
- 查找、下载基因序列的方法
- jenkins构建一个maven项目[五]
- 关于百度分享——bdCustomStyle一点bug
- Android平台利用OpenCL框架实现并行开发初试
- TSQL--NESTED LOOPS JOIN
- 用例子来彻底搞明白Virtual 和 非 virtual(C#)
- java arraylist 实现原理_Java进阶--深入理解ArrayList实现原理