|
前言 设备树 (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 核心属性 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 驱动开发的基础技能,掌握其原理和使用方法是进阶的必经之路。本文系统讲解了设备树的语法、编译、驱动匹配流程,并给出了实际开发案例。实际开发中,建议多参考内核源码中已有驱动的设备树写法,养成规范的编码习惯。
|