1. SPI协议基础与Rockchip平台特性
1.1 SPI通信核心机制解析
SPI(Serial Peripheral Interface)作为嵌入式系统中最常用的短距离通信协议之一,其本质是两个移位寄存器之间的同步数据交换。在RK3566平台上,SPI控制器的工作机制可以分解为以下几个关键环节:
-
片选信号(CS)的时序控制:
- 主设备通过拉低CS信号线激活从设备,这个动作必须在SCLK时钟信号产生前完成
- 典型建立时间(Setup Time)要求至少半个时钟周期
- 传输结束后CS信号的保持时间(Hold Time)同样需要满足从设备规格
-
时钟同步机制:
- RK3566的SPI控制器支持可编程时钟分频,计算公式为:
code复制其中SCR为时钟速率寄存器值,DIV为分频系数实际时钟频率 = 总线时钟 / (SCR * DIV) - 时钟极性和相位配置直接影响数据采样点,错误设置会导致数据错位
- RK3566的SPI控制器支持可编程时钟分频,计算公式为:
-
数据移位过程:
- 每个时钟周期完成1bit数据移出(MOSI)和1bit数据移入(MISO)
- RK3566支持MSB-first和LSB-first两种传输顺序,通过SPI_LSB_FIRST寄存器位控制
1.2 Rockchip SPI控制器特性
RK3566的SPI控制器在硬件设计上有几个值得注意的特性:
-
双缓冲机制:
- 发送和接收各有一个32字节的FIFO缓冲区
- 通过SPI_RXFTLR和SPI_TXFTLR寄存器可设置触发中断的水位线
-
DMA支持:
- 支持通道分离的DMA传输配置
- 在dts中需明确声明dma-names为"tx"和"rx"
-
高速模式:
- 通过pinctrl-1可切换至高速IO引脚配置
- 需要配合assigned-clock-rates提升时钟源频率
关键提示:RK3566的SPI3控制器最大理论时钟频率为50MHz,但实际稳定运行频率受PCB布局和负载特性影响,建议初期测试使用5MHz以下频率。
2. RK3566 Android11 SPI驱动配置详解
2.1 内核层配置要点
2.1.1 内核编译选项配置
在rockchip_defconfig中,以下三个关键配置项必须启用:
makefile复制CONFIG_SPI=y # 启用SPI子系统
CONFIG_SPI_ROCKCHIP=y # Rockchip专用SPI控制器驱动
CONFIG_SPIDEV=y # 用户空间SPI设备接口
特别说明:CONFIG_SPIDEV会创建/dev/spidevX.Y设备节点,这是用户空间测试的关键接口。在生产环境中建议关闭此选项以提高安全性。
2.1.2 设备树配置解析
以spi3为例,完整的设备树配置应包含以下要素:
dts复制spi3: spi@fe640000 {
compatible = "rockchip,rk3566-spi";
reg = <0x0 0xfe640000 0x0 0x1000>;
interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_SPI3>, <&cru PCLK_SPI3>;
clock-names = "spiclk", "apb_pclk";
pinctrl-names = "default", "high_speed";
pinctrl-0 = <&spi3m1_cs0 &spi3m1_cs1 &spi3m1_pins>;
pinctrl-1 = <&spi3m1_cs0 &spi3m1_cs1 &spi3m1_pins_hs>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
spi_test0@00 {
compatible = "rockchip,spi_test_bus1_cs0";
reg = <0>;
spi-max-frequency = <5000000>;
// spi-cpol; // 取消注释设置CPOL=1
// spi-cpha; // 取消注释设置CPHA=1
};
};
关键参数说明:
spi-max-frequency:实际运行频率受限于时钟分频系数,最终频率取最接近的可用值rx-sample-delay-ns:在信号质量较差时可增加采样延迟dma-names:必须同时声明"tx"和"rx"才能启用DMA模式
2.2 用户空间权限配置
在Android系统中,需要修改init.rc文件添加设备节点权限:
rc复制on boot
# SPI设备权限设置
chown system system /dev/spidev3.0
chown system system /dev/spidev3.1
chmod 0666 /dev/spidev3.*
安全建议:量产固件中应改为更严格的权限设置(如660),并限定访问组。
3. SPI测试工具开发与验证方法
3.1 测试程序编译集成
Android.bp配置示例:
python复制cc_binary {
name: "spidev_test",
vendor: true,
srcs: ["spidev_test.c"],
cflags: [
"-Wall",
"-Wextra",
"-D_GNU_SOURCE",
],
shared_libs: ["libcutils"],
init_rc: ["spidev_test.rc"],
}
关键实现函数:
c复制int spi_transfer(int fd, 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,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
if (ioctl(fd, SPI_IOC_MESSAGE(1), &tr) < 0) {
ALOGE("SPI transfer failed: %s", strerror(errno));
return -1;
}
return 0;
}
3.2 测试方法对比分析
3.2.1 逻辑分析仪测试法
| 参数 | 推荐设置 | 作用说明 |
|---|---|---|
| 采样率 | ≥4×SCLK频率 | 确保能捕获完整波形 |
| 触发条件 | CS下降沿 | 准确捕捉传输起始点 |
| 协议解码 | SPI模式 | 自动解析数据内容 |
典型问题诊断:
- 时钟抖动过大:检查PCB走线长度匹配
- 数据偏移:调整CPHA/CPOL设置
- CS信号毛刺:检查上拉电阻配置
3.2.2 短接环回测试
操作步骤:
- 物理连接:
- MOSI ↔ MISO
- GND ↔ GND
- 执行测试命令:
bash复制
adb shell /vendor/bin/spidev_test -D /dev/spidev3.0 \ -s 5000000 -b 8 -l 32 -N 100 - 结果验证:
- 发送和接收数据应完全一致
- 测试不同模式组合(CPOL/CPHA)
实测技巧:在RK3566平台上,短接测试时建议将spi-max-frequency设为≤5MHz,过高频率可能导致信号完整性问题。
4. 典型问题排查与性能优化
4.1 常见故障处理指南
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法打开设备节点 | 权限不足/驱动未加载 | 检查init.rc配置和内核日志 |
| 传输数据全为0xFF | MISO线路断开 | 检查硬件连接和引脚复用 |
| 数据错位 | CPHA/CPOL设置错误 | 确认主从设备模式一致 |
| 高频率下数据错误 | 信号完整性问题 | 降低频率或优化PCB布局 |
| DMA传输失败 | 缓存未对齐 | 确保缓冲区64字节对齐 |
4.2 性能优化实践
-
DMA配置优化:
dts复制&spi3 { dmas = <&dmac0 20>, <&dmac0 21>; dma-names = "tx", "rx"; dma-burst-size = <16>; }; -
时钟配置技巧:
- 使用assigned-clocks提升源时钟频率
- 合理设置SCR和DIV分频系数组合
-
中断优化:
- 调整FIFO水位线减少中断频率
- 使用SPI_INT_EN寄存器选择性启用中断源
实测数据对比(传输1KB数据):
| 模式 | CPU占用率 | 耗时(μs) |
|---|---|---|
| 轮询 | 100% | 520 |
| 中断 | 35% | 580 |
| DMA | 5% | 450 |
5. 深入理解SPI全双工特性
5.1 硬件层面的实现原理
RK3566的SPI控制器通过独立的发送和接收路径实现真正的全双工通信:
-
发送路径:
- APB总线 → TX FIFO → 移位寄存器 → MOSI引脚
- 时钟源来自SPI_CLK分频器
-
接收路径:
- MISO引脚 → 采样电路 → RX FIFO → APB总线
- 采样时钟与发送时钟同步
关键设计特点:
- 双通道DMA引擎可同时处理收发数据
- 独立的FIFO水位中断触发点
- 硬件自动管理CS信号时序
5.2 软件层面的并发控制
在Android HAL层实现全双工通信时需要注意:
-
ioctl调用原子性:
c复制struct spi_ioc_transfer tr[2] = { { .tx_buf = tx1, .len = len1 }, { .rx_buf = rx2, .len = len2 } }; ioctl(fd, SPI_IOC_MESSAGE(2), tr); -
缓冲区管理要点:
- 避免在传输过程中修改发送缓冲区
- 接收缓冲区应预先分配足够空间
- 考虑缓存行对齐(通常64字节)
-
实时性保障措施:
- 使用RT优先级线程处理SPI中断
- 禁用CPU频率调节器
- 锁定内存避免换页
在实际项目中,我们通过以下方法验证了全双工特性:
- 同时持续发送递增数列和接收数据
- 使用逻辑分析仪比对时间戳
- 验证吞吐量达到理论值(模式0/3:2×单工速率)