今天无聊,掏出我82年产的microzed玩一玩。玩啥好呢,要不点个灯吧。于是,三下五除二,通过linux sys接口以及echo,很快就点亮了。无聊按了一下复位按键,发现系统重新先加载后,灯又不亮了。这不能忍,于是继续深入,发现导出的gpio都没了。
linux sys 接口,对GPIO实行的export操作,是临时性质的,因此每次重启系统,都需要重新进行export。通常可以通过制作脚本,在系统加载后或者用户登录后自动export,但今天,我想借这个事情,聊一条怎么用linux设备树解决这个问题。
相关的基础知识,可以参考我的博客
[Linux实战] Linux设备树原理与应用详解
[实战]Zynq设备树详细教程
[实战] 深入解析Petalinux下Zynq7000设备树开发:从理论到实战
zynq的用户自定义设备树,在{petalinux工程}/project-spec/meta-user/recipes-bsp/device-tree/files/ 下,那个叫 system-user.dtsi 就是, 用文本编辑工具打开它,会发现它是这样的:
/include/ "system-conf.dtsi"
/ {
};
这是一个空文件,里面没有任何节点,用户自定义的节点,只需要添加在这里面就行。
对于gpio自动导出,只需要新建一个gpio-export节点,并设置好必要属性,具体内容如下
/include/ "system-conf.dtsi"
/ {
gpio-export {
compatible = "gpio-export";
#gpio-cells = <2>;
gpio47 {
gpio-export,name = "custom_gpio47";
gpio-export,output = <0>;
gpios = <&gpio0 47 0>;
};
};
};
作用:创建一个名为gpio-export的子节点,用于管理GPIO导出功能。
作用:绑定Linux内核的gpio-export驱动,该驱动负责将GPIO导出到用户空间。
验证:可通过内核源码查找COMPATIBLE_STR匹配(如drivers/gpio/gpio-export.c)。
作用:声明每个GPIO描述需要2个参数(控制器、引脚号、激活电平)。
背景:与GPIO控制器的#gpio-cells定义一致,确保语法匹配。
作用:定义GPIO 47的导出配置,子节点名称通常与GPIO编号对应。
作用:指定用户空间看到的GPIO名称(在/sys/class/gpio/下生成同名目录)。
用户空间路径:
/sys/class/gpio/custom_gpio47/
作用:初始化方向为输入(0=输入,1=输出)。
扩展:若需初始化为输出,需添加gpio-export,init = <0/1>;设置默认电平。
作用:关联硬件GPIO控制器和引脚,格式为:
<&控制器 引脚号 激活电平>
参数详解:
改完之后保存,petalinux-build
在petalinux工程目录下运行
petalinux-build
[INFO] Sourcing buildtools
[INFO] Building project
[INFO] Sourcing build environment
[INFO] Generating workspace directory
INFO: bitbake petalinux-image-minimal
NOTE: Started PRServer with DBfile: /D/study/zynq7010_project/build/cache/prserv.sqlite3, Address: 127.0.0.1:43583, PID: 73789
Loading cache: 100% |#############################################################| Time: 0:00:01
Loaded 6495 entries from dependency cache.
Parsing recipes: 100% |#############################################################| Time: 0:00:02
Parsing of 4461 .bb files complete (4459 cached, 2 parsed). 6497 targets, 627 skipped, 1 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies
Initialising tasks: 100% |#############################################################| Time: 0:00:08
Checking sstate mirror object availability: 100% |#############################################################| Time: 0:00:11
Sstate summary: Wanted 478 Local 9 Network 304 Missed 165 Current 1106 (65% match, 89% complete)
Removing 19 stale sstate objects for arch zynq_generic: 100% |#############################################################| Time: 0:00:00
NOTE: Executing Tasks
NOTE: Tasks Summary: Attempted 4232 tasks of which 4178 didn't need to be rerun and all succeeded.
INFO: Successfully copied built images to tftp dir:
/tftpboot/microZed
[INFO] Successfully built project
然后打包,在工程目录下运行
petalinux-package --boot --fsbl --fpga --u-boot --force
然后将boot.bin、image.ub、boot.csr三个文件拷贝到sd卡fat分区,把rootfs.tar.gz文件解压缩要sd卡ext分区。插入microzed,重启。
首先启动之前的版本
zynq7010:~$ cd /sys/class/gpio/
zynq7010:/sys/class/gpio$ ls
export gpiochip906 unexport
可见目录下没有任何导出的gpio,手动导出:
为了方便操作,先切换root:
zynq7010:/sys/class/gpio$ sudo -i
Password:
root@zynq7010:~#
再进行导出
root@zynq7010:~# cd /sys/class/gpio/
root@zynq7010:/sys/class/gpio# ls
export gpiochip906 unexport
root@zynq7010:/sys/class/gpio# echo 953 > export
root@zynq7010:/sys/class/gpio# ls
export gpio953 gpiochip906 unexport
导出成功,进行属性配置和值操作,gpio操作成功
root@zynq7010:/sys/class/gpio# echo out > gpio953/direction
root@zynq7010:/sys/class/gpio#
root@zynq7010:/sys/class/gpio#
root@zynq7010:/sys/class/gpio# echo 1 > gpio953/value
root@zynq7010:/sys/class/gpio# cat gpio953/value
1
root@zynq7010:/sys/class/gpio# echo 0 > gpio953/value
root@zynq7010:/sys/class/gpio# cat gpio953/value
修改属性为GPO,同时修改输出值,操作都成功了。
复位板卡,重新启动,再次查看GPIO情况
zynq7010:~$ cd /sys/class/gpio/
zynq7010:/sys/class/gpio$ ls
export gpiochip906 unexport
上次导出的已经没有了,export是一个临时操作,每次重启后都会丢失,需要重新export。
好了,上修改了设备树后的版本,看看什么情况。
zynq7010:~$ cd /sys/class/gpio/
zynq7010:/sys/class/gpio$ ls
export gpiochip906 unexport
哦和,还是一样的,没有被导出。是什么原因呢????
经过仔细研究linux设备操作,搞清楚了问题的根本原因,他在于设备树只是申明有这么一个设备,但是是不是会使用,取决于驱动,所以现在要想实现自动export还得自己写一段驱动才行。这个事情先留后面了。在这里借用linux本身就已经有驱动的Led设备。我们通过设备树,把这个设备定义成LED设备,这样linux加载时,就会自动export了。
设备树改动如下:
/include/ "system-conf.dtsi"
/ {
my_gpios {
compatible = "gpio-leds"; // 使用标准 gpio-leds 驱动(自动导出)
status = "okay";
my_gpio0 {
label = "my-gpio0";
gpios = <&gpio0 47 0>; // EMIO GPIO 0 (MIO 0-53, EMIO 54-117)
default-state = "on"; // 初始状态
};
};
};
保存,编译,打包,拷贝到SD卡,启动开发版。进去一查看
zynq7010:~$ cd /sys/class/gpio/
zynq7010:/sys/class/gpio$ ls
export gpiochip906 unexport
傻了,怎么还没有不应该阿。。。。
突然,灵光一现,设备树把它指定为led设备了,就算导出了,也不在这里了,因该会有一个led目录,于是仔细查看果然:
zynq7010:~$ ls /sys/class/leds/
mmc0:: my-gpio0
这个my-gpio0不就是我在设备树中定义的标签吗?原来他躲在这里。正好我这个gpio就是用来控制板子上Led,我低头一看板子,果然多亮了一个灯,开心。
继续进到目录,看到
root@zynq7010:/sys/class/leds/my-gpio0# ls
brightness device max_brightness power subsystem trigger uevent
root@zynq7010:/sys/class/leds/my-gpio0#
brightness 应该就是控制指令了,于是:
root@zynq7010:/sys/class/leds/my-gpio0# cat brightness
1
root@zynq7010:/sys/class/leds/my-gpio0#
现在灯亮它是1,改成0应该灭了,试一下:
root@zynq7010:/sys/class/leds/my-gpio0# echo 0 > brightness
root@zynq7010:/sys/class/leds/my-gpio0# echo 1 > brightness
root@zynq7010:/sys/class/leds/my-gpio0# echo 0 > brightness
果然,灭-亮-灭,就是它了。
通过本文,应该对设备树、驱动、linux硬件驱动框架,应该能有个感性的认知了。
本文的实验至少说明: