当前位置: 首页 > news >正文

RK3566 SPI设备节点实战:从内核配置到用户空间spidev3.0测试

1. RK3566 SPI开发基础与实战意义

SPI总线作为嵌入式系统中最常用的通信协议之一,在RK3566这类高性能处理器上的应用尤为广泛。我最近在立创泰山派开发板上折腾SPI设备时,发现很多新手都会卡在内核配置和用户空间测试的衔接环节。这篇文章就来手把手带你打通这个全流程。

先说说为什么需要掌握SPI设备节点的配置。当你需要连接显示屏、传感器(比如IMU或环境传感器)、存储设备(如SPI Flash)时,SPI往往是首选方案。相比I2C,它的传输速率更高(RK3566的SPI3控制器最高支持50MHz时钟);相比并行总线,它又节省引脚资源。在实际项目中,我遇到过需要同时控制多个SPI从设备的情况,这时候理解设备树配置就尤为重要。

RK3566的SPI控制器有多个实例(SPI0-SPI3),其中SPI3是最常用的通用接口。内核自带的spidev驱动让我们可以像操作普通文件一样操作SPI设备,极大简化了开发流程。不过要注意的是,生产环境中建议使用专用驱动而非spidev,后者更适合快速原型开发。

2. SPI核心概念快速掌握

2.1 四线制与工作模式

SPI通信最基础的就是要理解四根信号线:

  • SCLK:时钟信号,由主设备产生
  • MOSI:主设备输出,从设备输入
  • MISO:主设备输入,从设备输出
  • CS:片选信号(低电平有效)

我常用一个水管工的比喻来理解:SCLK就像水泵的脉冲,MOSI和MISO是双向水管,CS则是决定哪个水龙头打开。这种全双工通信方式让数据可以同时进出,实测在RK3566上传输效率能达到I2C的3-5倍。

2.2 极性与相位详解

CPOL和CPHA这两个参数决定了数据采样的时机:

  • CPOL=0:时钟空闲时为低电平
  • CPOL=1:时钟空闲时为高电平
  • CPHA=0:在第一个时钟边沿采样
  • CPHA=1:在第二个时钟边沿采样

最常用的模式是Mode 0(CPOL=0, CPHA=0),这也是大多数传感器的默认模式。我在调试OLED屏时就遇到过模式不匹配导致花屏的问题,后来用逻辑分析仪抓取信号才发现从设备要求Mode 3。

2.3 时钟频率与数据位宽

RK3566的SPI控制器支持灵活的配置:

  • 时钟频率可从几KHz到50MHz
  • 数据位宽支持4-32bit(常用8bit)
  • 支持DMA传输减轻CPU负担

需要注意的是,实际速率受限于PCB布线质量。我曾在20MHz以上速率遇到信号完整性问题,通过缩短走线长度和添加端接电阻解决。

3. 设备树配置实战

3.1 定位设备树文件

在立创泰山派的SDK中,设备树文件通常位于:

kernel/arch/arm64/boot/dts/rockchip/tspi-rk3566-user-v10-linux.dts

建议先备份原文件,我吃过没备份的亏——一次错误的修改导致系统无法启动,最后只能重新烧录固件。

3.2 SPI控制器节点配置

找到或添加SPI3控制器节点:

spi3: spi@fe640000 { compatible = "rockchip,rk3066-spi"; reg = <0x0 0xfe640000 0x0 0x1000>; interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru CLK_SPI3>, <&cru PCLK_SPI3>; clock-names = "spiclk", "apb_pclk"; dmas = <&dmac0 26>, <&dmac0 27>; dma-names = "tx", "rx"; pinctrl-names = "default", "high_speed"; pinctrl-0 = <&spi3m0_cs0 &spi3m0_pins>; pinctrl-1 = <&spi3m0_cs0 &spi3m0_pins_hs>; status = "disabled"; };

关键参数说明:

  • reg:控制器寄存器物理地址和范围
  • interrupts:中断号配置
  • pinctrl-0:默认引脚配置组
  • dmas:启用DMA传输通道

3.3 启用SPI设备节点

在设备树末尾添加:

&spi3 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&spi3m1_cs0 &spi3m1_pins>; spi_dev@0 { compatible = "rockchip,spidev"; reg = <0>; spi-max-frequency = <10000000>; status = "okay"; }; };

这里有几个容易踩坑的点:

  1. pinctrl-0必须与硬件实际连接引脚一致
  2. spi-max-frequency不要超过从设备规格
  3. reg = <0>表示使用CS0片选

4. 内核编译与烧写

4.1 编译配置检查

执行make menuconfig确保以下选项启用:

Device Drivers -> SPI support -> Rockchip SPI controller Device Drivers -> SPI support -> User mode SPI device driver

我建议同步开启CONFIG_SPI_DEBUG选项,这样可以通过dmesg查看详细的SPI传输日志。

4.2 编译与烧录

使用以下命令编译内核和设备树:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8

烧录生成的boot.img到开发板后,启动时应该能看到类似这样的内核日志:

[ 1.235678] spi-nor spi3.0: spi3.0 at 0MHz (mode 0)

5. 用户空间测试实战

5.1 验证设备节点

成功启动后,检查是否存在设备节点:

ls /dev/spidev3.0

可以用ioctl命令快速测试:

ioctl -l /dev/spidev3.0

5.2 使用内核测试工具

内核自带的spi-tools位于:

cd kernel/tools/spi make CROSS_COMPILE=aarch64-linux-gnu-

将生成的spidev_test拷贝到开发板运行:

./spidev_test -D /dev/spidev3.0 -s 1000000 -p "\x01\x02"

5.3 自定义测试程序

这里给出一个增强版的测试程序,支持模式切换和连续传输:

#include <stdio.h> #include <fcntl.h> #include <linux/spi/spidev.h> void spi_config(int fd, uint8_t mode, uint32_t speed) { int ret; ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); } void spi_loopback_test(int fd) { uint8_t tx[] = {0xAA, 0x55, 0xF0, 0x0F}; uint8_t rx[4] = {0}; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)tx, .rx_buf = (unsigned long)rx, .len = sizeof(tx), .delay_usecs = 0, .speed_hz = 1000000, .bits_per_word = 8, }; ioctl(fd, SPI_IOC_MESSAGE(1), &tr); printf("Sent: "); for(int i=0; i<sizeof(tx); i++) printf("%02X ", tx[i]); printf("\nReceived: "); for(int i=0; i<sizeof(rx); i++) printf("%02X ", rx[i]); printf("\n"); }

编译命令:

aarch64-linux-gnu-gcc -o spi_test spi_test.c

6. 高级调试技巧

6.1 逻辑分析仪抓包

推荐使用Saleae Logic Analyzer配合PulseView软件,连接时注意:

  • 探头接地要可靠
  • 采样率至少设为时钟频率的4倍
  • 使用协议解码器自动解析SPI数据

6.2 内核调试信息

查看SPI控制器寄存器:

cat /sys/kernel/debug/regmap/spi3/registers

监控传输统计:

cat /sys/kernel/debug/spi/spi3/stats

6.3 性能优化建议

  1. 启用DMA传输减少CPU占用
  2. 合理设置SPI时钟分频
  3. 批量传输代替单字节操作
  4. 使用ioctl的SPI_IOC_MESSAGE接口

在最近的一个项目中,通过优化SPI传输参数,我们将传感器数据采集效率提升了40%。关键点在于找到了设备支持的最高稳定时钟频率——不是所有设备都能跑在标称的最高频率下。

http://www.jsqmd.com/news/515232/

相关文章:

  • libcli:嵌入式轻量级CLI库原理与实战
  • BME280 I²C驱动开发实战:嵌入式传感器底层驱动与补偿算法
  • 新手必看!Granite-4.0-H-350M保姆级教程:一键搭建本地爬虫代码生成器
  • 单IO口控制双LED的硬件设计方法
  • 如何在Linux系统下快速搭建vaspkit1.5.1+Anaconda3计算环境
  • Java调用DeepSeek API中文乱码终极解决方案:从编码原理到实战修复
  • CH9329串口转键鼠实战:从硬件对接到HID指令解析
  • FlowState Lab游戏开发应用:自动生成剧情对话与关卡描述
  • Wiley期刊投稿返修实战:手把手教你搞定Response Letter和Graphical Abstract
  • Bugku SQL注入实战:绕过黑名单的5种骚操作(附完整Payload)
  • Adafruit LPS35HW气压温度传感器Arduino驱动详解
  • 三极管放大电路实战指南:共射、共集、共基接法怎么选?附华成英课件解析
  • 嵌入式软件工程师校招面试经验实录
  • SOONet模型C语言基础接口封装:嵌入式设备轻量级集成
  • 工业4.0数据枢纽:FreeSCADA开源监控系统的跨协议融合方案
  • 大模型Token计费揭秘:如何避免花冤枉钱,高效使用AI工具?
  • HeyGem数字人系统优化技巧:让生成的视频更自然、更专业
  • Nunchaku FLUX.1 CustomV3优化心得:如何调整提示词,让生成的人像光影更自然、细节更丰富
  • 【Dify企业级Rerank实战白皮书】:3大工业级重排序算法选型指南,92%的AI应用性能提升源自这一步优化
  • Hunyuan如何快速部署?镜像免配置一键启动教程
  • LVGL图片显示全攻略:从TF卡到GIF动画的5种实战方法(附代码)
  • CoPaw API接口详解与性能优化:提升高并发调用稳定性
  • Lingyuxiu MXJ LoRA创作引擎Web前端集成实战
  • 跨模态融合Transformer在多光谱目标检测中的技术深度解析与应用实践
  • ChatGLM3-6B模型服务网格化:Istio流量管理配置
  • 2026年靠谱的铜管冲孔机工厂推荐:铜管冲孔机品牌厂家推荐 - 品牌宣传支持者
  • 利用Wan2.1 VAE自动化生成产品包装设计初稿
  • 【CSAPP】-LinkLab实战:从ELF文件解析到链接漏洞利用
  • 【嵌入式】定时器采集编码器实验点
  • Leather Dress Collection 企业级应用:Java八股文面试题库自动生成与评估