嵌入式 Linux 工业主板:设备树自定义配置与外设驱动加载实操
工业主板(如 i.MX6ULL、RK3568、Amlogic A311D 方案)的外设往往不像树莓派那样"默认全开"——CAN、RS485、SPI ADC、I2C 温湿度、GPIO 按键,每款底板的引脚定义都不一样,内核 defconfig 开好了驱动也只是"编进去了",真正要让 /dev/ 下出设备,还得改设备树(DTS)+ 保证驱动加载。下面以 i.MX6ULL 工业板(原厂 DTS 只开了 UART1/ETH,要加 UART3-RS485 + CAN0 + SPI ADC)为主线。
一、设备树改什么:pinctrl + 节点 + status
设备树不是改驱动代码,而是告诉内核"哪个外设挂哪根引脚、用什么时钟、地址多少"。三件事:
1. pinctrl:引脚复用 + 电气属性
i.MX 系列用 iomuxc 节点,每个外设建一个 pinctrl_xxx 子节点,含引脚号(寄存器偏移)、复用功能(ALT 模式)、上下拉、驱动强度。
/* imx6ull-14x14-evk.dts 片段:加 UART3-RS485 + CAN0 */
&iomuxc {
pinctrl_uart3_rs485: uart3rs485grp {
fsl,pins = <
MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX 0x1b0b1 /* TX, ALT1, 47k上拉 */
MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX 0x1b0b1 /* RX */
MX6UL_PAD_UART2_CTS_B__GPIO1_IO14 0x1b0b1 /* RTS 复用 GPIO 控方向 */
>;
};
pinctrl_flexcan1: can0grp {
fsl,pins = <
MX6UL_PAD_UART4_TX_DATA__FLEXCAN1_TX 0x1b0b0
MX6UL_PAD_UART4_RX_DATA__FLEXCAN1_RX 0x1b0b0
>;
};
};
0x1b0b1 是电气属性(PKE=使能保持、PUE=上拉、PUS=47k、SPEED=100MHz、DSE=驱动强度),参照 IMX6ULLRM 的 SW_PAD_CTL 寄存器填。
2. 外设节点:挂 pinctrl + 参数
&uart3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart3_rs485>;
linux,rs485-enabled-at-boot-time; /* 开机即 RS485 模式 */
rts-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>;
status = "okay";
};
&can1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_flexcan1>;
xceiver-supply = <®_can_3v3>; /* 3.3V 收发器供电 */
status = "okay";
};
⚠ status = "disabled" 是出厂默认值,不改 "okay" 驱动 probe 不到,这是新手最常见的坑。
3. 编译 & 替换
# 在内核源码根目录
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
# 拷到板子(TFTP 或 scp)
scp arch/arm/boot/dts/imx6ull-14x14-evk.dtb root@192.168.1.100:/boot/
reboot
# bootloader 加载新 dtb 后,dmesg | grep -E "uart|can" 看 probed 没
二、驱动加载:builtin vs module
驱动两种形态,工业板按需选:
① builtin(Y)= 编进 uImage/zImage
• DTS status="okay" 后内核启动时自动 probe()
• 适合必用外设(CAN、ETH、UART)
• 改 DTS 后重编 dtb + 重启即可,不用动驱动
② module(M)= 编成 .ko,modprobe 加载
• 适合现场可换的外设(SPI ADC 模组、4G 模组、不同型号 RTC)
• 好处:不用重烧内核,换 KO 就行;坏处:开机要 systemd 或 rc.local 里 modprobe
# 内核 menuconfig 把 SPIDEV 改 M
make ARCH=arm menuconfig
# Device Drivers → SPI → User mode SPI →spidev
make modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
# 拷 KO 到板子
scp drivers/spi/spidev.ko root@192.168.1.100:/lib/modules/$(uname -r)/kernel/drivers/spi/
ssh root@192.168.1.100 "depmod -a && modprobe spidev"
# 看 /dev/spidev32766.0 是否出来
DTS 里 SPI 节点要配 spidev 兼容:
&ecspi1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi1>;
status = "okay";
adc_dev@0 {
compatible = "rohm,dh2228fv"; /* spidev 通用兼容 */
reg =;
spi-max-frequency =;
};
};
三、现场适配:DTS Overlay 救急
工业主板经常"同 CPU 底板换三款"(A 款带 RS485、B 款带 CANopen、C 款带 4G),全重编 DTB 麻烦,用 DTS Overlay(fdtoverlay 命令把 .dtbo 贴到主 DTB 上):
/* overlay: 只加 RS485 RTS 反相(某款底板 RTS 低有效) */
/dts-v1/;
/plugin/;
&uart3 {
rts-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>; /* 改极性 */
};
# 板端合成
fdtoverlay -i /boot/imx6ull.dtb -o /boot/imx6ull-custom.dtb /boot/rs485-rts.dtbo
# uboot 里改 fdt_file=imx6ull-custom.dtb
RK3568 工业板更方便,U-Boot 阶段通过 fdt set 命令改节点状态,不用重编译。
四、三个翻车高发点
• pinctrl 冲突:同一个引脚在 pinctrl_uart3 和 pinctrl_gpio 里都被占了,内核启动时 pinctrl_probe 报 pin already requested,后面那个外设 probe 失败。改 DTS 前先 cat /sys/kernel/debug/pinctrl/pinctrl-handles 看占用
• CAN 没 xceiver-supply:flexcan probe 成功但 ip link set can0 up 时报 No such device,是因为收发器供电没配,3.3V 那路 regulator 没 status="okay"
• RS485 没配 RTS GPIO:linux,rs485-enabled-at-boot-time 开了但 RTS 悬空,发数据时 DE/RE 没拉,对端收不到。必须 rts-gpios 指到具体 GPIO,且 pinctrl 里这个 GPIO 也要配(不能只配 TX/RX)
调试窍门:dmesg | grep "failed to get pinctrl" 是 DTS 第一排查点;ls /dev/ 看不到设备 → 查 status 是不是还是 disabled;modprobe 报错 Unknown symbol → 先 depmod -a 再加载。





