正点原子嵌入式linux驱动开发——设备树下LED驱动

经过对设备树的学习以及驱动开发中常用的OF函数介绍,本篇笔记将之前的新字符设备驱动的LED,换成设备树形式

设备树LED驱动原理

在之前的新字符设备驱动实验中,直接在驱动文件newchrled.c中定义有关寄存器物理地址,然后使用io_remap函数进行内存映射,得到对应的虚拟地址,最后操作寄存器对应的虚拟地址完成对GPIO的初始化。现在使用设备树来向Linux内核传递相关的寄存器物理地址,Linux驱动文件使用上一篇笔记中重点学习讲解的OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关的IO。本章实验重点内容如下:

  1. 在stm32mp157d-atk.dts文件中创建相应节点设备。
  2. 编写驱动程序,获取设备树种相关属性值。
  3. 使用获取的有关属性值来初始化LED使用的GPIO。

硬件原理图

这个就是之前的LED灯的原理图,没有区别,这里不再展示。

实验程序

修改设备树文件

在根节点"/"下创建一个名为"stm32mp1_led"的子节点,打开stm32mp157d-atk.dts文件,在根节点"/"最后面输入如下所示内容:

c 复制代码
示例代码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"节点的话可以重点检查下面两点:

  1. 检查设备树修改是否成功,也就是stm32mp1_led节点是否为根节点"/"的子节点。
  2. 检查是否使用新的设备树启动的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的相关寄存器。

相关推荐
俺不要写代码5 分钟前
Linux上一个简单的echo服务器搭建
linux·运维·服务器
努力努力再努力wz5 分钟前
【MySQL入门系列】:不只是建表:MySQL 表约束与 DDL 执行机制全解析
android·linux·服务器·数据结构·数据库·c++·mysql
凯尔萨厮6 分钟前
Maven学习笔记
笔记·学习·maven
OSwich7 分钟前
【 Godot 4 学习笔记】运算符
笔记·学习·godot
【ql君】qlexcel15 分钟前
可跑在STM32上的EtherCAT主机协议栈
stm32·soem·ethercat·igh·协议栈
bukeyiwanshui16 分钟前
20260416 DHCP以及DNS
linux·网络
Hammer_Hans20 分钟前
DFT笔记42
笔记
IMPYLH20 分钟前
Linux 的 printf 命令
linux·运维·服务器·bash
SuperEugene23 分钟前
Vue3 前端配置驱动避坑:配置冗余、渲染性能、扩展性问题解决|配置驱动开发实战篇
前端·javascript·vue.js·驱动开发·前端框架
艾莉丝努力练剑25 分钟前
【Linux加餐】mmap文件映射
linux·运维·服务器·c语言·c++·学习