第三章:Linux内核与设备树基础——内核编译、DTS语法与CAN控制器节点配置
做嵌入式Linux工控网关,内核和设备树是绕不开的两座山。很多初学者觉得内核编译是个黑盒子,设备树更是天书。其实没那么玄乎。我刚开始接触时也一头雾水,后来在几个项目里反复折腾,才摸清了门道。今天咱们就把这两块掰开揉碎了讲清楚。
3.1 内核编译:从源码到镜像的完整流程
内核编译说白了就是把Linux内核源码变成能在你板子上跑起来的二进制文件。工控网关对实时性和稳定性要求高,所以内核配置要精打细算,不能一股脑全选上。
3.1.1 获取内核源码
我习惯从官方内核仓库拉取长期支持版本(LTS)。比如5.10.y或6.1.y系列,这些版本维护周期长,社区支持好。
# 以5.10.200为例
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.200.tar.xz
tar -xf linux-5.10.200.tar.xz
cd linux-5.10.200
3.1.2 配置内核选项
配置内核有三种常用方式:
| 配置方式 | 命令 | 适用场景 |
|---|---|---|
| 默认配置 | make defconfig |
快速生成通用配置 |
| 基于现有配置 | make menuconfig |
交互式调整,最常用 |
| 板级配置 | make ARCH=arm imx_v7_defconfig |
特定开发板官方配置 |
我个人强烈推荐 make menuconfig。它基于ncurses的图形界面,可以逐项搜索、勾选、取消。工控网关必须开启的选项包括:
- CAN总线子系统:
Networking support → CAN bus subsystem support - 实时抢占补丁(如果使用PREEMPT_RT):
General setup → Preemption Model → Fully Preemptible Kernel (Real-Time) - 设备树支持:
Device Drivers → Device Tree and Open Firmware support
.config文件备份一份。我曾经因为误操作覆盖了配置,不得不从头再来,浪费了半天时间。
3.1.3 编译与安装
# 编译内核镜像和设备树
make -j$(nproc) zImage
make -j$(nproc) dtbs
# 编译内核模块
make -j$(nproc) modules
# 安装模块到根文件系统
make modules_install INSTALL_MOD_PATH=/path/to/rootfs
这里有个坑:-j参数指定并行编译线程数,一般设为CPU核心数的1.5到2倍。但如果你在虚拟机里编译,别开太多,否则内存容易爆。我有一台4核的笔记本,开-j8直接卡死,后来老老实实改成-j4才稳定。
3.2 设备树(DTS)语法:硬件描述的语言
设备树是Linux用来描述硬件拓扑结构的数据结构。它把板级细节从内核源码里剥离出来,变成一种树形文本文件。你想想看,如果没有设备树,每换一个板子就要改内核代码,那得多麻烦?
3.2.1 基本结构
一个典型的设备树文件(.dts)长这样:
/dts-v1/;
/ {
model = "工控网关CAN采集板";
compatible = "mycompany,iot-gateway-v1";
chosen {
stdout-path = &uart1;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; // 512MB
};
cpus {
cpu@0 {
compatible = "arm,cortex-a7";
reg = <0x0>;
};
};
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart1: serial@02020000 {
compatible = "fsl,imx6ul-uart";
reg = <0x02020000 0x4000>;
interrupts = <0 26 4>;
status = "okay";
};
};
};
关键概念:
- 节点(Node):用
/开头表示根节点,子节点用{}包裹 - 属性(Property):键值对,描述节点特征
- 标签(Label):如
uart1:,方便在其他地方引用 - 引用(Reference):用
&uart1引用标签
3.2.2 常用属性
| 属性 | 含义 | 示例 |
|---|---|---|
compatible |
设备兼容性字符串,驱动匹配依据 | "fsl,imx6ul-uart" |
reg |
寄存器地址和大小 | <0x02020000 0x4000> |
interrupts |
中断号、类型 | <0 26 4> |
status |
设备状态:okay、disabled |
"okay" |
clocks |
时钟源引用 | <&clks IMX6UL_CLK_UART1> |
reg属性的地址和大小由父节点的#address-cells和#size-cells决定。如果这两个值配错了,内核解析出来的地址会完全不对。我曾经在这个问题上排查了整整一个下午。
3.3 CAN控制器节点配置:让硬件说话
工控网关的核心功能之一就是CAN总线数据采集。在设备树里配置CAN控制器节点,是让CAN硬件正常工作的前提。
3.3.1 常见的CAN控制器
嵌入式平台常用的CAN控制器有两种:
- SoC内置CAN:如i.MX6的FlexCAN、STM32MP1的bxCAN
- 外部SPI转CAN模块:如MCP2515、MCP2518
咱们以i.MX6UL的FlexCAN为例,它在设备树里的配置是这样的:
&flexcan1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_flexcan1>;
xceiver-supply = <®_can_3v3>;
status = "okay";
};
&flexcan2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_flexcan2>;
xceiver-supply = <®_can_3v3>;
status = "okay";
};
对应的引脚复用配置:
pinctrl_flexcan1: flexcan1grp {
fsl,pins = <
MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x1b020
MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x1b020
>;
};
pinctrl_flexcan2: flexcan2grp {
fsl,pins = <
MX6UL_PAD_UART2_RTS_B__FLEXCAN2_RX 0x1b020
MX6UL_PAD_UART2_CTS_B__FLEXCAN2_TX 0x1b020
>;
};
3.3.2 关键配置项说明
| 配置项 | 作用 | 注意事项 |
|---|---|---|
pinctrl-0 |
指定引脚复用配置 | 必须与原理图一致,RX/TX不能接反 |
xceiver-supply |
CAN收发器电源控制 | 如果收发器独立供电,需要添加regulator节点 |
clock-frequency |
CAN控制器时钟频率 | 影响波特率计算,通常为30MHz或40MHz |
bosch,mram-cfg |
M_CAN的MRAM配置(仅M_CAN) | 配置邮箱数量和大小 |
3.3.3 外部SPI转CAN模块配置
如果SoC没有内置CAN控制器,可以用MCP2515这类SPI转CAN芯片。配置方式略有不同:
&ecspi1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi1>;
cs-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>;
status = "okay";
can0: mcp2515@0 {
compatible = "microchip,mcp2515";
reg = <0>;
clocks = <&clk16m>;
interrupt-parent = <&gpio4>;
interrupts = <10 IRQ_TYPE_EDGE_FALLING>;
spi-max-frequency = <10000000>;
status = "okay";
};
};
这里要注意:
spi-max-frequency不要设太高,MCP2515最高支持10MHz- 中断引脚必须支持边沿触发,我遇到过用电平触发导致中断丢失的情况
- 外部晶振频率要准确,否则波特率会偏
3.4 知识体系总览
下面这张图把本章的核心逻辑串起来了:
3.5 避坑指南与调试技巧
做CAN设备树配置,有几个坑我踩过,分享给你:
我曾经在i.MX6UL上同时配置了UART2和FlexCAN2,结果两个都用不了。查了半天才发现它们复用了同一组引脚。解决方案:仔细看芯片手册的引脚复用表,确保功能不冲突。
make dtbs时报语法错误,最常见的原因是漏了分号或括号不匹配。我习惯用dtc -I dts -O dtb -o test.dtb test.dts单独编译检查,比在内核树里编译快得多。
ls /proc/device-tree/查看设备树是否被正确解析。如果CAN节点没出现,说明设备树没加载或者节点状态不是okay。
嗯,到这里内核编译、设备树语法和CAN控制器配置的核心内容就讲完了。这些知识是后面实现CAN数据采集的基础,你可以在自己的板子上动手试试,把FlexCAN或MCP2515配起来,看看能不能用ip link set can0 up type can bitrate 250000把CAN接口点亮。