欢迎光临
我们一直在努力

ARM Linux 驱动开发篇---Linux设备树实战-- Ubuntu20.04

🎬 渡水无言:个人主页渡水无言

❄专栏传送门: 《linux专栏》   《嵌入式linux驱动开发》 ⭐️流水不争先,争的是滔滔不绝

 📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生

| 省级优秀毕业生获得者 | csdn新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生

在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连

目录

前言

一、向节点追加或修改内容

1.1、直接修改imx6ull.dtsi文件的弊端

1.2、正确方案:通过 & label 向节点追加 / 修改内容

二、创建小型模板设备树

2.1、编写目标:描述 I.MX6ULL 核心硬件

2.2、分步编写:从框架到完整设备树

2.3、步骤 1:搭建根节点基础框架

2.4、步骤 2:添加 CPU 节点(描述核心处理器)

2.5、步骤 3:添加 SOC 节点(管理内部外设)

2.6、步骤 4:添加 OCRAM 节点(描述内部 RAM)

2.7、步骤 5:添加 AIPS1/AIPS2/AIPS3 总线域节点

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 总线域,每个域对应独立的地址空间,地址范围如下:

    总线域起始地址大小
    AIPS10x020000000x100000
    AIPS20x021000000x100000
    AIPS30x022000000x100000

    每个 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 的缩小版。


    总结

    这一期博客我们介绍了如何向节点追加或修改内容,以及尝试创建一个小型的模板设备树。

    赞(0)
    未经允许不得转载:171主机测评 » ARM Linux 驱动开发篇---Linux设备树实战-- Ubuntu20.04
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址