循环队列通用模版
循环队列:
RingQ.h
#ifndef RINGQ_H #define RINGQ_H #include <stdint.h> #include <stddef.h> #include "stm32g4xx_hal.h" #ifndef RINGQ_DEFAULT_CAP #define RINGQ_DEFAULT_CAP (2048U) // 默认 2KB #endif // 可选:RTOS/中断临界区(按需在编译单元里重定义) #ifndef RINGQ_ENTER_CRITICAL #define RINGQ_ENTER_CRITICAL() __disable_irq() #endif #ifndef RINGQ_EXIT_CRITICAL #define RINGQ_EXIT_CRITICAL() __enable_irq() #endif #ifdef __cplusplus extern "C" { #endif // 基础循环队列核心(把它放进你自己的结构体即可) typedef struct { uint8_t *buf; // 缓冲区 uint32_t cap; // 容量(字节) volatile uint32_t head; // 写索引 [0..cap-1] volatile uint32_t tail; // 读索引 [0..cap-1] volatile uint32_t size; // 已用字节数 [0..cap] } RingQ; // ① 注册/初始化:绑定缓冲并清零 void RingQ_Register(RingQ *q, void *buf, uint32_t cap); // ② 写入:把 src 的 len 字节写入队列;返回实际写入(空间不足则部分写入) uint32_t RingQ_Write (RingQ *q, const void *src, uint32_t len); // ③ 读取:从队列读最多 want_len 字节到 dst;返回实际读到的字节数 uint32_t RingQ_Read (RingQ *q, void *dst, uint32_t want_len); // 便捷宏:静态声明一个队列与其缓冲,并提供 init #define RINGQ_DECLARE(name, capacity) \ static uint8_t name##_buf[(capacity)]; \ static RingQ name; \ static inline void name##_init(void){ \ RingQ_Register(&name, name##_buf, (capacity)); \ } #ifdef __cplusplus } #endif #endif // RINGQ_H
RingQ.c
#include "ringq.h" #include <string.h> #include <stdint.h> /*====================== 内存屏障 ======================*/ #if defined(__arm__) || defined(__thumb__) #if defined(__GNUC__) #define RINGQ_DMB() __asm__ __volatile__("dmb ish" ::: "memory") #elif defined(__ARMCC_VERSION) #include <arm_acle.h> #define RINGQ_DMB() __DMB() #else #define RINGQ_DMB() __asm__ __volatile__("" ::: "memory") #endif #else #if defined(__GNUC__) || defined(__clang__) #define RINGQ_DMB() __asm__ __volatile__("" ::: "memory") #else #define RINGQ_DMB() do{}while(0) #endif #endif /*====================== 工具函数 ======================*/ static uint32_t _min_u32(uint32_t a, uint32_t b){ return (a < b) ? a : b; } /*====================== API 实现 ======================*/ void RingQ_Register(RingQ *q, void *buf, uint32_t cap) { if (!q || !buf || cap == 0) return; q->buf = (uint8_t*)buf; q->cap = cap; q->head = 0; q->tail = 0; q->size = 0; /* 兼容保留:不再使用 */ } /* * 写入(生产者/ISR): * - 不关中断;只写 head。 * - 先写数据,再 DMB,再提交 head。 */ uint32_t RingQ_Write(RingQ *q, const void *src_, uint32_t len) { if (!q || !src_ || q->cap == 0 || len == 0) return 0; const uint8_t *src = (const uint8_t*)src_; /* 快照游标(消费者只改 tail,32bit 对齐读是原子) */ uint32_t head = q->head; uint32_t tail = q->tail; uint32_t used = (uint32_t)(head - tail); uint32_t free = q->cap - used; uint32_t to_write = _min_u32(len, free); if (to_write == 0) return 0; /* 满了,按策略丢弃/返回0 */ uint32_t widx = head % q->cap; uint32_t first = _min_u32(to_write, q->cap - widx); if (first) memcpy(&q->buf[widx], src, first); uint32_t second = to_write - first; if (second) memcpy(&q->buf[0], src + first, second); /* 发布屏障:保证数据到达内存再“发布”新的 head */ RINGQ_DMB(); q->head = head + to_write; /* 提交 */ return to_write; } /* * 读取(消费者/Task): * - 不关中断;只写 tail。 * - 先读 head,再 DMB,再读数据,最后提交 tail。 */ uint32_t RingQ_Read(RingQ *q, void *dst_, uint32_t want_len) { if (!q || !dst_ || q->cap == 0 || want_len == 0) return 0; uint8_t *dst = (uint8_t*)dst_; uint32_t head = q->head; /* 观察生产者已提交上界 */ RINGQ_DMB(); /* 获取屏障:随后读到的数据与 head 一致 */ uint32_t tail = q->tail; uint32_t used = (uint32_t)(head - tail); uint32_t to_read = _min_u32(want_len, used); if (to_read == 0) return 0; /* 空 */ uint32_t ridx = tail % q->cap; uint32_t first = _min_u32(to_read, q->cap - ridx); if (first) memcpy(dst, &q->buf[ridx], first); uint32_t second = to_read - first; if (second) memcpy(dst + first, &q->buf[0], second); /* (可选)发布屏障:确保数据已被拷出再扩大空闲 */ RINGQ_DMB(); q->tail = tail + to_read; /* 提交 */ return to_read; } /* * 备注: * 1) 由于使用“单调递增的 head/tail”,可用容量==cap,不再需要“留1字节”来区分空满。 * 2) 如果需要阻塞/等待,可在上层根据返回0时采取等待或通知策略。 * 3) 若在极端高频 ISR 里担心 dmb 开销,可只在提交前后保留一次 dmb(已如此)。 */
注意!!!如果存在中断入队的情况记得加上开关中断,那么问题来了,如果不开关中断的话有没有办法不出错呢?
所以他来了!

浙公网安备 33010602011771号