经过对设备树的学习以及驱动开发中常用的OF函数介绍,本篇笔记将之前的新字符设备驱动的LED,换成设备树形式。
设备树LED驱动原理
在之前的新字符设备驱动实验中,直接在驱动文件newchrled.c中定义有关寄存器物理地址,然后使用io_remap函数进行内存映射,得到对应的虚拟地址,最后操作寄存器对应的虚拟地址完成对GPIO的初始化。现在使用设备树来向Linux内核传递相关的寄存器物理地址,Linux驱动文件使用上一篇笔记中重点学习讲解的OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关的IO。本章实验重点内容如下:
- 在stm32mp157d-atk.dts文件中创建相应节点设备。
- 编写驱动程序,获取设备树种相关属性值。
- 使用获取的有关属性值来初始化LED使用的GPIO。
硬件原理图
这个就是之前的LED灯的原理图,没有区别,这里不再展示。
实验程序
修改设备树文件
在根节点“/”下创建一个名为“stm32mp1_led”的子节点,打开stm32mp157d-atk.dts文件,在根节点“/”最后面输入如下所示内容:
示例代码24.3.1.1 stm32mp1_led节点
1 stm32mp1_led {
2 compatible = "atkstm32mp1-led";
3 status = "okay";
4 reg = <0X50000A28 0X04 /* RCC_MP_AHB4ENSETR */
5 0X5000A000 0X04 /* GPIOI_MODER */
6 0X5000A004 0X04 /* GPIOI_OTYPER */
7 0X5000A008 0X04 /* GPIOI_OSPEEDR */
8 0X5000A00C 0X04 /* GPIOI_PUPDR */
9 0X5000A018 0X04 >; /* GPIOI_BSRR */
10 };
第2行,属性compatible设置stm32mp1_led节点兼容为“atkstm32mp1-led”。
第3行,属性status设置状态为“okay”。
第4-9行,reg属性,非常重要!reg属性设置了驱动里面所要使用的寄存器物理地址,比如第4行的“0X50000A28 0X04”表示STM32MP1的RCC_MP_AHB4ENSETR寄存器,其中寄存器地址为0X50000A28,长度为4个字节。
设备树修改完成以后输入如下命令重新编译一下
stm32mp157d-atk.dts:
make dtbs |
编译完成后得到stm32mp157d-atk.dtb,使用新的stm32mp157d-atk.dtb启动Linux内核。Linux启动成功以后进入到/proc/device-tree/目录中查看是否有“stm32mp1_led”这个节点,结果如下图所示:
如果没有“stm32mp1_led”节点的话可以重点检查下面两点:
- 检查设备树修改是否成功,也就是stm32mp1_led节点是否为根节点“/”的子节点。
- 检查是否使用新的设备树启动的Linux内核。
可以进入到stm32mp1_led的目录中,使用cat查看各个属性值。
LED灯驱动编写
在之前已经编写好的LED驱动基础上,在dtsled_dev结构体中,加上struct device_node* nd的设备节点;然后需要在驱动的led_init函数中,加上获取设备树属性的操作:主要是通过dtsled.nd(之前的dtsled_dev结构体)来接住of_find_node_by_path(“/stm32mp1_led”)的返回值,从而获得LED节点;而后通过property*的proper来接住of_find_property返回值,获取compatible属性;int ret来接住of_property_read_string返回值,获取status属性值;再用ret接住of_property_read_u32_array返回值,获取reg属性值,存放到regdata数组中;以上属性值全部存到了dtsled.nd之中就获得了LED的相关寄存器,之后通过of_iomap来获取dtsled.nd的reg属性以及内存映射,从而获得了要操作的寄存器地址。
编写测试APP
这个可以直接用之前的ledApp.c文件。
编译驱动程序和测试APP
编译驱动
在Makefile文件中,将obj-m的值改为dtsled.o即可,然后通过“make -j8”就可以编译,最终得到dtsled.ko文件。
编译测试APP
可以通过如下命令编译:
arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp |
运行测试
将之前编译得到的dtsled.ko和ledApp拷贝到rootfs/lib/modules/5.4.31目录中,然后重启开发板,进入/lib/modules/5.4.31目录,输入如下命令加载dtsled.ko:
depmod //第一次加载驱动的时候需要运行此命令 modprobe dtsled //加载驱动 |
加载成功后会出现如下信息:
从上图中可以看出,stm32mp1_led这个节点找到了,并且compatible属性值为“atkstm32mp1-led”,status属性值为“okay”,reg属性的值为“0X50000A28 0X4 0X5000A000 0X4 0X5000A004 0X4 0X5000A008 0X4 0X5000A00C 0X4 0X5000A018 0X4”,这些都和设置的设备树一致。
加载成功后可以通过如下命令打开和关闭LED:
./ledApp /dev/dtsled 1 //打开LED灯 ./ledApp /dev/dtsled 0 //关闭LED灯 |
可以通过如下命令卸载驱动:
rmmod dtsled.ko |
总结
这一篇笔记中,主要的区别就是在stm32mp157d-atk.dts文件中,在"/"根节点下添加了LED的设备树节点,然后在驱动程序中增加了对应了结构体成员device_node* nd,然后在led_init中通过OF函数获取属性值,主要是reg属性值来读取LED的相关寄存器。