Linux SPI 数据收发spi_transfer / spi_message 机制详解同步示例一、核心结构体基础1.struct spi_transfer单次传输片段描述一段连续收发缓冲区代表一次单向/双向数据移位操作。structspi_transfer{constvoid*tx_buf;// 发送缓冲区NULL只收不发void*rx_buf;// 接收缓冲区NULL只发不收unsignedlen;// 收发字节长度tx/rx长度一致unsignedcs_change:1;// 传输完是否拉高片选unsigneddelay_usecs;// 本次传输后延时u32 speed_hz;// 本次单独时钟覆盖spi_device默认u8 bits_per_word;// 位宽覆盖默认u8 tx_nbits;// MOSI线数单线/双线u8 rx_nbits;// MISO线数};关键规则tx_buf/rx_buf可单独置空tx非空、rx0只写rx非空、tx0只读都不为空全双工同时收发len是字节总数tx/rx长度必须相同cs_change1该transfer结束后释放片选默认整条message只首尾拉/拉高CS。2.struct spi_message一整笔SPI事务把多个连续spi_transfer打包为一次完整SPI操作全程片选保持低电平仅事务结束释放。structspi_message{structlist_headtransfers;// transfer链表structspi_device*spi;// 所属spi设备unsignedstatus;// 执行状态 0成功负数错误码void(*complete)(void*context);// 异步完成回调void*context;unsignedactual_length;// 实际传输字节};事务执行流程内核拉低CS依次执行链表内所有spi_transfer全部transfer执行完毕后拉高CS同步阻塞等待全部完成返回异步直接返回完成触发complete回调。3. 层级关系图spi_message完整事务CS全程选中 ├─ spi_transfer1写指令 ├─ spi_transfer2读数据 └─ spi_transfer3写寄存器二、同步 vs 异步传输区别同步传输spi_sync()调用进程阻塞直到整条message传输完毕简单易写90%普通外设Flash、OLED、传感器优先使用适合probe、read/write系统调用上下文接口原型intspi_sync(structspi_device*spi,structspi_message*msg);返回值0成功负数错误码。异步传输spi_async()调用立即返回不阻塞线程传输完成后执行message-complete回调函数适合高速大数据、DMA大批量传输LCD、摄像头注意回调运行在中断上下文不能睡眠、不能调用阻塞接口。三、内核封装简易同步API日常优先用内核基于spi_sync封装好简易接口不用手动构造message/transferspi_write_then_read()最常用先发指令再读数据Flash、传感器标准操作intspi_write_then_read(structspi_device*spi,constvoid*txbuf,unsignedn_tx,void*rxbuf,unsignedn_rx);spi_write()仅发送数据staticinlineintspi_write(structspi_device*spi,constvoid*buf,size_tlen)spi_read()仅读取数据staticinlineintspi_read(structspi_device*spi,void*buf,size_tlen)四、底层原生同步示例手动构造messagetransfer场景W25Q读取JEDEC ID先发送0x9F指令再读取3字节ID。#includelinux/spi/spi.h#includelinux/mutex// 互斥锁保护SPI并发访问staticDEFINE_MUTEX(spi_lock);staticintspi_read_jedec_id(structspi_device*spi,u8*id_buf){intret;structspi_messagemsg;structspi_transferxfer[2];// 两段传输发指令、读IDu8 tx_cmd0x9F;// W25读ID指令// 初始化spi_messagespi_message_init(msg);msg.spispi;// 第一段transfer只发送指令不接收memset(xfer[0],0,sizeof(xfer[0]));xfer[0].tx_buftx_cmd;xfer[0].rx_bufNULL;xfer[0].len1;spi_message_add_tail(xfer[0],msg);// 第二段transfer只接收数据不发送memset(xfer[1],0,sizeof(xfer[1]));xfer[1].tx_bufNULL;xfer[1].rx_bufid_buf;xfer[1].len3;spi_message_add_tail(xfer[1],msg);mutex_lock(spi_lock);retspi_sync(spi,msg);// 同步阻塞传输mutex_unlock(spi_lock);if(ret){dev_err(spi-dev,spi sync failed ret%d\n,ret);returnret;}return0;}代码流程说明spi_message_init()清空初始化消息构造2段transfer发指令、读数据spi_message_add_tail()将transfer挂入message链表spi_sync()提交给SPI控制器阻塞等待传输结束多线程操作SPI必须加互斥锁防止传输交叉错乱。五、spi_write_then_read 简化等效写法上面手动构造message的逻辑可一行简化功能完全一致u8 cmd0x9F;u8 id[3];intretspi_write_then_read(spi,cmd,1,id,3);内核内部自动封装spi_message、transfer、spi_sync开发优先使用该接口。六、纯发送、纯读同步示例1. 仅写操作发送单字节命令u8 cmd0x06;// W25写使能指令intretspi_write(spi,cmd,1);2. 仅读操作无前置指令u8 data[4];intretspi_read(spi,data,4);七、多段连续传输示例多transfer需求先发指令(1B) → 发地址(3B) → 读16字节数据全程CS不释放staticintspi_multi_xfer_demo(structspi_device*spi){intret;structspi_messagemsg;structspi_transferxfer[3];u8 tx_buf[4]{0x03,0x00,0x00,0x00};// 读指令地址u8 rx_buf[16]{0};spi_message_init(msg);msg.spispi;// xfer0:发送4字节指令地址xfer[0].tx_buftx_buf;xfer[0].len4;spi_message_add_tail(xfer[0],msg);// xfer1:接收16字节数据xfer[1].rx_bufrx_buf;xfer[1].len16;spi_message_add_tail(xfer[1]);// xfer2可选传输后拉高片选xfer[2].cs_change1;spi_message_add_tail(xfer[2],msg);mutex_lock(spi_lock);retspi_sync(spi,msg);mutex_unlock(spi);returnret;}八、关键机制总结分层模型spi_transfer最小数据分片spi_message完整一次CS选中的事务由多个transfer组成同步传输spi_sync阻塞适合绝大多数外设驱动开发成本最低简易封装接口spi_write_then_read覆盖90%寄存器读写场景优先使用多线程访问SPI必须互斥锁保护避免多条message交错执行一条message内部默认全程CS低仅全部transfer完成后释放单transfer末尾可通过cs_change提前释放片选。
企业数字化 ERP 产品动态
相关推荐
自媒体数据分析核心:如何搭建完整的作品特征体系 一、实验目的在实验7-1 清洗后的数据的基础上,使用助睿ETL完成以下两类特征的计算与存储:(1)标题特征与互动总数(更新明细表)计算互动总数(total_interaction likes favorites shares coins… · 2026/8/6 19:40:57
Three.js 粒子星空教程 粒子星空 Starry Sky ▶ 在线运行案例
案例合集: 三维可视化功能案例(threehub.cn)开源仓库github地址: https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么
ShaderMaterial 自定义… · 2026/8/31 21:02:03
聪明的小羊 一只小羊翻过了2个栅栏 fa{fe13f590lg6d46d0d0}考点:栅栏换位密码、杂项字符过滤。根据题目提示,是“栅栏密码”,又因为描述中有“2”,所以,key2. 使用在线工具: http://www.hiencode.com/railfence.html 得… · 2026/8/6 19:00:22
AI芯片选型:主频、TOPS、NPU与SoC全解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 10:42:55
从Boson NetSim实验拆解VLAN与RIP配置:交换机互联与路由表验证 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 10:42:43
能源互联网统一接入平台:CPS架构下的多协议适配与设备协同实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 10:42:36
除臭设备的技术底座:植物液、高能离子与光水离子对比 除臭设备的效果差异,本质来自技术路线不同。本文面向公共厕所、写字楼、火车站、飞机场、景点、休闲场所、酒店、商场等应用场所,从机理层面把三条路线讲清楚。一、植物液雾化:物理雾化加吸附机理是什么通过超高频物理振动,把植物… · 2026/9/24 10:42:36
闪击与围剿:Opus 5.5 刚登顶,OpenAI 就祭出 GPT-6 Sol 2026 年 9 月的生成式人工智能前沿,正在上演一场堪称教科书级的商业与技术攻防战。
9 月 22 日,Anthropic 正式推出旗舰模型 Claude Opus 5.5,凭借在终端自主编程基准 Terminal-Bench 4.0 上飙出 66.4% 的历史极值,不仅将两周前自… · 2026/9/24 10:42:30
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44