迫于项目的压力,一直没时间更新文章,最近项目取得阶段性进展,总算有点时间写文章了(顺便去电影院看了《Alien: Romulus》,拍的很好,强烈推荐)。其实这篇文章的内容很早就完成了,当时本来想写文章记录的,但为了赶进度只能无奈放弃。毕竟玩树莓派就是玩它的GPIO,这也是树莓派和PC除架构外最显著的区别:它具有更加强烈的工业属性,换言之它适合去读取传感器的数据/控制一些设备。
话不多说,让我们平复一下心情,进入今天的正题。
前期准备
基本和上篇hello驱动移植差不多,我选择的是交叉编译,所以需要在Linux虚拟机中配置环境变量为交叉编译工具链。因为驱动的编译需要在编译好了的内核目录下进行,故这里我们默认树莓派内核代码已被成功下载和编译。值得注意的一个点是,下载的树莓派代码内核版本需要和使用中的树莓派内核版本一致,否则需要更换使用中树莓派的系统内核。理由很简单:编译出的驱动适配的系统版本是已下载树莓派代码的内核版本,内核版本不一致会导致编译出的驱动无法装载至现有系统。对上述概念依然模糊的话可参考上一篇文章:
韦东山初版hello驱动树莓派4B移植(64位系统)——操作篇https://blog.youkuaiyun.com/qq_38667470/article/details/145865476
修改并编译led驱动
因为是移植,所以我们直接使用韦东山写好的代码,将下图中的01_led文件夹拖动到我们自己的工程目录(比如我的放到了/home/nfs_rootfs/driver_projects):
注意路径,最初的目录是桌面那个文件夹
在自己的工程目录打开01_led文件夹,里面的文件如下图所示:
这是未编译的状态,这三个文件从左到右分别是:用于和硬件交互的驱动文件,用于测试驱动的应用文件,用于编译这两个文件的makefile。在终端定位到这个目录并执行make命令后,交叉编译工具会根据makefile将drv.c编译为drv.ko(即未装载的驱动程序),将test.c编译为test应用程序。我们需要先修改makefile并编译出合适的驱动和应用程序,然后装载驱动并执行应用程序进行测试。
修改makefile
原版的makefile如下所示:
我们的内核版本不同,将第一行中的路径改为已经编译好了的树莓派内核路径,我的路径如下:
KERN_DIR = /home/book/100ask_imx6ull_mini-sdk/linux-rpi-6.1.y
解释一下,执行make会编译所有文件,执行make clean会清除之前编译出的程序,obj-m表示这个文件会被编译为内核模块(这里就是驱动程序的意思)
修改gpio
韦东山使用的开发板和树莓派不同,我们需要修改驱动代码中gpio的编号,那么问题来了:在我们的驱动代码中应该将gpio的编号改成多少呢?
首先我们要明确读取/操纵的设备连接到的树莓派gpio端口,即具体的gpio位置,然后参照类似下图中对照表得到gpio编号:
从上图中可以得知树莓派引脚的三种编码方式:wiringPi编码、BCM编码和BOARD编码。其中BOARD编码不具备软件映射功能,仅仅标识gpio在开发板上的位置;wiringPi是一个C语言库,在树莓派的C语言IDE中包含这个库的头文件可以调用库函数直接操纵gpio,此时使用的就是wiringPi编码标识gpio;而在驱动代码中我们使用BCM编码作为gpio的编号。
如果没有上面的对照表,也可以通过下面的命令快速调出这些映射关系:
gpio readall
韦东山视频中驱动的是led,树莓派上没有便于实验的led,我使用双路继电器中的其中一路作为替代。这一路的控制端连接到了左排倒数第五个gpio,对应的编号是6。我们打开led_drv.c,找到gpio结构体数组的部分:
可以看到这里定义了一个容量为2的结构体数组,对照上面的gpio结构体定义可知:这段代码将gpio数组中第0个gpio的编号设置为131,中断号设置为0,名字设置为led0。为了使驱动和硬件对应,我们将这里的131更改为6。
执行编译
至此所有的代码修改完成,可以开始交叉编译。打开终端,cd到01_led目录下,输入make回车,目录中的文件会变化为下图这样:
这里面只有led_drv.ko(驱动)和led_test(应用)是我们需要的,通过filezila之类的无线传输软件将这两个文件传输到开发板的某个文件夹,等待装载和测试。
装载驱动并测试
通过vnc之类的无线连接软件连接到开发板,打开终端cd到装有驱动和应用的文件夹,执行下面的命令装载驱动:
sudo insmod led_drv.ko
因为我们在驱动代码中自动创建了设备节点,故不用再手动创建设备节点,可以打开led_drv.c找到设备文件名的设置部分:
得知设备文件名后可以通过下面的命令快速确认我们的驱动是否安装成功:
ls /dev/100ask_led
如果终端打印出的是ls后面的内容说明驱动安装成功。
接下来可以开始测试,用于测试的led_test应用程序可能没有开放权限,我们执行下面的命令开放所有权限:
chmod 777 led_test
在正式测试驱动之前,我们还需要知道如何执行应用程序。led_test.c中包含有使用提示,可以先执行sudo su切换为root用户,再执行应用程序:
./led_test
此时终端会打印提示信息:
Usage: ./led_test <0|1|2|...> [on | off]
解释一下,在linux中<>表示必须包含的内容,[]则表示可选择的内容。这个应用程序既可以读取gpio的状态(未输入on/off),也可以控制gpio的状态(后面加上on/off)。因为不管是硬件连接还是驱动代码中都只设置了一个gpio口,故./led_test后面的数字只能设置为0,可以先执行下面的命令查看gpio的状态:
./led_test 0
此时终端打印信息如下:
led 0 status is off
然后执行下列命令改变gpio的状态:
./led_test 0 on
执行完后继电器立刻传来了清脆的咔哒声,这说明成功改变了gpio的状态。不过我们还是可以通过之前的命令再确认一下,执行完查看命令后发现之前的off已经变为了on,确认成功。
至此所有的移植流程结束。
补充
on/off
部分人可能对这里的on/off有点疑惑,这两个哪个代表输出高电平,哪个代表输出低电平呢?
这点并不用纠结,这里的on/off代表的是灯的亮灭。on可以是输出高电平,也可以是输出低电平,具体是哪种需要参考实际电路,可以参考我之前二极管文章中提到的共阴和共阳。韦东山开发板中led的电路如下:
图源韦东山教学视频
可以看到这里是输出低电平代表on,输出高电平代表off。
驱动名、设备名和设备文件名
这些是我最近不太清晰的概念,搜索后发现一篇文章对这些概念解释的比较清晰,原文链接如下:
简单概括一下:
驱动名是.ko文件的名字,装载后可以通过lsmod命令查看到这个名字。
设备名可以通过cat /proc/devices命令查看,且一般和主设备号在一起,如图:
236是主设备号,100ask_hello是设备名
设备文件名是使用open函数时打开的文件,形如“/dev/xxx”,我们之前确认驱动是否安装成功就是查看这个。
本来还想分析韦东山的代码的,原来的标题是《韦东山初版led驱动树莓派4B移植与分析》,看了眼左下角的字数,不知不觉又写了这么多,于是默默把标题改了。想想也确实没必要,要捋清楚这些代码的来龙去脉,要单独写一篇长文才行,等有时间再来更新这个吧。
注:这篇文章参考并引用了韦东山老师的部分教学资料和网上其他博主的内容,这些内容仅用于科普,若构成侵权本人将第一时间进行删改。
觉得文章有帮助的朋友可以点赞收藏转发支持一下,感谢!
当然,也欢迎来我其他的内容平台逛逛,B站、知乎、公众号同名。