1. 设计背景与问题在嵌入式系统中SPI 通常是同步外设但业务层APPLICATION不希望被慢速传输阻塞。直接逐字节收发会带来两个问题阻塞等待APPLICATION 发起读/写后只能死等传输完成浪费 CPU 时间。请求堆积当 APPLICATION 的请求频率高于 SPI 实际传输速度时后续请求会丢失或被覆盖。环形队列方案的核心思路是把「请求产生」和「请求消费」解耦APPLICATION 只负责把请求压入队列之后立即返回SPI 传输由底层驱动通过 DMA/中断逐步消费队列完成后通过回调通知上层。这样既能异步化又能用环形缓冲区安全地缓存排队中的请求。2. 整体架构与流程图整体数据流可以概括为「生产者—环形队列—消费者」模型消费者底层驱动 SPI环形队列生产者/消费者共享生产者APPLICATION① APPLICATION发起 SPI 读/写请求② 请求压入环形队列临界区保护Count1③ 请求数据进入 FIFOHead/Tail 推进④ StartTransfer 触发传输⑤ 进入 SPI DMA / IRQ 传输⑥ 传输完成触发 callback⑦ StartNext 取出下一个请求临界区保护Count-1图说明整个链路从 APPLICATION 发起请求开始经环形队列缓存后由驱动消费传输完成回调后驱动再从队列取出下一个请求形成闭环。G 回到 B 表示「环形队列持续被消费」直到队列为空才停止。3. 环形队列示意图环形队列的关键是「头指针Head指向下一个要消费的位置尾指针Tail指向下一个可写入的位置」。写入推进 Tail消费推进 Head环形缓冲区容量 Nslot 0slot 1slot 2slot 3slot 4Head消费指针Tail写入指针说明Head 到 Tail 之间的区域是已压入但尚未消费的数据。为了更直观地管理队列状态可以在 Head/Tail 之外再增加一个计数变量Count每次入队时Count加 1每次出队时Count减 1。这样队空即可表示为Count 0队满即可表示为Count NN 为队列容量避免 Head 与 Tail 相等时无法区分「队空」和「队满」。同时对Count、Head、Tail的修改必须放在临界区中完成防止中断和多个任务同时访问导致计数与指针状态错乱。4. 流程分步讲解下面逐条说明这 7 步在工程中分别做了什么① APPLICATION 请求 SPI 读/写业务代码调用统一的 API例如Spi_AsyncRead(buffer, len)/Spi_AsyncWrite(buffer, len)只描述「要做什么」不关心底层传输细节。② 请求压入队列API 内部把本次请求封装为一个描述符目标地址、长度、读写标志、回调函数等尝试压入环形队列若队列未满写入 Tail 位置Tail 前移函数立即返回。若队列已满根据策略返回错误如QUEUE_FULL或进入等待。③ 请求数据进入 FIFO这里的 FIFO 既指环形队列本身的先进先出语义也指请求描述符按顺序进入缓冲区。因为队列遵循先进先出APPLICATION 的请求顺序会被严格保留保证传输顺序与业务提交顺序一致。④ StartTransfer 触发传输当队列从「空」变为「非空」时驱动调用StartTransfer()启动一次 SPI 传输。这是异步链路的关键起点只有检测到有货且硬件空闲时才真正开启传输。⑤ 进入 SPI DMA / IRQ 开始传输底层通过DMA或SPI 中断完成实际数据搬运。DMA 方式适合大数据块搬运期间 CPU 可以继续处理其他事务中断方式适合小数据或需要逐字节处理的场景。传输期间不占用 APPLICATION 的阻塞调用。⑥ 传输完成触发 callback传输完成DMA 完成中断 / SPI 收发完成中断后驱动调用该请求描述符里的回调函数把传输结果成功/失败、实际长度通知给 APPLICATION。回调应在中断上下文或低优先级任务中谨慎处理避免在中断里做过重操作。⑦ StartNext 传输回调处理完成后驱动检查队列中是否还有未处理的请求若有下一个请求取出并再次调用StartTransfer()回到第 ④ 步形成连续流水。若队列为空硬件进入空闲状态等待下一次 APPLICATION 写入时再次触发。5. 关键实现要点原子性保护Head、Tail指针以及计数变量Count的读写都必须在临界区内完成。APPLICATION生产者入队时会执行Count中断或驱动消费者出队时会执行Count--如果两类操作同时发生且不加以保护就会造成计数错乱、队列状态被破坏。典型做法是进入临界区前关闭中断或使用互斥锁操作完成后立即释放。队满/队空判定引入Count后可以更直观地判断状态Count 0表示队空Count N表示队满。相比「保留一个空位」的方式这种方案不浪费队列空间但必须保证Count的增减与Head/Tail的推进在同一个临界区里原子完成。入队判定APPLICATION 写入前先判断队列是否已满若Count N则本次请求不能入队根据策略返回错误如QUEUE_FULL或等待避免覆盖尚未消费的数据。回调上下文回调应尽量短小如需复杂处理可投递到工作队列或任务中避免拖慢中断响应。传输状态机空闲IDLE→ 传输中BUSY→ 完成回调DONE→ 取下一条状态清晰便于调试。## 6. 小结环形队列 SPI 异步通信的本质是用环形缓冲区充当生产者和消费者之间的缓冲让 APPLICATION 以非阻塞方式提交请求让底层驱动以「完成即取下一个」的方式持续消费。其中 ① 到 ③ 是「入队」④ 到 ⑦ 是「出队并传输」两者通过环形队列衔接实现高效、顺序、非阻塞的 SPI 通信。环形异步通信伪代码Std_ReturnTypeAsync_AeSpi_WriteReg(uint32 RegAddr,uint8 DataWidth,uint32 Data,AsyncSpi_ReqIdType SpiReqId){/* Step1: Build SPI Frame */SuspendOSInterrupts();/* Step2: Push Into Spi Queue *//* Step3: Actively request SPI Transmit, if an ongoing transfer is detected, push the request into the request queue */ResumeOSInterrupts();/* Step4: Reset the request buffer s_AsyncSpiRequestData */AsyncResetAsyncSpiRequestData();}Std_ReturnTypeAsync_AeSpi_ReadReg(uint32 RegAddr,uint8 DataWidth,uint32*RegValPtr,AsyncSpi_ReqIdType SpiReqId){/* Step1: Build SPI Frame */SuspendOSInterrupts();/* Step2: Push Into Spi Queue *//* Step3: Actively request SPI Transmit, if an ongoing transfer is detected, push the request into the request queue */ResumeOSInterrupts();/* Step4: Reset the request buffer s_AsyncSpiRequestData */AsyncResetAsyncSpiRequestData();}仅展示思路
企业数字化 ERP 产品动态
相关推荐
Spring Cloud Alibaba Nacos配置中心 一、前言
上一节Spring Cloud Alibaba Nacos注册中心记录了Nacos作为注册中心的使用方式,这节继续记录下Nacos作为配置中心的使用方式。
二、框架搭建
新建一个Spring Boot项目,artifactId为spring-cloud-alibaba-nacos-config,项目的pom内… · 2026/9/24 17:14:49
Spring Cloud Alibaba Nacos注册中心 一、前言
Nacos是一款集服务发现、服务健康监测、动态配置服务、动态 DNS 服务、服务及其元数据管理于一身的开源软件,这节主要记录Nacos的服务注册发现功能的使用。借助Spring Cloud Alibaba Nacos Discovery,我们可以轻松地使用Spring Cloud编程模型体… · 2026/9/24 17:14:49
【在线五子棋对战】实用工具类模块实现 目录 日志类
strftime()
宏封装
声明全局Logger对象
MySQL API封装
Jsoncpp的序列化/反序列化封装
字符串分割封装
读取文件封装
密码模块
二进制(字节)数据转十六进制字符串
生成随机盐
计算密码哈希
密码验证 日志类
日志类完成的功能是对… · 2026/9/24 17:14:49
高斯Copula传递熵分析相位数据:原理与Matlab实现 做信号分析的人应该都遇到过这种尴尬:手里拿到的是一串相位序列,比如脑电的瞬时相位、转子振动的相位角、或者气象里的周期节律。你想判断两个通道之间到底谁在驱动谁,第一时间想到传递熵(Transfer Entropy)࿰… · 2026/9/24 20:12:48
用DeepSeek Flash和MCP协议在网页聊天框里操控Blender建模 最近我把大部分业余时间砸在了一件有点"偏门"但很有意思的事情上:用一个HTML网页聊天框,接上DeepSeek Flash模型,再通过MCP协议去直接控制Blender建模。说得直白一点,就是我不用鼠标抠Blender菜单,也不用自己… · 2026/9/24 20:12:48
Apache IoTDB TTL 实践:时序数据保留与数据生命周期管理 Apache IoTDB 的 TTL 功能我用了快三年,从最初只是给测试环境设个自动清理,到后来把整个生产集群的数据保留策略都搭在它上面,中间踩过不少坑,也把官网文档翻了个底朝天。这篇文章就围绕“时序数据库的数据保留时间管理”这件事&a… · 2026/9/24 20:12:48
PentAGI沙箱设计:用Docker隔离打造安全的自主渗透测试Agent 1. 先聊清楚:PentAGI 到底想解决什么问题如果你的渗透测试 Agent 有了“手”,第一件事很可能就是拆掉自己所在的机房。这不是段子,而是所有做 AI 安全工具的人迟早要面对的现实。PentAGI 这类全自主渗透测试 Agent,和市面上那些“… · 2026/9/24 20:12:48
葡萄数据集YOLO训练实战:从标签解析到过拟合避坑指南 简介:面向YOLO系列目标检测的初学者与算法工程师,这份葡萄图像数据集包含拣选点、斑点葡萄、腐烂葡萄、成熟葡萄、未成熟葡萄五类目标,共165张已标注图像。数据集已完成训练集与验证集划分,并附带可直接读取的data.yaml配置文件&a… · 2026/9/24 20:12:48
基于Python的微博数据可视化分析系统设计与实现 1. 项目定位与整体技术选型:一套可交付的毕设需要什么每年到了三四月份,就会有一批同学来问我同一个问题:师兄,Python大数据的毕设到底能做什么?说句实话,这个方向确实被写烂了,但大多数人交上来… · 2026/9/24 20:12:42
基于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