
🎬 渡水无言 :个人主页渡水无言
❄专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》
⭐️流水不争先,争的是滔滔不绝
📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生
| 省级优秀毕业生获得者 | csdn新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生
在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连

目录
[1.2、正确方案:通过 & label 向节点追加 / 修改内容](#1.2、正确方案:通过 & label 向节点追加 / 修改内容)
[2.1、编写目标:描述 I.MX6ULL 核心硬件](#2.1、编写目标:描述 I.MX6ULL 核心硬件)
[2.3、步骤 1:搭建根节点基础框架](#2.3、步骤 1:搭建根节点基础框架)
[2.4、步骤 2:添加 CPU 节点(描述核心处理器)](#2.4、步骤 2:添加 CPU 节点(描述核心处理器))
[2.5、步骤 3:添加 SOC 节点(管理内部外设)](#2.5、步骤 3:添加 SOC 节点(管理内部外设))
[2.6、步骤 4:添加 OCRAM 节点(描述内部 RAM)](#2.6、步骤 4:添加 OCRAM 节点(描述内部 RAM))
[2.7、步骤 5:添加 AIPS1/AIPS2/AIPS3 总线域节点](#2.7、步骤 5:添加 AIPS1/AIPS2/AIPS3 总线域节点)
[2.7、步骤 6:添加外设控制器节点(ECSPI1/USBOTG1/RNGB)](#2.7、步骤 6:添加外设控制器节点(ECSPI1/USBOTG1/RNGB))
前言
前一期博客我们介绍了设备树的语法,这一期博客我们再介绍一下如何向节点追加或修改内容,以及尝试创建一个小型的模板设备树。
一、向节点追加或修改内容
在嵌入式产品开发中,硬件需求变更十分常见设备树作为硬件描述文件,必须同步适配硬件修改。本次博客以 I.MX6U-ALPHA 开发板为例,介绍如何向已有 I2C1 节点追加 fxls8471 子节点,同时保证修改仅作用于当前开发板,不影响其他基于 I.MX6ULL 的板子。
1.1、直接修改imx6ull.dtsi文件的弊端
首先看 I.MX6ULL 的 I2C1 节点定义(位于imx6ull.dtsi文件中),这是所有基于 I.MX6ULL 的板子都会引用的通用设备树头文件:
i2c1: i2c@021a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "disabled"; // 默认禁用I2C1
};
如果直接在imx6ull.dtsi的 i2c1 节点下添加 fxls8471 子节点(如下所示),会导致所有引用该 DTSI 的板子都被强制添加这个设备 ,这明显不合理啊!
// 错误示范:直接修改通用DTSI文件
i2c1: i2c@021a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "disabled";
// 直接添加会影响所有板子
fxls8471@1e {
compatible = "fsl,fxls8471";
reg = <0x1e>;
};
};
这里就要引入另外一个内容,那就是如何向节点追加数据,我们现在要解决的就是如何向 i2c1 节点追加一个名为 fxls8471 的子节点,而且不能影响到其他使用到 I.MX6ULL 的板子。
1.2、正确方案:通过 & label 向节点追加 / 修改内容
I.MX6U-ALPHA 开发板有专属的设备树文件imx6ull-alientek-emmc.dts,我们需要在这个文件中通过节点标签引用(&label) 的方式,仅对当前板子的 i2c1 节点做修改,核心语法如下:
&节点标签 {
// 要追加的子节点/要修改的属性
};
&节点标签:通过节点的 label(如i2c1)定位到imx6ull.dtsi中的目标节点,无需写完整节点路径;
花括号内:可新增属性、修改原有属性值、添加子节点,且所有修改仅作用于当前 DTS 文件对应的板子。
以下是imx6ull-alientek-emmc.dts中对 i2c1 节点的完整修改示例,兼顾 "启用 I2C1""配置参数""追加子节点" 三大需求:
&i2c1 {
// 1. 新增属性:配置I2C1时钟频率为100KHz
clock-frequency = <100000>;
// 2. 配置I2C1引脚复用
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
// 3. 修改原有属性:启用I2C1(覆盖dtsi中的disabled)
status = "okay";
// 4. 追加子节点:mag3110磁力计(NXP官方板配置,可根据实际硬件删除)
mag3110@0e {
compatible = "fsl,mag3110";
reg = <0x0e>; // I2C设备地址0x0e
position = <2>;
};
// 5. 追加子节点:fxls8471六轴芯片(核心需求)
fxls8471@1e {
compatible = "fsl,fxls8471";
reg = <0x1e>; // I2C设备地址0x1e
position = <0>;
// 配置中断引脚:挂载到GPIO5的第0引脚,触发方式为高电平(8对应IRQ_TYPE_LEVEL_HIGH)
interrupt-parent = <&gpio5>;
interrupts = <0 8>;
};
};
属性修改优先级:DTS 文件中通过&label修改的属性(如status = "okay"),会覆盖 DTSI 文件中该节点的原有属性值;
子节点作用域:追加的子节点(如 fxls8471@1e)仅存在于当前开发板的设备树中,不会影响其他引用imx6ull.dtsi的板子;
二、创建小型模板设备树
上一期博客我们详细讲解了DTS的核心语法规则,本节将通过从零编写一个极简版 I.MX6ULL 设备树文件,这个设备树仅用于语法练习(无实际运行意义);实际产品开发中,我们无需从零写.dts 文件,只需基于 SOC 厂商提供的.dts/.dtsi 文件做定制化修改即可。
2.1、编写目标:描述 I.MX6ULL 核心硬件
我们需要在设备树中描述 I.MX6ULL 的 5 类核心硬件,覆盖 CPU、内部 RAM、外设控制器等关键组件:
-
Cortex-A7 架构的 32 位 CPU(单核心);
-
内部 OCRAM:起始地址
0x00900000,大小 128KB(0x20000); -
AIPS1 域下 ECSPI1 控制器:寄存器起始地址
0x02008000,大小0x4000; -
AIPS2 域下 USBOTG1 控制器:寄存器起始地址
0x02184000,大小0x4000; -
AIPS3 域下 RNGB 控制器:寄存器起始地址
0x02284000,大小0x4000。
2.2、分步编写:从框架到完整设备树
新建myfirst.dts文件,按 "基础框架→CPU→SOC→内存→总线域→外设" 的顺序逐步完善。
2.3、步骤 1:搭建根节点基础框架
设备树的核心是根节点/,先搭建最基础的根节点框架,仅包含兼容属性:
// myfirst.dts - 基础框架
/ {
// 兼容属性:标识板子和SOC型号
compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";
};
2.4、步骤 2:添加 CPU 节点(描述核心处理器)
I.MX6ULL 是单核心 Cortex-A7,需创建cpus父节点管理 CPU 子节点,核心规则:
#address-cells = <1>:CPU 地址占 1 个 32 位字长;
#size-cells = <0>:CPU 无地址长度(仅需标识核心 ID);
device_type = "cpu":明确节点类型为 CPU。
/ {
compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";
// CPU集合节点:管理所有CPU核心
cpus {
#address-cells = <1>;
#size-cells = <0>;
// CPU0子节点:描述唯一的Cortex-A7核心
cpu0: cpu@0 {
compatible = "arm,cortex-a7"; // 匹配Cortex-A7驱动
device_type = "cpu"; // 标识为CPU设备
reg = <0>; // CPU核心ID为0
};
};
};
2.5、步骤 3:添加 SOC 节点(管理内部外设)
I.MX6ULL 的 UART、I2C、SPI 等外设均属于 SOC 内部资源,需创建soc父节点统一管理,核心配置:
#address-cells = <1>/#size-cells = <1>:子节点 reg 属性为 "1 个地址 + 1 个长度";
compatible = "simple-bus":标识为简单总线(通用总线类型);
ranges;:空值表示子地址空间与父地址空间一致,无需地址转换。
/ {
compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>;
};
};
// SOC节点:管理所有内部外设
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges; // 子地址=父地址,无需转换
};
};
2.6、步骤 4:添加 OCRAM 节点(描述内部 RAM)
OCRAM 是 I.MX6ULL 的内部 SRAM,属于 SOC 子节点,核心配置:
节点命名:sram@00900000(类型 + 起始地址)。
reg属性:<起始地址 大小>,即<0x00900000 0x20000>。
/ {
compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>;
};
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges;
// OCRAM节点:内部SRAM
ocram: sram@00900000 {
compatible = "fsl,lpm-sram"; // 匹配NXP低功耗SRAM驱动
reg = <0x00900000 0x20000>; // 起始地址+大小
};
};
};
2.7、步骤 5:添加 AIPS1/AIPS2/AIPS3 总线域节点
I.MX6ULL 的外设分为 3 个 AIPS 总线域,每个域对应独立的地址空间,地址范围如下:
总线域 起始地址 大小
AIPS1 0x02000000 0x100000
AIPS2 0x02100000 0x100000
AIPS3 0x02200000 0x100000
每个 AIPS 节点需配置:
compatible = "fsl,aips-bus", "simple-bus":匹配 NXP AIPS 总线驱动;
reg属性:总线域的地址范围;
ranges;:子地址与父地址一致。
/ {
compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>;
};
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges;
ocram: sram@00900000 {
compatible = "fsl,lpm-sram";
reg = <0x00900000 0x20000>;
};
// AIPS1总线域:管理ECSPI1等外设
aips1: aips-bus@02000000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x02000000 0x100000>;
ranges;
};
// AIPS2总线域:管理USBOTG1等外设
aips2: aips-bus@02100000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x02100000 0x100000>;
ranges;
};
// AIPS3总线域:管理RNGB等外设
aips3: aips-bus@02200000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x02200000 0x100000>;
ranges;
};
};
};
2.7、步骤 6:添加外设控制器节点(ECSPI1/USBOTG1/RNGB)
最后在对应 AIPS 域下添加外设控制器节点,核心规则:
ECSPI1 属于 AIPS1,USBOTG1 属于 AIPS2,RNGB 属于 AIPS3;
reg属性:外设寄存器的起始地址 + 大小;
status = "disabled":默认禁用(实际开发中可改为okay启用)。
// myfirst.dts - 完整的I.MX6ULL极简设备树
/dts-v1/; // 补充DTS版本声明(规范要求)
/ {
compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull";
// CPU核心描述
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>;
};
};
// SOC内部外设总节点
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges;
// 内部OCRAM
ocram: sram@00900000 {
compatible = "fsl,lpm-sram";
reg = <0x00900000 0x20000>;
};
// AIPS1域 + ECSPI1控制器
aips1: aips-bus@02000000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x02000000 0x100000>;
ranges;
ecspi1: ecspi@02008000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
reg = <0x02008000 0x4000>;
status = "disabled"; // 默认禁用
};
};
// AIPS2域 + USBOTG1控制器
aips2: aips-bus@02100000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x02100000 0x100000>;
ranges;
usbotg1: usb@02184000 {
compatible = "fsl,imx6ul-usb", "fsl,imx27-usb";
reg = <0x02184000 0x4000>;
status = "disabled"; // 默认禁用
};
};
// AIPS3域 + RNGB控制器
aips3: aips-bus@02200000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x02200000 0x100000>;
ranges;
rngb: rngb@02284000 {
compatible = "fsl,imx6sl-rng", "fsl,imx-rng", "imx-rng";
reg = <0x02284000 0x4000>;
};
};
};
};
至此,myfirst.dts 这个小型的模板设备树就编写好了,基本和 imx6ull.dtsi 很像,可以看做是 imx6ull.dtsi 的缩小版。
总结
这一期博客我们介绍了如何向节点追加或修改内容,以及尝试创建一个小型的模板设备树。