循环队列通用模版

循环队列:

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(已如此)。
 */

 

注意!!!如果存在中断入队的情况记得加上开关中断,那么问题来了,如果不开关中断的话有没有办法不出错呢?

所以他来了! 

 

posted @ 2025-11-05 18:34  西北小蚂蚁  阅读(11)  评论(0)    收藏  举报