2. 开发环境搭建:硬件平台与工具链

做嵌入式驱动开发,环境搭建是第一道坎。说实话,我见过不少新手在这上面卡了一两天,最后发现是工具链版本不对。这一章咱们就把这些基础工作捋清楚。

2.1 硬件平台选型

目前主流的异构多核平台,我接触最多的是 STM32MP157 和 i.MX8M 系列。这两款芯片在工业产品和消费电子里都很常见。

特性 STM32MP157 i.MX8M Mini
架构 单核/双核 Cortex-A7 + Cortex-M4 四核 Cortex-A53 + Cortex-M4
主频 A7: 650MHz / M4: 209MHz A53: 1.6GHz / M4: 400MHz
核间通信 Mailbox + RPMsg Mailbox + RPMsg
开发板 STM32MP157C-DK2 i.MX8M Mini EVK
成本 较低 中等

我个人习惯用 STM32MP157 做教学,因为它的文档更全,社区资源也多。但如果你手头是 i.MX8M,原理完全一样,只是设备树和寄存器地址不同。

我的建议: 初学者优先选 STM32MP157C-DK2 这块板子。它自带 ST-Link 调试器,不用额外买仿真器。我在项目里用它做过一个工业网关,跑了半年没出过问题。

2.2 获取 Linux 内核源码

内核源码是驱动开发的根基。你想想看,没有源码,你怎么知道 Mailbox 驱动怎么注册的?

获取方式有两种:

  1. 从官方仓库克隆:git clone https://github.com/STMicroelectronics/linux.git(以 ST 为例)
  2. 下载 LTS 版本:wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.xx.tar.xz

我建议用厂商维护的分支。为什么?因为厂商已经帮你适配好了设备树和驱动补丁。我自己踩过坑——用主线内核跑 STM32MP157,结果 Mailbox 驱动死活不工作,后来发现是时钟配置没对齐。

关键路径:

  • 内核源码:linux/drivers/mailbox/ —— Mailbox 框架
  • 内核源码:linux/drivers/rpmsg/ —— RPMsg 框架
  • 设备树:linux/arch/arm/boot/dts/stm32mp157*.dts

2.3 交叉编译工具链配置

交叉编译,说白了就是在 PC 上编译出能在 ARM 上跑的程序。这个环节最容易出问题的是版本不匹配。

以 STM32MP157 为例,我常用的工具链是 arm-linux-gnueabihf-:

# 下载 Linaro 工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

# 解压到 /opt
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/

# 设置环境变量
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
注意: 工具链的 glibc 版本必须与目标板上的根文件系统匹配。我曾经因为用了太新的工具链,编译出的驱动在板子上报 FATAL: kernel too old,折腾了一天才发现是 glibc 版本冲突。

验证工具链是否生效:

arm-linux-gnueabihf-gcc --version
# 输出类似:arm-linux-gnueabihf-gcc (Linaro GCC 7.5-2019.12) 7.5.0

2.4 设备树基础

设备树(Device Tree)是 Linux 内核用来描述硬件信息的机制。对于异构多核通信,设备树里要声明 Mailbox 控制器、通道以及 RPMsg 相关的节点。

嗯,这里要注意:设备树不是 C 代码,它是一种树形结构文本,后缀是 .dts。

一个典型的 Mailbox 节点长这样:

/* stm32mp157.dtsi 中的 Mailbox 控制器 */
m4_mailbox: mailbox@4c001000 {
    compatible = "st,stm32mp1-mailbox";
    reg = <0x4c001000 0x400>;
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&rcc 0 0>;
    #mbox-cells = <2>;
    status = "disabled";
};

这里 #mbox-cells = <2> 表示每个通道需要两个参数:通道 ID 和方向。我在项目中遇到过一个问题——忘记在板级 dts 里 status = "okay",结果 Mailbox 驱动根本没加载。

RPMsg 相关的设备树节点通常放在 m4_system_resources 或 reserved-memory 里:

reserved-memory {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;

    m4_rsc_table: rsc-table@10044000 {
        reg = <0x10044000 0x1000>;
        no-map;
    };

    m4_vdev0_buffer: vdev0-buffer@10045000 {
        reg = <0x10045000 0x4000>;
        no-map;
    };
};
避坑指南: 我曾经在配置 reserved-memory 时,忘了加 no-map 属性。结果内核把这块内存映射了两次,导致 RPMsg 通信时数据错乱。记住:核间共享内存一定要加 no-map,告诉内核不要自己管理这块区域。

2.5 整体环境结构

下面这张图展示了开发环境的核心组件和它们之间的关系:

开发环境结构图 开发环境核心组件关系 PC 开发机 Linux 内核源码 drivers/mailbox/ 交叉编译工具链 arm-linux-gnueabihf- 设备树源文件 *.dts / *.dtsi 编译输出 zImage + dtb + ko 编译 目标板(STM32MP157) Cortex-A7 内核 运行 Linux Cortex-M4 内核 运行裸机/RTOS Mailbox 硬件 核间中断 + 共享内存 RPMsg 驱动 基于 Mailbox 的通信 部署 调试工具 串口终端 JTAG/SWD dmesg /sys/kernel/debug 流程:PC 上编辑源码 → 交叉编译 → 部署到目标板 → 调试验证

2.6 验证环境是否就绪

环境搭好之后,我习惯跑一个简单的测试:编译一个空的 Mailbox 驱动模块,确保工具链和内核源码能配合工作。

# 进入内核源码目录
cd linux

# 配置内核(使用厂商默认配置)
make stm32mp1_defconfig

# 编译设备树
make dtbs

# 编译内核模块
make modules

# 如果以上三步都通过,环境就基本就绪了

快速检查清单:

  • ✅ 交叉编译器能输出版本信息
  • ✅ 内核源码目录下有 drivers/mailbox/ 和 drivers/rpmsg/
  • ✅ 设备树文件包含 Mailbox 节点
  • ✅ 板子能通过串口打印内核启动日志