您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   模型库  
会员   
   
基于 UML 和EA进行分析设计
6月23-24日 北京+线上
人工智能、机器学习 TensorFlow
6月30日-7月1日 直播
图数据库与知识图谱
8月21日-22日 北京+线上
   
 
 订阅
实战演练:设备树(DTS)语法详解 + LED控制实战

 
作者:往事敬秋风
  92  次浏览      4 次
 2025-6-18
 
编辑推荐:
本文主要介绍了 设备树(DTS)语法详解 + LED控制实战相关内容。希望对您的学习有所帮助。
本文来自于微信公众号深度Linux,由火龙果软件Linda编辑、推荐。

在嵌入式开发领域,设备树(Device Tree)扮演着极为关键的角色。它如同一份精准的硬件说明书,让操作系统能够清晰识别并高效管理硬件资源。当我们着手为自定义硬件编写设备树时,就如同为新成员量身定制一套专属 “档案”,确保其能无缝融入系统 “大家庭”。

想象一下,你精心设计了一款独特的硬件,满心期待它在系统中发挥作用。但如果没有正确编写设备树,操作系统很可能对其 “视而不见”,让你的心血难以充分施展。那么,如何才能为自定义硬件编写正确的设备树呢?

接下来,我们将通过一系列实战演练,带你逐步攻克这一技术要点,从了解硬件配置的基础信息,到精准修改设备树源文件,再到生成可被系统读取的二进制文件等,每一步都为你详细拆解,助你在自定义硬件开发之路上稳步前行 。

一、设备树初印象:为何而生?

在嵌入式系统的广袤天地中,设备树(Device Tree)宛如一座桥梁,横跨在硬件与软件之间,承担着至关重要的沟通使命。在设备树出现之前,嵌入式系统的开发可没少让开发者们头疼。就拿 ARM 架构来说,不同开发板的硬件信息就像一团乱麻,紧紧地与内核代码交织在一起。想象一下,每更换一次开发板,那密密麻麻的内核代码就得大改特改,移植工作不仅繁琐,还极易出错,维护成本更是高得吓人。

为了化解这些难题,设备树应运而生。它最早在 PowerPC 架构中崭露头角,随后便在 ARM、MIPS 等多种架构中广泛应用,迅速成为嵌入式开发的得力助手。设备树的核心使命,就是将硬件描述从内核代码中剥离出来,实现两者的解耦。如此一来,内核变得更加通用,开发板相关的特定信息则由设备树文件专门负责描述。当我们要将内核移植到新的开发板时,只需对设备树文件进行修改,这极大地降低了移植的难度和工作量,就像是给复杂的开发工作找到了一条捷径。

举个例子,在一个基于 ARM 架构的智能家居控制系统开发中,以往若要适配不同厂家生产的开发板,每个开发板都有独特的硬件配置,如不同的 GPIO 引脚分配、外设接口等。在没有设备树时,针对每一款开发板,内核代码中都要硬编码大量的硬件相关信息,这导致内核代码臃肿不堪,且维护极为困难。一旦硬件稍有变动,例如更换了一个不同型号的传感器,与之相连的引脚发生变化,就需要在冗长的内核代码中四处寻找并修改相关配置,不仅容易遗漏,而且修改过程繁琐,极易引入新的错误。

有了设备树后,情况就大为不同。硬件相关信息被清晰地描述在设备树文件中。若要适配新的开发板,或者更改硬件配置,只需在设备树文件中相应节点处修改属性值即可。比如,要更改传感器连接的 GPIO 引脚,只需在设备树中找到对应的传感器节点,修改其 GPIO 属性值,而无需对内核代码进行大规模改动。这使得开发过程更加灵活高效,同时也提高了代码的可维护性和可移植性 。

1.1设备树在ARM架构的引入

在之前使用 S3C2440 开发板移植 Linux 3.4.2 内核时,修改了很多关于 c 文件去适配开发板,和开发板相关的文件放在arch/arm/mxch-xxx目录下,因此 linux 内核 arm 架构下添加了很多开发板的适配文件:

这些 c 文件仅仅用来适配某款开发板,对于 Linux 内核来说并没有提交什么新功能,但是每适配一款新的开发板就需要一堆文件,导致 Linux 内核越来越臃肿:

终于 Linus 忍不住天天 merge 这些鬼东西,向 arm 社区发出了一封邮件,第一句话就足矣表现不满:"This whole ARM thing is a f*cking pain in the ass"。

因此,Arm 社区开始引入之前 powerPC 架构就采用的设备树,将描述这些板级信息的文件与 Linux 内核代码分离,Linux 4.x 版本几乎都支持设备树,所有开发板的设备树文件统一放在arch/arm/boot/dts目录中。

1.2什么是设备树

设备树全称 Device Tree,是一种数据结构,用来描述板级设备信息,比如 CPU 数量、外设基地址、总线设备等,如图:

DTS、DTB、DTC三种文件的区别

DTS 是设备树源码文件

DTB 是将DTS 编译以后得到的二进制文件

DTC工具是将dts文件变成编译成dtb的工具,就像.c文件变成成.o需要用到gcc编译器一样。

1.3 设备树编译

①简单粗暴,编译内核

make

②编译全部设备树文件

make dtbs

③编译指定的设备树文件

make <xxx.dtb>

二、设备树语法

2.1设备树相关术语全解析

在深入探索设备树的世界之前,先来认识一下那些频繁出现的术语,它们就像是开启设备树大门的钥匙。

DT(Device Tree,设备树):这是一种用于描述计算机系统硬件布局的数据结构,它将系统中的各类硬件组件及其连接关系,以层次化的树状结构呈现出来。可以把它想象成是硬件的 “户口簿”,详细记录着每个硬件设备的 “身份信息” 以及它们之间的 “亲属关系” ,为内核提供了清晰的硬件信息,让内核能够有条不紊地初始化和操作硬件设备。

FDT(Flattened Device Tree,扁平设备树):FDT 是设备树的二进制表示形式,就像是设备树的 “压缩包”。它由设备树编译器(dtc)精心打造而成,是 dts 文件编译后的产物。这种紧凑的存储方式,使其在系统启动时能够快速加载和解析,大大提高了系统启动的效率 ,就好比将一本厚厚的书压缩成了一个小巧的文件,携带和读取都更加便捷。

dts(Device Tree Source,设备树源文件):dts 文件是设备树的源文件,以通俗易懂的文本格式编写,是硬件开发人员或系统集成商施展拳脚的舞台。他们通过编写 dts 文件,详细描述系统中的硬件设备及其属性,就像建筑师绘制建筑蓝图一样,为后续的硬件实现和系统开发奠定基础。这个文件就像是设备树的 “初稿”,记录着最原始的硬件描述信息。

dtsi(Device Tree Source Include,设备树源包含文件):dtsi 文件类似于编程中的头文件,是设备树源文件的得力助手。它通常包含一些被多个 dts 文件共享的硬件描述,通过 #include 指令巧妙地融入到具体的 dts 文件中。这样一来,不仅避免了重复代码,还让硬件描述的管理和维护变得更加轻松,就像是一个公共的资源库,各个 dts 文件都可以按需取用其中的内容 。

dtb(Device Tree Blob,设备树二进制文件):dtb 文件是 dts 文件经过编译后生成的二进制文件,包含了设备树的扁平表示形式(FDT)。在系统启动的关键时刻,引导加载程序(如 U-Boot)会将 dtb 文件从存储设备中加载到内存,并恭敬地传递给操作系统内核。内核则依据 dtb 文件中描述的信息,对硬件进行初始化操作,它就像是一份被加密后的硬件说明书,只有内核能够读懂并依据它来配置硬件 。

dtc(Device Tree Compiler,设备树编译器):dtc 是设备树世界中的 “翻译官”,它肩负着将 dts 文件编译为 dtb 文件的重任。不仅如此,在调试和修改设备树时,它还能将 dtb 文件反编译为 dts 文件,为开发者提供了极大的便利。就好比一个语言专家,能够在不同格式的设备树文件之间自由转换 。

这些术语相互协作,共同构建起设备树的生态系统。dts 和 dtsi 文件是设备树的源代码基础,dtc 编译器将它们转换为内核能够识别的 dtb 文件,而 FDT 则是 dtb 文件内部的一种高效存储结构,DT 则是整个硬件描述概念的统称 。

2.2设备树源码藏身何处

在 Linux 内核源码的庞大体系中,设备树源码有着自己专属的 “栖息地”。对于 32 位系统而言,设备树源码通常存放在 “源码 /arch/arm/boot/dts” 目录下;而在 64 位系统中,它们则位于 “源码 /arch/arm64/boot/dts” 目录 。

在这些目录中,你能找到众多以.dts 和.dtsi 为扩展名的文件,它们就像是隐藏在代码海洋中的宝藏,记录着硬件设备的详细信息 。不同架构的设备树文件存放位置和命名规则可能会略有差异,所以在探索时,一定要查阅对应架构的文档或源码,这样才能准确找到所需的设备树文件 。

2.3DTC 工具的神奇用法

DTC 工具作为设备树编译和反编译的利器,掌握它的使用方法至关重要。下面就来看看如何使用 DTC 工具进行设备树的编译和反编译操作 。

(1)编译设备树

基本的编译命令格式为 “dtc -I dts -O dtb -o output_file.dtb input_file.dts”。其中,“dtc” 是调用设备树编译器;“-I dts” 明确指定输入文件的格式为设备树源文件(DTS),就像是告诉编译器要处理的是哪种类型的 “原材料”;“-O dtb” 指定输出文件的格式为设备树二进制文件(DTB),也就是确定了 “成品” 的格式;“-o output_file.dtb” 则指定了输出文件的名称,让编译器知道要把生成的文件保存成什么名字;“input_file.dts” 自然就是指定输入的设备树源文件了 。

例如,我们有一个名为 “my_device.dts” 的设备树源文件,想要将其编译成二进制文件 “my_device.dtb”,就可以在命令行中输入 “dtc -I dts -O dtb -o my_device.dtb my_device.dts”,按下回车键,DTC 工具就会迅速行动,将 dts 文件编译成 dtb 文件 。

(2)反编译设备树

反编译的命令格式为 “dtc -I dtb -O dts -o example.dts example.dtb”。这里的参数含义与编译时类似,只是输入和输出的格式进行了互换。“-I dtb” 表示输入文件是设备树二进制文件(DTB),“-O dts” 表示输出文件为设备树源文件(DTS) 。

假设我们有一个 “example.dtb” 的二进制文件,想要查看其原始的设备树描述内容,就可以使用 “dtc -I dtb -O dts -o example.dts example.dtb” 命令,将 dtb 文件反编译成 dts 文件,方便我们进行查看和修改 。

在实际操作过程中,还可能会用到其他一些参数。比如,“-q” 参数可以让编译过程更加安静,减少不必要的提示信息;“-i” 参数可以添加搜索包含文件的路径,方便在编译时找到所需的 dtsi 文件等 。掌握这些参数的用法,能够让我们更加灵活地使用 DTC 工具,应对各种设备树处理的需求 。

三、深入设备树语法殿堂

3.1节点:设备树的基石

节点是设备树的基本组成单元,就像是树状结构中的一个个 “树枝分叉点” ,每个节点都代表着一个硬件设备或组件。节点的命名遵循特定规则,通常以 “node-name@unit-address” 的形式出现 。其中,“node-name” 是节点的名称,一般由字母、数字和下划线组成,用来描述节点所代表的设备类型或功能,如 “uart” 代表串口设备,“i2c” 代表 I2C 总线设备等 。

“unit-address” 则是设备的单元地址,通常表示设备在内存空间或总线上的地址,像 “0x10000000” 这样的十六进制数,它与设备的 “reg” 属性密切相关 。若节点没有地址相关信息,“@unit-address” 部分可以省略 。

每个节点内部可以包含多个属性和子节点。属性以键值对的形式存在,用于描述节点的各种特性;子节点则进一步细化硬件设备的层次结构 。例如,在描述一个 SPI 控制器时,可能会有如下节点:

spi@12340000 {
    compatible = "fsl,imx6ul-spi";
    reg = <0x12340000 0x1000>;
    interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
    #address-cells = <1>;
    #size-cells = <0>;

    spi_device: spi@0 {
        compatible = "my_spi_device";
        reg = <0>;
        spi-max-frequency = <5000000>;
    };
};

在这个例子中,“spi@12340000” 是 SPI 控制器节点,包含了描述其兼容性、地址、中断等属性 。而 “spi_device: spi@0” 则是 SPI 控制器下的一个子节点,表示连接在该 SPI 总线上的一个设备,有自己的兼容性、设备地址和最大工作频率等属性 。

3.2属性:描述设备的关键

属性是节点的重要组成部分,以 “name = value” 的形式存在,用于详细描述节点所代表设备的各种特性 。属性的值可以是多种数据类型,常见的有以下几种:

32 位无符号整数:用尖括号 “<>” 括起来,如 “interrupts = <17 0xc>”,表示该设备的中断号为 17,触发方式为 0xc(具体含义根据中断控制器的定义) 。

字符串:用双引号 “""” 括起来,如 “compatible ="arm,cortex-a7"”,“compatible” 属性用于驱动与设备的匹配,这里表示该设备与 “arm,cortex-a7” 类型兼容 。

字节数组:用方括号 “[]” 括起来,每个字节用两个十六进制数表示,如 “local-mac-address = [00 00 12 34 56 78]”,用于表示设备的本地 MAC 地址 。

复合类型:可以是多种值的组合,用逗号 “,” 隔开,如 “example = <0xf00f0000 19>, "a strange property format"” 。

在众多属性中,“compatible” 和 “reg” 属性尤为常用 。“compatible” 属性就像是设备的 “身份证”,用于设备与驱动程序的匹配 。内核在寻找设备驱动时,会根据设备节点的 “compatible” 属性值,在驱动列表中查找与之匹配的驱动 。例如,“compatible = "fsl,imx6ul-spi", "spi-gpio"”,表示该设备首先尝试匹配 “fsl,imx6ul-spi” 驱动,若找不到,则尝试匹配 “spi-gpio” 驱动 。

“reg” 属性用于描述设备的寄存器地址范围,其值是一系列的 “address size” 对 。“address” 表示设备寄存器的起始地址,“size” 表示地址范围的大小 。这两个值的表示方式由其父节点的 “#address-cells” 和 “#size-cells” 属性决定 。比如,在下面的例子中:

soc {
    #address-cells = <1>;
    #size-cells = <1>;

    spi@12340000 {
        reg = <0x12340000 0x1000>;
        ...
    };
};

由于 “soc” 节点的 “#address-cells = <1>” 和 “#size-cells = <1>”,所以 “spi@12340000” 节点的 “reg” 属性中,“0x12340000” 用 1 个 32 位整数表示起始地址,“0x1000” 用 1 个 32 位整数表示地址范围大小 。

3.3包含文件:代码复用的利器

在设备树开发中,为了提高代码的复用性和可维护性,常常会使用包含文件(dtsi) 。dtsi 文件类似于 C 语言中的头文件,用于存放被多个 dts 文件共享的设备树描述 。通过使用包含文件,可以避免在多个 dts 文件中重复编写相同的硬件描述代码 。

在 dts 文件中,可以使用 “#include” 指令来引用 dtsi 文件 。例如:

#include "common.dtsi"

这样,“common.dtsi” 文件中的内容就会被包含到当前 dts 文件中 。除了 dtsi 文件,设备树源文件也可以包含标准的 C 头文件(.h) 。这在需要使用一些宏定义或常量时非常方便 。例如,在 dts 文件中可能会包含如下头文件:

#include <dt-bindings/gpio/gpio.h>

通过包含这个头文件,可以在设备树中使用其中定义的 GPIO 相关宏,如 “GPIO_ACTIVE_HIGH”“GPIO_ACTIVE_LOW” 等 。这样不仅提高了代码的可读性,还减少了人为错误 。

3.4节点路径:精准定位节点

在设备树这个庞大的 “家族树” 中,每个节点都有其唯一的路径,就像每个人在家族族谱中都有独特的位置标识一样 。节点路径是从根节点开始,通过各级子节点的名称和地址,以 “/” 分隔组成的 。例如,对于前面提到的 SPI 控制器节点,其路径为 “/soc/spi@12340000” 。

通过节点路径,我们可以在设备树中精准地访问特定节点 。在 Linux 内核中,提供了一系列函数来通过节点路径查找节点 。例如,“of_find_node_by_path (const char *path)” 函数,它接收一个节点路径作为参数,返回对应节点的指针 。假设我们要获取 SPI 控制器节点的信息,就可以使用如下代码:

#include <linux/of.h>
#include <linux/device.h>

struct device_node *spi_node;
spi_node = of_find_node_by_path("/soc/spi@12340000");
if (spi_node) {
    // 在这里可以对找到的节点进行操作,如获取属性等
    of_node_put(spi_node);
}

 

在实际应用中,节点路径常用于设备驱动开发 。驱动程序需要根据设备树中节点的信息来初始化和操作硬件设备,通过节点路径可以快速准确地找到对应的设备节点,从而获取设备的属性和配置信息 。

3.5别名:便捷访问节点

别名就像是设备树节点的 “昵称”,为了更方便地访问设备树中的节点而存在 。通过定义别名,可以使用一个简短的名称来代替冗长的节点路径 。别名的定义通常在设备树的根节点下,使用 “aliases” 属性 。例如:

aliases {
    spi0 = &spi@12340000;
    uart1 = &uart@101f0000;
};

在这个例子中,为 “spi@12340000” 节点定义了别名 “spi0”,为 “uart@101f0000” 节点定义了别名 “uart1” 。这样,在设备树的其他部分,或者在驱动程序中,就可以使用这些别名来代替完整的节点路径 。例如,在驱动程序中,可以使用 “of_find_node_by_phandle (of_parse_phandle (aliases, "spi0", 0))” 来通过别名 “spi0” 找到对应的 SPI 控制器节点 。

别名的使用不仅提高了设备树的可读性,还使代码更加简洁和易于维护 。特别是在大型设备树中,当需要频繁访问某些节点时,使用别名可以大大减少代码中冗长路径的出现,降低出错的概率 。

3.6复合节点与定义引用:复杂关系的处理

在描述复杂的硬件设备关系时,复合节点和定义引用发挥着重要作用 。复合节点是指一个节点可以包含多个子节点,这些子节点共同描述一个复杂的硬件设备或功能 。例如,在描述一个包含多个功能模块的芯片时,可能会有如下复合节点:

chip@10000000 {
    compatible = "my_chip";
    reg = <0x10000000 0x10000>;

    module1: submodule@1000 {
        compatible = "module1_type";
        reg = <0x1000 0x100>;
    };

    module2: submodule@2000 {
        compatible = "module2_type";
        reg = <0x2000 0x200>;
    };
};

 

在这个例子中,“chip@10000000” 是一个复合节点,包含了 “module1” 和 “module2” 两个子节点,分别描述芯片中的两个不同功能模块 。

定义引用则是通过 “&label” 的方式来引用其他节点 。例如,在前面的例子中,如果有另一个节点需要使用 “module1” 节点的信息,可以这样引用:

other_node {
    depends_on = <&module1>;
    // 其他属性和子节点
};

这里的 “depends_on = <&module1>” 表示 “other_node” 节点依赖于 “module1” 节点 。通过这种方式,可以清晰地表达硬件设备之间的依赖关系 。

在实际应用中,复合节点和定义引用常用于描述复杂的硬件系统,如片上系统(SoC) 。SoC 通常包含多个处理器核心、各种外设控制器等,通过复合节点和定义引用,可以准确地描述这些组件之间的层次结构和相互关系,为内核提供完整的硬件信息 。

四、实战:设备树实例深度剖析

4.1 GPIO设备树实例详解

以某款基于 ARM 架构的开发板为例,其设备树中 GPIO 设备树节点如下:

gpio0: gpio@10000000 {
    compatible = "fsl,imx6ul-gpio", "gpio-generic";
    reg = <0x10000000 0x1000>;
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
    gpio-controller;
    #gpio-cells = <2>;
};

在这个节点中,“compatible” 属性表明该 GPIO 控制器与 “fsl,imx6ul-gpio” 和 “gpio-generic” 兼容,这使得内核能够准确找到与之匹配的驱动程序 。“reg” 属性指定了 GPIO 控制器的寄存器地址范围,起始地址为 0x10000000,大小为 0x1000 字节 。“interrupts” 属性描述了 GPIO 控制器的中断信息,使用 GIC_SPI 类型的中断,中断号为 66,触发方式为高电平触发 。

“gpio-controller” 属性标识该节点为 GPIO 控制器 。“#gpio-cells = <2>” 则表示在引用该 GPIO 控制器时,需要两个 32 位的参数 。第一个参数表示 GPIO 的编号,第二个参数表示 GPIO 的触发类型 。例如,在其他节点中引用该 GPIO 控制器的某个引脚时,可能会这样写:

my_device {
    gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
    ...
};

这里表示使用 “gpio0” 控制器的第 10 号引脚,触发类型为高电平有效 。这种属性设置方式,为系统中其他设备使用 GPIO 提供了清晰的规范和接口 。

4.2 LED设备树实例全解析

假设我们要在开发板上实现一个 LED 驱动,首先在设备树中定义 LED 节点:

leds {
    compatible = "gpio-leds";
    pinctrl-names = "default";
    pinctrl-0 = <&led_pins>;

    led_red: red_led {
        label = "red_led";
        gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;
        default-state = "off";
    };
};

pinctrl: pinctrl@10000400 {
    compatible = "fsl,imx6ul-pinctrl";
    reg = <0x10000400 0x1000>;

    led_pins: ledgrp {
        fsl,pins = <
            MX6UL_PAD_GPIO1_IO18__GPIO1_IO18 0x10b0
        >;
    };
};

 

在这个设备树实例中,“leds” 节点的 “compatible = "gpio-leds"” 属性表明这是一个基于 GPIO 的 LED 设备 。“pinctrl-names” 和 “pinctrl-0” 属性用于引脚控制,通过引用 “led_pins” 节点来配置 LED 所使用的引脚 。

“led_red” 子节点定义了一个红色 LED,“label” 属性为其命名,“gpios” 属性指定该 LED 连接到 “gpio1” 控制器的 18 号引脚,且为低电平有效 。“default-state = "off"” 表示 LED 默认处于熄灭状态 。

在驱动开发中,内核驱动会根据 “compatible” 属性找到对应的 LED 驱动程序 。在驱动的 probe 函数中,通过设备树 API 函数来获取设备树节点信息 。例如,使用 “of_find_node_by_path” 函数找到 “leds” 节点,再通过 “of_get_child_by_name” 函数获取 “led_red” 子节点 。

然后,利用 “of_property_read_u32_array” 函数读取 “gpios” 属性的值,从而获取 LED 所连接的 GPIO 引脚信息 。最后,根据这些信息进行 GPIO 的初始化和 LED 的控制操作 。

4.3自定义设备树节点与驱动匹配实例

假设我们要添加一个自定义的温度传感器设备,首先在设备树中创建自定义节点:

temperature_sensor: sensor@1234 {
    compatible = "mycompany,temperature-sensor";
    reg = <0x1234 0x100>;
    sensor_type = "temperature";
    sampling_rate = <100>;
};

这个自定义节点 “temperature_sensor” 的 “compatible” 属性设置为 “mycompany,temperature-sensor”,用于与驱动程序进行匹配 。“reg” 属性指定了传感器的寄存器地址范围 。“sensor_type” 和 “sampling_rate” 是自定义属性,分别表示传感器类型和采样率 。

接下来编写对应的内核驱动:

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>

static int my_sensor_probe(struct platform_device *pdev) {
    struct device_node *np = pdev->dev.of_node;
    u32 reg_val, sampling_rate;
    const char *sensor_type;

    if (!np)
        return -EINVAL;

    // 读取reg属性
    if (of_property_read_u32(np, "reg", &reg_val) != 0) {
        dev_err(&pdev->dev, "Failed to read reg property\n");
        return -EINVAL;
    }

    // 读取sensor_type属性
    if (of_property_read_string(np, "sensor_type", &sensor_type) != 0) {
        dev_err(&pdev->dev, "Failed to read sensor_type property\n");
        return -EINVAL;
    }

    // 读取sampling_rate属性
    if (of_property_read_u32(np, "sampling_rate", &sampling_rate) != 0) {
        dev_err(&pdev->dev, "Failed to read sampling_rate property\n");
        return -EINVAL;
    }

    // 这里可以根据读取到的属性进行传感器的初始化和操作
    dev_info(&pdev->dev, "Sensor initialized: reg = 0x%x, type = %s, sampling_rate = %d\n", reg_val, sensor_type, sampling_rate);

    return 0;
}

static int my_sensor_remove(struct platform_device *pdev) {
    // 清理操作
    return 0;
}

static const struct of_device_id my_sensor_of_match[] = {
    {.compatible = "mycompany,temperature-sensor"},
    {},
};
MODULE_DEVICE_TABLE(of, my_sensor_of_match);

static struct platform_driver my_sensor_driver = {
   .probe = my_sensor_probe,
   .remove = my_sensor_remove,
   .driver = {
       .name = "mycompany-temperature-sensor-driver",
       .of_match_table = my_sensor_of_match,
    },
};

module_platform_driver(my_sensor_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Custom Temperature Sensor Driver");

在这个驱动程序中,“my_sensor_of_match” 数组用于匹配设备树中自定义节点的 “compatible” 属性 。“my_sensor_probe” 函数在设备探测时被调用,通过设备树 API 函数读取设备树节点的属性信息,并进行相应的初始化操作 。这样,通过 “compatible” 属性,设备树中的自定义节点与内核驱动成功匹配,驱动能够正确解析设备树属性并对设备进行控制 。

   
92 次浏览       4
 
相关文章

CMM之后对CMMI的思考
对软件研发项目管理的深入探讨
软件过程改进
软件过程改进的实现
 
相关文档

软件过程改进框架
软件过程改进的CMM-TSP-PSP模型
过程塑造(小型软件团队过程改进)
软件过程改进:经验和教训
 
相关课程

以"我"为中心的过程改进(iProcess )
iProcess过程改进实践
CMMI体系与实践
基于CMMI标准的软件质量保证

最新活动计划
DeepSeek大模型应用开发 6-12[厦门]
人工智能.机器学习TensorFlow 6-30[直播]
基于 UML 和EA进行分析设计 6-23[北京]
嵌入式软件架构-高级实践 7-9[北京]
用户体验、易用性测试与评估 7-25[西安]
图数据库与知识图谱 8-23[北京]
 
 
最新文章
iPerson的过程观:要 过程 or 结果
基于模型的需求管理方法与工具
敏捷产品管理之 Story
敏捷开发需求管理(产品backlog)
Kanban看板管理实践精要
最新课程
基于iProcess的敏捷过程
软件开发过程中的项目管理
持续集成与敏捷开发
敏捷过程实践
敏捷测试-简单而可行
更多...   
成功案例
英特尔 SCRUM-敏捷开发实战
某著名汽车 敏捷开发过程与管理实践
北京 敏捷开发过程与项目管理
东方证券 基于看板的敏捷方法实践
亚信 工作量估算
更多...