尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

环形队列 SPI 异步通信思路总结

发布时间:2026/9/24 17:14:58

资讯中心
01
ARTICLE

环形队列 SPI 异步通信思路总结

环形队列 SPI 异步通信思路总结
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();}仅展示思路
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。