问答 店铺
热搜: ZYNQ FPGA discuz

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

微信扫一扫 分享朋友圈

已有 18 人浏览分享

开启左侧

设备树 (DTS) 核心原理与驱动适配实战教程

[复制链接]
18 0
经验分享
经验分享: 06-龙芯 » X
前言
设备树 (Device Tree) 是现代 Linux 驱动开发的核心技术,它将硬件描述信息从内核代码中剥离,实现了 "一个内核镜像适配多种硬件" 的目标。本文从原理出发,结合实际开发场景,详细讲解设备树的语法结构、编译方法、驱动适配流程,帮助开发者彻底掌握设备树开发技能。
一、设备树基础概念1.1 为什么需要设备树
在设备树出现之前,Linux 内核中大量硬编码了各类硬件的板级信息,导致内核代码臃肿、可移植性差。ARM 社区引入设备树后:
· 硬件描述与驱动代码分离
· 同一内核可适配不同电路板
· 驱动开发标准化,降低维护成本
· 新增硬件无需修改内核源码,只需修改设备树
1.2 核心组成
设备树体系包含三个部分:
1. DTS:设备树源文件,人类可读的硬件描述
2. DTC:设备树编译器,将 DTS 编译为 DTB
3. DTB:设备树二进制文件,bootloader 传递给内核
二、DTS 语法详解2.1 基本结构
设备树采用树形结构,根节点为/,每个节点代表一个硬件模块:
/dts-v1/;
/ {
    model = "My Embedded Board";
    compatible = "vendor,board-name";
    /* CPU节点 */
    cpus {
        cpu@0 {
            compatible = "arm,cortex-a53";
        };
    };
    /* 总线节点 */
    soc {
        uart@10000000 {
            compatible = "vendor,uart";
            reg = <0x10000000 0x1000>;
            interrupts = <12>;
            status = "okay";
        };
    };
};
2.2 核心属性
属性名
作用
示例
compatible
驱动匹配标识,"厂商,型号" 格式
"fsl,imx6ul-uart"
reg
寄存器地址范围
<0x10000000 0x1000>
interrupts
中断号
<12>
status
设备状态
"okay"/"disabled"
pinctrl
引脚复用配置
<&pinctrl_uart1>
clocks
时钟源配置
<&clks 12>
2.3 节点引用与覆盖
通过&节点标签可以引用已有节点进行修改,这是板级 DTS 修改 SOC 通用配置的常用方式:
&uart1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart1>;
    status = "okay";
};
三、设备树编译与反编译3.1 编译 DTB
内核源码中编译:
make dtbs ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
单独编译单个 dts 文件:
dtc -I dts -O dtb -o board.dtb board.dts
3.2 反编译 DTB
调试时经常需要反编译 dtb 文件查看内容:
dtc -I dtb -O dts -o board.dts board.dtb
系统启动后,设备树节点会映射到/proc/device-tree目录:
# 查看所有节点
ls /proc/device-tree
# 查看某个属性
cat /proc/device-tree/soc/uart@10000000/status
四、驱动与设备树匹配4.1 匹配方式
驱动通过of_device_id表与设备树节点的compatible属性匹配:
static const struct of_device_id my_driver_of_match[] = {
    { .compatible = "vendor,my-device" },
    { /* 哨兵 */ }
};
MODULE_DEVICE_TABLE(of, my_driver_of_match);
static struct platform_driver my_driver = {
    .probe = my_driver_probe,
    .remove = my_driver_remove,
    .driver = {
        .name = "my-driver",
        .of_match_table = my_driver_of_match,
    },
};
4.2 读取设备树属性
驱动 probe 函数中读取节点属性:
struct device_node *np = pdev->dev.of_node;
u32 reg_val;
int ret;
/* 读取32位整数属性 */
ret = of_property_read_u32(np, "reg", ®_val);
/* 读取字符串属性 */
const char *str;
of_property_read_string(np, "status", &str);
/* 获取内存映射 */
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *base = devm_ioremap_resource(&pdev->dev, res);
五、实战:添加自定义 GPIO 设备5.1 修改设备树
在设备树中添加自定义 LED 节点:
/ {
    my_led {
        compatible = "myboard,my-led";
        led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
        status = "okay";
    };
};
5.2 驱动实现要点
1. 定义 compatible 匹配表
2. probe 中解析 gpio 属性
3. 申请 GPIO 并注册字符设备
4. 实现读写控制接口
5.3 验证方法
编译设备树和驱动后,加载驱动模块:
insmod my_led.ko
ls /sys/class/leds/
echo 1 > /sys/class/leds/my_led/brightness
六、常见问题排查
1. 驱动不 probe:检查 compatible 字符串是否完全一致,status 是否为 okay
2. 属性读取失败:确认属性名称和类型匹配,使用of_property_read_*正确函数
3. 地址映射错误:检查 reg 属性的地址和长度是否与硬件手册一致
4. 中断不触发:确认中断号和触发方式配置正确
总结
设备树是嵌入式 Linux 驱动开发的基础技能,掌握其原理和使用方法是进阶的必经之路。本文系统讲解了设备树的语法、编译、驱动匹配流程,并给出了实际开发案例。实际开发中,建议多参考内核源码中已有驱动的设备树写法,养成规范的编码习惯。

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

10

粉丝

168

主题
精彩推荐
热门资讯
    网友晒图
      图文推荐
        
        • 微信公众平台

        • 扫描访问手机版