ADC驱动开发:多通道采集与DMA传输配置实践 - 第4节
4.1 交叉编译工具链配置
嵌入式Linux驱动开发与桌面开发最大的区别在于交叉编译。目标板(ARM、RISC-V等架构)资源有限,无法运行完整的编译工具链,因此需要在性能强大的宿主机(x86_64)上编译出目标板可执行的二进制文件。
4.1.1 工具链选择与安装
根据目标处理器架构选择对应的交叉编译工具链。常见选择如下:
| 目标架构 | 工具链前缀 | 典型包名 |
|---|---|---|
| ARM Cortex-A7/A9 | arm-linux-gnueabihf- | gcc-arm-linux-gnueabihf |
| ARM Cortex-A53/A72 (64位) | aarch64-linux-gnu- | gcc-aarch64-linux-gnu |
| RISC-V 64位 | riscv64-linux-gnu- | gcc-riscv64-linux-gnu |
以ARM 32位目标板为例,在Ubuntu宿主机上安装:
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf
sudo apt-get install libc6-dev-armhf-cross
4.1.2 验证工具链
安装完成后,验证工具链是否可用:
arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-objdump --version
输出应显示版本信息,无“command not found”错误。
提示: 如果目标板使用Buildroot或Yocto构建系统,工具链通常由构建系统自动生成,位于
output/host/bin/ 目录下,前缀为 arm-buildroot-linux-gnueabihf-。
4.1.3 交叉编译测试
编写一个简单的C程序测试交叉编译环境:
// test_adc.c
#include <stdio.h>
int main(void) {
printf("ADC Driver Development - Cross Compile OK\n");
return 0;
}
编译并检查文件类型:
arm-linux-gnueabihf-gcc -o test_adc test_adc.c
file test_adc
# 输出应包含 "ELF 32-bit LSB executable, ARM"
4.2 内核源码树准备
ADC驱动以内核模块形式存在,编译时需要依赖完整的内核源码树(Kernel Source Tree)。源码树提供了头文件、Kconfig/Makefile体系以及编译规则。
4.2.1 获取内核源码
从官方仓库或板卡厂商获取内核源码:
# 从kernel.org获取主线内核
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz
tar -xf linux-6.1.tar.xz
# 或从板卡厂商Git仓库克隆
git clone -b vendor-branch https://github.com/vendor/linux.git
4.2.2 配置内核
进入内核源码目录,使用目标板的默认配置:
cd linux-6.1
# 使用板卡厂商提供的defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vendor_defconfig
# 或使用menuconfig手动配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
确保以下ADC相关选项被启用:
CONFIG_IIO— IIO子系统支持CONFIG_IIO_BUFFER— IIO缓冲区支持CONFIG_IIO_DMA_BUFFER— IIO DMA缓冲区支持CONFIG_ADC_xxx— 具体ADC驱动(如CONFIG_ADC_STM32)
4.2.3 编译内核与模块
# 编译内核镜像
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
# 编译设备树
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
# 编译内核模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
注意: 编译ADC驱动模块时,必须使用与目标板运行内核完全相同的源码树。版本不匹配会导致模块插入失败("Invalid module format"错误)。
4.3 设备树基础
设备树(Device Tree)是描述硬件资源的结构化数据,Linux内核在启动时解析设备树来发现和初始化外设。ADC驱动依赖设备树来获取寄存器地址、中断号、时钟、DMA通道等参数。
4.3.1 设备树文件结构
| 文件类型 | 扩展名 | 说明 |
|---|---|---|
| 设备树源文件 | .dts | 可读的文本格式,描述硬件 |
| 设备树包含文件 | .dtsi | 公共硬件描述,被.dts引用 |
| 设备树二进制 | .dtb | 编译后的二进制,内核加载 |
| 设备树覆盖 | .dtbo | 运行时叠加的硬件描述 |
4.3.2 ADC设备树节点示例
以下是一个典型的ADC设备树节点,包含多通道和DMA配置:
adc1: adc@40012000 {
compatible = "st,stm32-adc";
reg = <0x40012000 0x400>;
interrupts = <18 0>;
clocks = <&rcc ADC1>;
clock-names = "adc";
vref-supply = <&vref>;
#address-cells = <1>;
#size-cells = <0>;
/* 多通道配置 */
channel@0 {
reg = <0>;
label = "AIN0";
};
channel@1 {
reg = <1>;
label = "AIN1";
};
channel@2 {
reg = <2>;
label = "AIN2";
};
/* DMA传输配置 */
dmas = <&dma1 0 1 0x400 0x0>;
dma-names = "rx";
/* 连续转换模式 */
st,adc-continuous-conv = <1>;
};
4.3.3 设备树编译
使用设备树编译器(dtc)将.dts编译为.dtb:
# 在内核源码树中编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
# 或单独编译
dtc -I dts -O dtb -o myboard.dtb myboard.dts
# 反编译查看
dtc -I dtb -O dts -o myboard.dts myboard.dtb
4.3.4 设备树关键属性解析
| 属性 | 作用 | ADC驱动使用场景 |
|---|---|---|
compatible |
驱动匹配标识 | 驱动通过此字符串找到对应硬件 |
reg |
寄存器地址范围 | ioremap映射ADC控制寄存器 |
interrupts |
中断号与触发类型 | 注册ADC转换完成中断 |
clocks |
时钟源 | 使能ADC工作时钟 |
dmas |
DMA控制器与通道 | 配置DMA传输ADC数据 |
vref-supply |
参考电压 regulator | 获取ADC参考电压值 |
实践要点: 在编写ADC驱动前,务必确认设备树中ADC节点的
compatible值与驱动中of_match_table完全一致;DMA配置中的dmas属性需要参考芯片参考手册的DMA请求映射表。
4.4 环境验证清单
完成上述配置后,使用以下清单验证开发环境是否就绪:
- 交叉工具链:执行
arm-linux-gnueabihf-gcc -v无报错 - 内核源码树:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- kernelversion输出版本号 - 设备树编译:
make ARCH=arm dtbs生成对应.dtb文件 - 模块编译:编写空模块Makefile,执行
make -C /path/to/kernel M=$(pwd) modules成功生成.ko
推荐工作流: 将交叉编译工具链路径加入
$PATH,设置 export ARCH=arm 和 export CROSS_COMPILE=arm-linux-gnueabihf- 到 ~/.bashrc,避免每次输入。