嵌入式Linux设备树配置实战:以SAM9X60-Curiosity开发板为例

📅 2026/6/21 23:29:30
嵌入式Linux设备树配置实战:以SAM9X60-Curiosity开发板为例
1. 项目概述与核心价值最近在调试一块Microchip的SAM9X60-Curiosity开发板想把一个定制的传感器模块挂上去。过程里最绕不开的就是修改和编译设备树Device Tree。这东西对于刚接触嵌入式Linux的开发者来说就像一堵无形的墙文档散落在各处概念抽象改错一个地方系统可能就启动不了。网上很多教程要么太泛泛而谈要么是针对特定平台语焉不详。所以我想结合手头这块具体的板子把设备树从配置、修改到编译、测试的完整流程掰开揉碎了讲清楚。如果你正在使用基于ARM架构的嵌入式Linux开发板尤其是Microchip的AT91系列或者类似需要手动配置设备树的平台这篇指南应该能帮你避开我踩过的那些坑。简单说设备树就是一个描述硬件的数据结构。在以前内核里要把每块板子的硬件信息比如内存地址、中断号、外设连接都硬编码进去换块板子就得重新编译内核非常麻烦。设备树解决了这个问题它把硬件描述从内核代码中分离出来变成一个可以单独编辑、编译的文本文件.dts或.dtsi。内核启动时Bootloader如U-Boot会把这个编译好的二进制文件.dtb加载到内存并传递给内核内核根据这个“地图”来识别和驱动硬件。对于SAM9X60-Curiosity这块板子我们做驱动开发或者外设移植核心工作就是正确地修改和生成这份“地图”。2. 设备树基础与SAM9X60硬件框架解析2.1 设备树的核心语法与结构设备树源文件.dts是一种层级化的数据结构语法类似JSON。理解几个基本概念是关键节点Node描述一个设备或总线用花括号{}定义。最顶层是根节点/。属性Property描述节点的具体信息是键值对。比如compatible “microchip,sam9x60-curiosity”是最重要的属性之一内核靠它来匹配对应的驱动程序。兼容性compatible这是设备的“身份证”。值是一个字符串列表优先匹配最具体的。例如一个设备的compatible是“microchip,sam9x60-gpio”, “microchip,at91sam9x5-gpio”, “atmel,at91rm9200-gpio”内核会按顺序寻找匹配的驱动。地址reg描述设备寄存器在总线地址空间中的位置和大小。通常包含多个地址 长度对。理解它需要结合#address-cells和#size-cells这两个属性它们定义了这个节点的子节点中reg属性的格式。一个最简单的设备树片段看起来是这样的/ { compatible “microchip,sam9x60-curiosity”, “microchip,sam9x60”; #address-cells 1; #size-cells 1; soc { compatible “simple-bus”; #address-cells 1; #size-cells 1; ranges; usart0: serialf801c000 { compatible “microchip,sam9x60-usart”, “atmel,at91sam9260-usart”; reg 0xf801c000 0x400; interrupts 23 IRQ_TYPE_LEVEL_HIGH 7; pinctrl-names “default”; pinctrl-0 pinctrl_usart0_default; status “okay”; }; }; };这段代码描述了一个串口设备usart0它的寄存器起始地址是0xf801c000长度0x400使用的中断号是23。pinctrl-0引用了另一个节点pinctrl_usart0_default来配置该串口所用到的具体引脚功能。2.2 SAM9X60-Curiosity开发板硬件特点SAM9X60是一款基于ARM926EJ-S内核的微处理器运行频率可达600MHz。Curiosity开发板是其评估板集成了丰富的外设接口。在修改设备树前必须清楚板载资源核心与内存ARM926EJ-S核心板载128MB DDR2 SDRAM。存储128MB的QSPI Flash一个MicroSD卡槽。网络一个10/100 Mbps以太网控制器GMAC。USB一个USB Host和一个USB Device接口。串口多个USART/UART其中USART0通常作为调试串口。扩展接口包括Arduino兼容接口和 mikroBUS 接口方便连接各种外设模块。我们的设备树修改主要就是围绕这些外设以及我们想要添加的额外模块比如通过mikroBUS连接的传感器来展开。关键是要查阅两个文档SAM9X60的数据手册和Curiosity开发板的原理图。数据手册告诉你处理器每个外设模块的寄存器基地址、中断号原理图告诉你具体的外设连接到了处理器的哪个引脚。这是所有修改工作的基石。注意在修改任何内容前务必备份原始的.dts文件。最好使用版本控制工具如git来管理你的设备树修改这样你可以清晰地追踪每一次变更。3. 设备树配置的完整实操流程3.1 环境准备与源码获取首先你需要一个可以编译设备树的Linux环境。通常是在你的开发主机PC上进行交叉编译。安装交叉编译工具链对于ARM架构的SAM9X60你需要ARM Linux GNU工具链。可以从Linaro或ARM官网下载或者使用包管理器安装。# 例如在Ubuntu上安装gcc-arm-linux-gnueabi sudo apt-get update sudo apt-get install gcc-arm-linux-gnueabi # 验证安装 arm-linux-gnueabi-gcc --version获取Linux内核源码与设备树你需要获取针对SAM9X60移植好的Linux内核源码。最直接的方式是从Microchip的官方GitHub仓库克隆。git clone https://github.com/linux4sam/linux-at91.git cd linux-at91 # 切换到与你的板卡系统镜像匹配的分支例如 linux-5.10-at91 git checkout linux-5.10-at91设备树源文件通常位于arch/arm/boot/dts/目录下。对于SAM9X60-Curiosity核心文件是at91-sam9x60_curiosity.dts。理解设备树的包含关系.dts文件不是孤立的。它通过#include语句包含其他.dtsi设备树源包含文件文件。这是一种层级化设计at91-sam9x60.dtsi描述SAM9X60芯片共性的硬件信息如CPU、内存控制器、所有外设模块的节点框架。at91-sam9x60_curiosity.dts描述Curiosity这块特定板子的硬件信息如哪些外设被启用、引脚复用配置、板载外设如EEPROM的地址等。 我们大部分针对具体板卡的修改都是在.dts文件中进行的。3.2 修改设备树以添加I2C传感器为例假设我们要通过mikroBUS接口其背后是I2C1总线连接一个温湿度传感器例如SHT30。第一步确认硬件连接。查看Curiosity原理图找到mikroBUS接口的I2C信号线连接到了处理器的哪个I2C控制器和哪个引脚。假设原理图显示连接到了TWD1和TWCK1即I2C1总线。第二步在设备树中启用并配置I2C1控制器。打开at91-sam9x60_curiosity.dts查找i2c1相关的节点。它可能已经在at91-sam9x60.dtsi中定义但在.dts文件中状态是“disabled”。/* 在 at91-sam9x60.dtsi 中可能已有定义 */ i2c1: i2cf8018000 { compatible “microchip,sam9x60-i2c”; reg 0xf8018000 0x400; interrupts 25 IRQ_TYPE_LEVEL_HIGH 7; clocks pmc PMC_TYPE_PERIPHERAL 25; pinctrl-names “default”; pinctrl-0 pinctrl_i2c1_default; status “disabled”; };我们需要在板级.dts文件中将其状态改为“okay”并确保引脚复用配置正确。/* 在 at91-sam9x60_curiosity.dts 的合适位置例如在 i2c1 节点处 */ i2c1 { pinctrl-names “default”; pinctrl-0 pinctrl_i2c1_default; status “okay”; /* 可以在这里直接添加子设备节点也可以保持为空由内核动态探测 */ };同时要检查pinctrl_i2c1_default这个引脚配置组是否已经在pinctrl节点中正确定义且与原理图匹配。第三步添加具体的传感器设备节点。在i2c1节点内部我们可以静态添加传感器设备。i2c1 { status “okay”; clock-frequency 100000; /* 标准模式100kHz */ sht30: temperature-sensor44 { compatible “sensirion,sht30”; reg 0x44; /* 其他可选属性例如中断引脚配置 */ // interrupts-extended pioA PIN_PBXX GPIO_ACTIVE_LOW; }; };sht30:是一个标签方便其他地方引用。temperature-sensor44节点名后跟设备的I2C从地址十六进制0x44。compatible必须与内核中SHT30驱动的of_device_id表里的字符串匹配。你需要确认内核是否编译了此驱动或者以模块形式提供。reg设备的I2C从地址即0x44。3.3 设备树的编译与生成修改保存后需要将其编译成二进制设备树Blob.dtb。配置内核确保内核配置包含了设备树支持以及你所用外设的驱动。# 在linux-at91源码根目录 make ARCHarm CROSS_COMPILEarm-linux-gnueabi- at91_dt_defconfig # 如果需要可以通过menuconfig进一步配置 # make ARCHarm CROSS_COMPILEarm-linux-gnueabi- menuconfig在menuconfig中确保CONFIG_OFy(设备树支持)对应的I2C驱动、SHT30传感器驱动被启用y或编译为模块m。编译设备树make ARCHarm CROSS_COMPILEarm-linux-gnueabi- dtbs这条命令会编译arch/arm/boot/dts/目录下所有.dts文件对应的.dtb。编译成功后你需要的at91-sam9x60_curiosity.dtb文件会生成在arch/arm/boot/dts/目录下。单独编译某个设备树如果只想测试当前板子make ARCHarm CROSS_COMPILEarm-linux-gnueabi- at91-sam9x60_curiosity.dtb3.4 设备树的部署与测试将生成的.dtb文件部署到开发板上有多种方式取决于你的Bootloader和启动方式。方式一通过U-Boot和TFTP加载适用于调试阶段将at91-sam9x60_curiosity.dtb复制到TFTP服务器目录。启动开发板在U-Boot命令行中断启动过程。使用U-Boot命令加载并启动# 设置服务器IP和板子IP setenv serverip 192.168.1.100 setenv ipaddr 192.168.1.50 # 通过TFTP加载设备树文件到内存地址 0x21000000 tftp 0x21000000 at91-sam9x60_curiosity.dtb # 启动内核并指定设备树地址 bootz 0x22000000 - 0x21000000这种方式非常灵活无需刷写存储适合快速迭代测试。方式二替换启动介质中的dtb文件适用于固化如果系统从MicroSD卡启动通常卡的第一个分区FAT格式存放着内核镜像zImage和设备树文件at91-sam9x60_curiosity.dtb。将SD卡挂载到电脑用新编译的.dtb文件覆盖原有的文件即可。启动后验证 系统启动后登录开发板通过以下命令检查设备树是否生效# 查看I2C1总线是否识别到设备 ls /sys/bus/i2c/devices/ # 应该能看到类似 “1-0044” 的目录对应I2C1总线上的地址0x44设备 # 查看传感器设备是否成功绑定驱动 ls /sys/bus/i2c/devices/1-0044/ # 如果驱动加载成功这里会有 driver - ../../../bus/i2c/drivers/sht30 的链接 # 查看设备树中I2C1节点的状态 cat /proc/device-tree/soc/i2cf8018000/status # 应该显示 “okay” # 更直观地查看完整的设备树 apt-get install device-tree-compiler # 安装dtc工具 dtc -I fs /sys/firmware/devicetree/base -O dts | less # 在输出中搜索 “sht30” 或 “i2c1”确认你的修改已存在4. 常见问题排查与调试技巧实录设备树配置出错轻则外设无法工作重则系统无法启动。以下是我在实际项目中积累的排查经验。4.1 系统无法启动或外设不工作检查语法错误设备树编译器dtc非常严格。在编译前可以用它做预检查。dtc -O dts -I dtb -o test.dts arch/arm/boot/dts/at91-sam9x60_curiosity.dtb 21 # 或者直接编译dts看错误 dtc -I dts -O dtb -o test.dtb arch/arm/boot/dts/at91-sam9x60_curiosity.dts常见的语法错误包括缺少分号、括号不匹配、节点名格式错误等。检查内核启动日志这是最重要的调试信息源。通过串口控制台查看内核启动早期的输出。[ 0.000000] OF: fdt: Ignoring memory range 0x0 - 0x100000 [ 0.000000] Machine model: Microchip SAM9X60-Curiosity [ 0.000000] …… [ 1.234567] i2c /dev entries driver [ 1.345678] at91_i2c f8018000.i2c: version 0x311 [ 1.345789] at91_i2c f8018000.i2c: using 16 bit data width [ 1.456789] at91_i2c f8018000.i2c: bus 1 at 100 kHz [ 1.567890] sht30 1-0044: chip found重点关注Machine model是否正确识别了你的板子。你的外设控制器如at91_i2c f8018000.i2c是否被成功探测和初始化。你的具体设备如sht30 1-0044是否被成功发现并绑定驱动。如果看到probe failed或类似的错误就要根据错误码去查原因。检查引脚复用冲突这是最隐蔽的问题之一。SAM9X60的每个引脚功能都是复用的。如果你为I2C1配置了引脚但同一个引脚组PIO里的另一个引脚被其他地方比如另一个已启用的外设或GPIO配置成了不同功能就会冲突。务必仔细核对原理图和pinctrl配置。一个技巧是在系统启动后查看引脚复用状态cat /sys/kernel/debug/pinctrl/pinctrl-handles # 或者查看具体的pinctrl状态 cat /sys/kernel/debug/pinctrl/*/pinmux-pins4.2 设备树与驱动匹配问题compatible字符串不匹配这是设备无法绑定驱动的首要原因。确保设备树节点中的compatible属性值与内核驱动源码中of_device_id结构体数组里定义的字符串完全一致包括大小写和标点。去内核源码drivers/目录下搜索你的驱动核对字符串。// 例如在 drivers/iio/humidity/sht30.c 中 static const struct of_device_id sht30_dt_ids[] { { .compatible “sensirion,sht30” }, { } };驱动未编译进内核即使compatible匹配如果驱动被配置为模块m而模块未自动加载或者驱动直接被禁用n设备也不会工作。检查内核配置文件.configgrep CONFIG_SHT30 .config # 或者在内核源码目录下 make ARCHarm menuconfig # 然后搜索 SHT30如果驱动是模块启动后需要手动modprobe sht30。4.3 设备树覆盖与动态修改对于生产环境有时我们希望在基础设备树之上进行小范围修改而不重新编译整个DTB。U-Boot支持设备树覆盖Device Tree Overlay。创建覆盖文件(my-overlay.dts)/dts-v1/; /plugin/; / { fragment0 { target i2c1; __overlay__ { new_device55 { compatible “vendor,new-device”; reg 0x55; }; }; }; };编译覆盖文件dtc -O dtb -o my-overlay.dtbo - my-overlay.dts在U-Boot中应用# 加载基础dtb和overlay dtbo tftp 0x21000000 at91-sam9x60_curiosity.dtb tftp 0x21100000 my-overlay.dtbo # 应用覆盖 fdt addr 0x21000000 fdt resize 8192 # 预留足够空间 fdt apply 0x21100000 # 然后正常启动内核 bootz 0x22000000 - 0x21000000这种方式非常适合在产线为不同配置的硬件加载不同的外设配置。4.4 实用调试命令速查表下表总结了在开发板上最常用的设备树相关调试命令命令作用示例/说明ls /proc/device-tree/查看根节点下的所有属性/节点这是查看运行时设备树结构的起点cat /proc/device-tree/model查看板卡型号应与设备树中根节点的model属性一致hexdump /proc/device-tree/soc/i2cf8018000/reg查看某个节点的reg属性原始值输出的是二进制数据需结合#address-cells解析dtc -I fs -O dts /sys/firmware/devicetree/base将运行时设备树导出为dts文本最强大的调试工具可以查看内核实际使用的、经过所有处理后的完整设备树find /sys/firmware/devicetree/base -name “*compatible*”查找所有包含compatible字符串的文件可以快速定位设备节点在sysfs中的路径echo 1 /sys/class/gpio/export导出GPIO进行测试用于验证引脚复用是否成功如果导出失败可能该引脚已被其他驱动占用dmesg | grep -i “error|fail|probe”过滤内核日志中的错误和探测信息快速定位启动过程中的外设初始化问题设备树的配置是一个需要耐心和细致的工作它连接着硬件原理图和软件驱动。我的经验是每次只做一处小的修改然后编译、测试、确认逐步推进。养成查阅芯片数据手册、板卡原理图和内核驱动源码的习惯这三份文档交叉对照能解决90%以上的配置问题。当系统按照你的“地图”正确识别出所有硬件时那种成就感是实实在在的。