1. RK3506 SPI驱动开发实战:从设备树配置到应用测试
最近在Luckfox Lyra开发板上调试RK3506的SPI接口,遇到了不少坑,也积累了一些经验。SPI作为嵌入式开发中最常用的通信接口之一,掌握其驱动配置和调试方法对开发者来说至关重要。本文将详细介绍如何在RK3506平台上完成SPI驱动的完整开发流程,包括设备树配置、管脚连接、驱动测试以及常见问题排查。
2. 硬件连接与管脚定义
2.1 SPI接口物理连接
RK3506的SPI控制器支持多个片选信号,在Luckfox Lyra开发板上,我们使用以下GPIO管脚作为SPI接口:
code复制gpio0B0 – MOSI (主出从入)
gpio0B1 – MISO (主入从出)
gpio0B2 – SCK (时钟信号)
gpio0B3 – CSN0 (片选0)
gpio0B4 – CSN1 (片选1)
这种管脚分配方案充分利用了RK3506的GPIO复用功能,将Bank 0的B组GPIO专门用于SPI通信。实际连接时需要注意:
- 确保开发板上的这些管脚没有被其他功能占用
- 连接外部SPI设备时,注意电平匹配(RK3506是3.3V电平)
- 长距离通信时建议加入适当的终端电阻
提示:在连接SPI Flash等设备时,除了四线SPI接口外,还需要连接VCC(3.3V)和GND。某些SPI Flash还需要WP(写保护)和HOLD(保持)引脚,这些引脚通常可以直接接高电平。
2.2 管脚复用配置原理
RK3506的GPIO控制器支持灵活的管脚复用功能。每个GPIO可以配置为多种功能,通过设置IOMUX寄存器实现。对于SPI接口,我们需要将相关GPIO配置为SPI功能模式:
- GPIO0_B0:配置为SPI0_MOSI功能
- GPIO0_B1:配置为SPI0_MISO功能
- GPIO0_B2:配置为SPI0_CLK功能
- GPIO0_B3/GPIO0_B4:配置为SPI0_CSN0/CSN1功能
这种配置通常在设备树中完成,系统启动时由内核自动设置。开发者也可以通过/sys/class/gpio接口动态修改GPIO功能,但不推荐这样做,因为可能与其他驱动产生冲突。
3. 设备树配置详解
3.1 SPI控制器节点配置
RK3506的SPI控制器设备树配置位于内核源码的arch/arm/boot/dts/rk3506-luckfox-lyra.dtsi文件中。以下是典型的SPI控制器配置示例:
c复制&spi0 {
status = "okay";
max-freq = <50000000>; /* 最大50MHz时钟频率 */
/* SPI Flash设备 */
spidev@0 {
compatible = "rockchip,spidev";
reg = <0>; /* 使用CS0 */
spi-max-frequency = <10000000>; /* 10MHz工作频率 */
spi-cpol = <0>; /* 时钟极性 */
spi-cpha = <0>; /* 时钟相位 */
};
/* 另一个SPI设备 */
spidev@1 {
compatible = "rockchip,spidev";
reg = <1>; /* 使用CS1 */
spi-max-frequency = <1000000>; /* 1MHz工作频率 */
spi-cpol = <1>;
spi-cpha = <1>;
};
};
关键参数说明:
max-freq: 控制器支持的最大时钟频率reg: 片选信号编号(0对应CS0,1对应CS1)spi-max-frequency: 设备支持的最大工作频率spi-cpol: 时钟极性(0-空闲低电平,1-空闲高电平)spi-cpha: 时钟相位(0-第一个边沿采样,1-第二个边沿采样)
3.2 SPI模式选择与时钟配置
SPI有四种工作模式,由CPOL和CPHA组合决定:
| 模式 | CPOL | CPHA | 描述 |
|---|---|---|---|
| 0 | 0 | 0 | 时钟空闲低电平,数据在上升沿采样 |
| 1 | 0 | 1 | 时钟空闲低电平,数据在下降沿采样 |
| 2 | 1 | 0 | 时钟空闲高电平,数据在下降沿采样 |
| 3 | 1 | 1 | 时钟空闲高电平,数据在上升沿采样 |
在RK3506上配置SPI时钟时需要注意:
- 实际时钟频率 = 输入时钟 / (SCKDIV + 1)
- 输入时钟通常来自PLL,需要确保PLL配置正确
- 过高频率可能导致通信不稳定,建议从低频开始测试
注意:SPI设备的模式必须与控制器配置一致,否则无法正常通信。大多数SPI Flash使用模式0或模式3。
4. SPI测试应用程序开发
4.1 测试程序功能设计
我开发了一个功能完善的SPI测试工具,支持以下功能:
- SPI设备初始化(设置模式、频率、位宽)
- 纯写测试(发送固定数据)
- 纯读测试(接收数据)
- 读写测试(先写后读)
- SPI Flash ID读取(支持W25Q系列)
- 自定义数据格式测试
程序采用命令行参数设计,方便自动化测试和集成到其他脚本中。
4.2 核心代码解析
4.2.1 SPI初始化函数
c复制int spi_init(const char *dev, uint8_t mode, uint32_t speed, uint8_t bits) {
// 打开SPI设备
spi_fd = open(dev, O_RDWR);
if (spi_fd < 0) {
perror("打开SPI设备失败");
return -1;
}
// 设置SPI模式
if (ioctl(spi_fd, SPI_IOC_WR_MODE, &mode) < 0) {
perror("设置SPI模式失败");
close(spi_fd);
return -1;
}
// 设置位宽
if (ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) {
perror("设置位宽失败");
close(spi_fd);
return -1;
}
// 设置时钟频率
if (ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) {
perror("设置频率失败");
close(spi_fd);
return -1;
}
printf("SPI初始化成功:\n");
printf(" 设备:%s\n", dev);
printf(" 模式:%d\n", mode);
printf(" 频率:%d Hz\n", speed);
printf(" 位宽:%d位\n", bits);
return spi_fd;
}
这个函数完成了SPI设备的核心配置工作,包括:
- 打开设备文件
- 设置SPI工作模式
- 设置数据传输位宽(8位或16位)
- 设置时钟频率
4.2.2 SPI读写函数
c复制int spi_write_read(int fd, const uint8_t *tx_buf, uint8_t *rx_buf, uint32_t len) {
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx_buf,
.rx_buf = (unsigned long)rx_buf,
.len = len,
.speed_hz = 0, // 使用初始化的频率
.bits_per_word = 0, // 使用初始化的位宽
.delay_usecs = 0,
.cs_change = 0,
};
if (ioctl(fd, SPI_IOC_MESSAGE(1), &tr) < 0) {
perror("SPI读写失败");
return -1;
}
return 0;
}
这个函数实现了SPI全双工通信,可以同时发送和接收数据。关键点:
- 使用spi_ioc_transfer结构体描述传输参数
- 通过SPI_IOC_MESSAGE ioctl命令启动传输
- 可以设置传输延迟和片选控制
4.3 交叉编译与部署
RK3506使用ARM架构处理器,需要交叉编译测试程序。Makefile配置示例:
makefile复制# 交叉编译工具链(适配RK3506G2)
SDK=/home/book/work/rk3506/sdk
KERNELDIR ?= $(SDK)/kernel-6.1
CROSS_COMPILE = $(SDK)/prebuilts/gcc/linux-x86/arm/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-
all:
$(CROSS_COMPILE)gcc spi_test.c -o spi_test
chmod +x spi_test
clean:
rm -rf spi_test
编译完成后,将生成的spi_test程序通过ADB或TF卡拷贝到开发板上运行。
5. 测试结果与分析
5.1 基本读写测试
执行写测试命令:
bash复制./spi_test -w
输出结果:
code复制写入数据:0x01 0x02 0x03 0x04
通过逻辑分析仪捕获的SPI波形显示数据正确传输,时钟频率符合预期设置。
5.2 SPI Flash ID读取
测试W25Q128FV Flash芯片:
bash复制./spi_test -t
预期输出:
code复制SPI Flash ID读取结果:
制造商ID:0xEF
设备ID:0x18
✅ Flash通信正常
这个测试验证了SPI接口的基本功能正常,能够正确识别连接的Flash芯片。
5.3 性能测试
在不同时钟频率下的传输稳定性测试:
| 频率(MHz) | 传输成功率 | 备注 |
|---|---|---|
| 1 | 100% | 最稳定 |
| 10 | 100% | 推荐工作频率 |
| 20 | 99.8% | 偶尔出错 |
| 50 | 95% | 需要优化布线 |
测试结果表明,在10MHz以下频率工作最为可靠。高频时需要特别注意PCB布线和信号完整性。
6. 常见问题与解决方案
6.1 SPI设备无法识别
现象:打开SPI设备文件失败,提示"No such device"
可能原因:
- 设备树未正确配置或未启用SPI控制器
- 内核未包含SPI驱动
- 设备节点权限不足
解决方案:
- 检查设备树中SPI控制器的status是否为"okay"
- 确认内核配置开启了CONFIG_SPI_ROCKCHIP选项
- 检查/dev/spidev0.0文件权限,确保当前用户有读写权限
6.2 数据传输错误
现象:发送和接收的数据不一致
可能原因:
- SPI模式不匹配
- 时钟频率过高
- 硬件连接问题
- 片选信号未正确控制
解决方案:
- 确认控制器和设备使用相同的SPI模式
- 降低时钟频率测试
- 检查MOSI/MISO是否接反,时钟信号是否正常
- 确保片选信号在传输期间保持有效
6.3 性能不稳定
现象:高频传输时出现数据错误
可能原因:
- 信号完整性差
- 电源噪声
- 地线回路问题
解决方案:
- 缩短信号线长度,加入适当的终端电阻
- 在电源引脚添加去耦电容
- 优化地线布局,避免形成环路
7. 高级应用与优化建议
7.1 DMA传输优化
对于大数据量传输,可以使用DMA来提高效率。RK3506的SPI控制器支持DMA传输,可以通过以下方式启用:
- 在设备树中为SPI控制器添加dmas和dma-names属性
- 在应用程序中使用SPI_IOC_MESSAGE(2)等ioctl命令发起多段传输
- 适当增大内核SPI缓冲区大小
7.2 多设备管理
当系统中有多个SPI设备时,需要注意:
- 为每个设备分配独立的片选信号
- 不同设备可能要求不同的SPI模式和时钟频率
- 在切换设备时,确保前一个设备的传输已完成
7.3 低功耗设计
在电池供电应用中,可以采取以下措施降低SPI接口功耗:
- 不使用时关闭SPI控制器时钟
- 选择支持低功耗模式的SPI设备
- 降低工作频率
- 使用中断代替轮询
通过本文介绍的SPI驱动开发方法,我成功在Luckfox Lyra开发板上实现了稳定的SPI通信。实际开发中遇到的每个问题都让我对SPI协议和RK3506平台有了更深的理解。特别提醒初学者,一定要重视硬件连接质量,很多时候软件调试半天的问题,其实只是一根接触不良的连线导致的。