【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十三章 设备树下的platform驱动
i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、
【公众号】迅为电子
【粉丝群】258811263
第五十三章 设备树下的platform驱动
本章导读
本章节我们来学习设备树下的platform驱动,之前我们学习了linux下的平台总线模型但是我们是使用传统的方法进行学习。什么是传统的方法呢?传统的方法就是把我们的驱动分为两个部分,第一部分是device.c,第二部分是driver.c,当device.c和driver.c匹配成功以后,进入probe函数后就可以获取硬件资源了,然后可以注册杂项设备,注册字符设备。
我们现在使用的是设备树,设备树相当于之前学习的device.c。之前传统的方法是使用“name”进行匹配的,我们使用设备树要怎么和我们的driver进行匹配呢?之前讲设备树语法的时候我们学习过compitable属性,那么这个compitable属性就是和driver.c匹配的。我们打开设备树文件,在设备树的根节点下,也有一个compitable属性,比如说内核在iTOP-3399开发板上面可以运行,那么这个内核放到4412开发板,imx6Q开发板上可以运行吗?答案肯定是不可以的,因为内核运行之前会进行一次匹配,内核在运行之前会检查下这个板子是否支持运行,那么他是根据根节点下的compitable属性来进行判断的。
53.1章节在前面52章节的基础上修改设备树文件,并查看是否生成设备节点。
53.2章节编写了驱动程序,该程序是设备树下的Platform驱动,匹配成功后在probe函数中获取到硬件资源,映射寄存器物理地址等等。
本章内容对应视频讲解链接(在线观看):
设备树下的platform总线 → https://www.bilibili.com/video/BV1Vy4y1B7ta?p=28
程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\011-设备树下的platform驱动”路径下。
53.1 设备树下的Platform设备
Linux 系统中 platform 平台框架包括总线、设备和驱动,其中总线不用我们去操心,Linux 内核中会自动管理,我们只需要关心设备和驱动如何实现。在不支持设备树的内核中,我们需要分别实现 platform_device和 platform_driver,其中 platform_device 是在平台文件中实现的。在支持设备树的内核中,我们就不用实现 platform_device 了,而是在设备树文件中添加设备信息。下面看一下在设备树文件中添加设备信息。
在之前关于设备树语法的章节中,我们学习了如何在根节点“/”下去添加一个设备节点信息。其中最重要的就是 compatible 属性值,compatible 属性使用来和驱动进行匹配的。下面是本实验用到的设备的设备节点:


在编写驱动以前,有一个地方需要注意一下,我们在加载driver.ko之前,一定要在开发板上已经成功地添加了test的节点,你可以在linux系统里面查看到你添加的节点,查看节点方法请参考51.1 查看设备树节点方法章节,添加自定义节点请参考51.2添加自定义节点章节。查看到test节点的comtabile属性的值为test1234,如下图所示:

53.2 实验程序编写
53.2.1 Platform驱动程序
程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\011-设备树下的platform驱动\001”路径下。
我们在Ubuntu的/home/topeet/imx8mm/11/001目录下新建driver.c文件,修改代码为如下所示
/** @Author: topeet* @Description: 实现设备树下Platform驱动匹配进入probe函数*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>/*** @description: platform 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行* @param {*}pdev : platform 设备* @return {*}0,成功;其他负值,失败*/
int led_probe(struct platform_device *pdev)
{ //匹配成功以后,进入到probe函数printk("led_probe\n");return 0;
}
int led_remove(struct platform_device *pdev)
{printk("led_remove\n");return 0;
}
const struct platform_device_id led_idtable = {.name = "led_test",
};
const struct of_device_id of_match_table_test[] = {{.compatible = "test1234"},{},
};
struct platform_driver led_driver = {//3. 在led_driver结构体中完成了led_probe和led_remove.probe = led_probe,.remove = led_remove,.driver = {.owner = THIS_MODULE,.name = "led_test",.of_match_table = of_match_table_test //接下来我们改一下驱动,让他来匹配设备树里面test的节点},.id_table = &led_idtable //4 .id_table的优先级要比driver.name的优先级要高,优先与.id_table进行匹配
};static int led_driver_init(void)
{// 1.我们看驱动文件要从init函数开始看int ret = 0;//2. 在init函数里面注册了platform_driverret = platform_driver_register(&led_driver);if (ret < 0){printk("platform_driver_register error \n");}printk("platform_driver_register ok \n");return 0;
}static void led_driver_exit(void)
{platform_driver_unregister(&led_driver);printk("goodbye! \n");
}
module_init(led_driver_init);
module_exit(led_driver_exit);MODULE_LICENSE("GPL");
保存driver.c文件,编译driver.c为驱动模块,如下图所示:

驱动编译完,我们通过nfs将编译好的驱动程序加载模块。我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示:
insmod driver.ko

如上图所示,已经匹配成功进入到probe函数中。如果没有进入probe函数,可能出现匹配不成功的原因是1 device或者设备树根本没有加到我们系统里面2 名字不一样导致匹配不成功。
53.2.2 获取资源
程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\011-设备树下的platform驱动\002”路径下。
我们进入了probe函数,可以在probe函数中获取资源,如下所示:
int led_probe(struct platform_device *pdev)
{ //匹配成功以后,进入到probe函数printk("led_probe\n");
/*********************方法一:直接获取节点**************************/printk("node name is %s\n",pdev->dev.of_node->name);return 0;
}
编译驱动,然后加载驱动后,如下图所示:

如上图所示,加载驱动以后,设备树上的节点和驱动程序匹配成功,进入了probe函数,并打印了节点的名字。
我们也可以用第52章学习过的of操作函数来获取我们的设备资源,修改driver.c为如下所示:
int led_probe(struct platform_device *pdev)
{ //匹配成功以后,进入到probe函数printk("led_probe\n");/*********************方法一:直接获取节点**************************/// printk("node name is %s\n",pdev->dev.of_node->name);/*********************方法二:通过函数获取硬件资源**************************///获得设备节点test_device_node = of_find_node_by_path("/test"); //获得设备节点if (test_device_node == NULL){printk("of_find_node_by_path is error \n");return -1;}//获取reg属性ret = of_property_read_u32_array(pdev->dev.of_node, "reg", out_values, 4);if (ret < 0){printk("of_property_read_u32_array is error \n");return -1;}printk("out_values[0] is 0x%08x\n", out_values[0]);printk("out_values[1] is 0x%08x\n", out_values[1]);printk("out_values[2] is 0x%08x\n", out_values[2]);printk("out_values[3] is 0x%08x\n", out_values[3]);return 0;
}
编译驱动,然后加载驱动后,如下图所示:

如上图所示,我们已经成功地获得设备树里面的reg属性。
53.2.3 获取节点属性
程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\011-设备树下的platform驱动\003”路径下。
我们修改driver.c如下所示:
int led_probe(struct platform_device *pdev)
{ //匹配成功以后,进入到probe函数printk("led_probe\n");/*********************方法一:直接获取节点**************************/// printk("node name is %s\n",pdev->dev.of_node->name);/*********************方法二:通过函数获取硬件资源**************************///获得设备节点// test_device_node = of_find_node_by_path("/test"); //获得设备节点// if (test_device_node == NULL)// {// printk("of_find_node_by_path is error \n");// return -1;// }//获取reg属性ret = of_property_read_u32_array(pdev->dev.of_node, "reg", out_values, 4);if (ret < 0){printk("of_property_read_u32_array is error \n");return -1;}printk("out_values[0] is 0x%08x\n", out_values[0]);printk("out_values[1] is 0x%08x\n", out_values[1]);printk("out_values[2] is 0x%08x\n", out_values[2]);printk("out_values[3] is 0x%08x\n", out_values[3]);return 0;
}
编译驱动,然后加载驱动后,如下图所示:

如上图所示,可以直接通过节点获取到reg属性的值。
53.2.4 映射物理地址
程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\011-设备树下的platform驱动\004”路径下。
现在我们已经拿到了寄存器的地址,接下来可以注册杂项设备或者字符设备,我们先将获取到的物理地址映射为虚拟地址,修改driver.c代码如下:
int led_probe(struct platform_device *pdev)
{ //匹配成功以后,进入到probe函数printk("led_probe\n");/*********************方法一:直接获取节点**************************/// printk("node name is %s\n",pdev->dev.of_node->name);/*********************方法二:通过函数获取硬件资源**************************///获得设备节点// test_device_node = of_find_node_by_path("/test"); //获得设备节点// if (test_device_node == NULL)// {// printk("of_find_node_by_path is error \n");// return -1;// }//获取reg属性ret = of_property_read_u32_array(pdev->dev.of_node, "reg", out_values, 4);if (ret < 0){printk("of_property_read_u32_array is error \n");return -1;}printk("out_values[0] is 0x%08x\n", out_values[0]);printk("out_values[1] is 0x%08x\n", out_values[1]);printk("out_values[2] is 0x%08x\n", out_values[2]);printk("out_values[3] is 0x%08x\n", out_values[3]);//映射GPIO资源vir_gpio1_io13 = of_iomap(pdev->dev.of_node, 0);if (vir_gpio1_io13 == NULL){printk("GPIO1_IO13 iomap is error \n");return EBUSY;}printk("GPIO1_IO13 iomap is ok \n");vir_gpio1_io13_gdir = of_iomap(pdev->dev.of_node, 0);if (vir_gpio1_io13_gdir == NULL){printk("GPIO1_IO13_GDIR iomap is error \n");return EBUSY;}printk("GPIO1_IO13_GDIR iomap is ok \n");return 0;
}
编译驱动,然后加载驱动后,如下图所示:

如上图所示,物理地址已经映射为虚拟地址,接下来可以注册字符设备和杂项设备,流程和我们前面学习到的内容是一模一样的。
相关文章:
【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十三章 设备树下的platform驱动
i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…...
Java正则表达式判断有无特殊字符
//^代表否定,匹配除了数字、字母、下划线的特殊字符。 private static final String SPECIAL_CHAR_PATTERN "[^a-zA-Z0-9_]"; Pattern pattern Pattern.compile(SPECIAL_CHAR_PATTERN); Matcher matcher pattern.matcher(userAccount); // 如果 find(…...
使用Java和Spring AMQP构建消息驱动应用
使用Java和Spring AMQP构建消息驱动应用 大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿! 消息驱动应用程序在现代系统架构中扮演着重要角色,特别是在处理高并发和异步任务时。Spring AMQ…...
【NLP】提升文本生成多样性的实用方法
比如用T5模型,训练数据是inputText-outputText格式,预测时do_sample=False # 预测代码from transformers import TFAutoModelForSeq2SeqLM from transformers import AutoTokenizercheckpoint_local = "./path/" tokenizer = AutoTokenizer.from_pretrained(check…...
鸿蒙(HarmonyOS)下拉选择控件
一、操作环境 操作系统: Windows 11 专业版、IDE:DevEco Studio 3.1.1 Release、SDK:HarmonyOS 3.1.0(API 9) 二、效果图 三、代码 SelectPVComponent.ets Component export default struct SelectPVComponent {Link selection: SelectOption[]priva…...
Java类加载器实现机制详细笔记
1. 类加载器的基本概念 类加载器(ClassLoader):在Java中,类加载器负责将Java类动态加载到JVM中。它是实现动态类加载机制的核心组件,对于开发复杂应用程序(如插件系统、模块化设计等)至关重要。…...
Git之repo sync -l与repo forall -c git checkout用法区别(四十九)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…...
【公式解释】《系统论》《控制论》《信息论》的共同重构:探索核心公式与深度解析
《系统论》《控制论》《信息论》的共同重构:探索核心公式与深度解析 关键词:系统论、控制论、信息论、状态空间方程、系统矩阵。 Keywords: System theory, Control theory, Information theory, State-space equations, System matrices. 核心公式与三论共同之处 在系统…...
电脑格式化好还是恢复出厂设置好?
电脑格式化好还是恢复出厂设置好?使用电脑的过程中,系统问题、病毒感染、性能下降等原因可能会导致我们考虑对电脑进行大规模的清理和恢复操作。本文将详细探讨电脑格式化和恢复出厂设置的区别、优缺点,以及不同场景选择哪种方法合适。 选择电…...
使用 Windows 应用程序 SDK 构建下一代应用程序
微软面临的最大问题之一是如何让 Windows 再次成为吸引开发者的平台。无论用户使用什么设备和操作系统,都可以很容易地将 Web 前端放在支持桌面和移动用户的云原生应用程序上。 我们处在一个奇怪的境地,唯一能利用最新 PC 硬件的应用程序是 Office、Phot…...
可消费的媒体类型和可生成的媒体类型
可消费的媒体类型和可生成的媒体类型 在 Spring MVC 中,“可消费的媒体类型”和“可生成的媒体类型”是两个重要的概念,用于控制控制器方法处理和返回的内容类型。它们分别通过 consumes 和 produces 属性来指定。下面是它们的详细区别: 可…...
C++中指针与迭代器的区别
C中的迭代器和指针都是用于访问和操作内存中的数据结构的机制,但它们在使用方式和功能上有一些关键的区别。 #mermaid-svg-23bevhEih3Ch4ucl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-23bevhEih3Ch…...
若依框架 : 生成代码
6.生成代码 6.1.配置生成设置 ruoyi-generator -> src -> main -> resources -> generator.yml 由于 案例中 表都有 前缀 为 tta_ , 这里设置去掉 6.2.生成代码 6.2.1.导入数据库中的表 6.2.2.修改设置 6.2.2.1.设置生成信息 点击 编辑 -> 生成信息 特别…...
RTMP协议解析
RTMP(Real Time Message Protocol)是一种由Adobe公司提出的应用层协议,主要用于实时音视频数据的传输。RTMP协议的主要有以下特点: 1. 多路复用:RTMP允许多个音视频数据流在单个TCP连接上进行传输。 2. 分包传输&…...
禁忌搜索算法(Tabu Search,TS)及其Python和MATLAB实现
禁忌搜索算法是一种现代启发式搜索方案,主要用于解决组合优化问题。该算法由George F. Lugeral于1986年首次提出,旨在增强局部搜索算法的性能,避免其陷入局部最优解。禁忌搜索利用一个称为“禁忌表”的数据结构,记住最近访问的解决…...
Meta发布Llama 3.1 405B模型:开源与闭源模型之争的新篇章
引言 在人工智能领域,开源与闭源模型之争一直是热点话题。近日,Meta发布了最新的Llama 3.1 405B模型,以其强大的性能和庞大的参数规模,成为了开源模型中的佼佼者。本文将详细介绍Llama 3.1 405B模型的性能、功能及其在开源领域的…...
Linux网络协议深度解析:从IP到TCP/IP堆栈
Linux网络协议深度解析是一个复杂而详细的主题,它涵盖了从基本的数据包传输到复杂的协议交互。以下是对"Linux网络协议深度解析:从IP到TCP/IP堆栈"这一主题的简要解析: IP协议(Internet Protocol) •作用:…...
AWS DMS MySQL为源端,如何在更改分区的时候避免报错
问题描述: 文档[1]中描述MySQL compatible Databases作为DMS任务的源端,不支持MySQL 分区表的 DDL 更改。 在源端MySQL进行分区添加时,日志里会出现如下报错: [SOURCE_CAPTURE ]W: Cannot change partition in table members…...
Java从基础到高级特性及应用
Java,作为一门历史悠久且广泛应用的编程语言,自1995年问世以来,便以其跨平台性、面向对象、自动内存管理等特点,在软件开发领域占据了举足轻重的地位。从桌面应用到企业级系统,从移动开发到云计算服务,Java…...
JavaScript(17)——事件监听
什么是事件? 事件是在编程时系统内发生的动作或发生的事情,比如用户在网页上单击一个按钮 什么是事件监听? 就是让程序检测是否有事件产生,一旦有事件触发,就立刻调用一个函数做出响应,也称为绑定事件或…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10pip3.10) 一:前言二:安装编译依赖二:安装Python3.10三:安装PIP3.10四:安装Paddlepaddle基础框架4.1…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
