环形缓冲区(Ring Buffer)
文章目录1. 环形缓冲区工作流程2. C 语言实现2.1 头文件 ringbuf.hRINGBUF_SIZE2562.2 源文件 ringbuf.c3. 在串口中断服务函数中使用示例4. 临界区保护说明5. 总结1. 环形缓冲区工作流程写入流程 (ISR中调用)写入流程 (ISR中调用)未满已满接收中断触发读取数据寄存器缓冲区是否满?写入数据到 head 位置head (head 1) % BUFFER_SIZE返回可选: 覆盖/丢弃记录溢出标志读取流程 (主循环/任务)读取流程 (主循环/任务)非空空应用需要读取数据缓冲区是否空?从 tail 位置读取数据tail (tail 1) % BUFFER_SIZE返回读取的字节返回 0 或错误码缓冲区逻辑head指向下一个可写入位置。tail指向下一个可读取位置。当head tail时缓冲区为空。当(head 1) % size tail时缓冲区为满保留一个字节用于区分空/满。2. C 语言实现2.1 头文件 ringbuf.hRINGBUF_SIZE256#ifndefRINGBUF_H#defineRINGBUF_H#includestdint.h#includestdbool.h/* 缓冲区大小需为 2 的幂时可用位掩码优化这里用通用取模方式 */#defineRINGBUF_SIZE256/* 环形缓冲区控制结构 */typedefstruct{uint8_tbuffer[RINGBUF_SIZE];/* 数据存储区 */volatileuint32_thead;/* 写指针ISR 修改 */volatileuint32_ttail;/* 读指针主循环修改 */volatileuint32_toverflow_cnt;/* 溢出计数器 */}ringbuf_t;/* 初始化 */voidringbuf_init(ringbuf_t*rb);/* 写入一个字节通常在中断中调用 */boolringbuf_put(ringbuf_t*rb,uint8_tdata);/* 读取一个字节非阻塞返回实际读取字节数 */intringbuf_get(ringbuf_t*rb,uint8_t*data);/* 读取多个字节 */uint32_tringbuf_read(ringbuf_t*rb,uint8_t*out,uint32_tmax_len);/* 获取当前可读字节数 */uint32_tringbuf_available(constringbuf_t*rb);/* 获取空闲空间大小 */uint32_tringbuf_free_space(constringbuf_t*rb);/* 清空缓冲区 */voidringbuf_clear(ringbuf_t*rb);/* 检查是否为空 */boolringbuf_is_empty(constringbuf_t*rb);/* 检查是否为满 */boolringbuf_is_full(constringbuf_t*rb);/* 获取溢出次数 */uint32_tringbuf_get_overflow(ringbuf_t*rb);#endif/* RINGBUF_H */2.2 源文件 ringbuf.c#includeringbuf.h/* 初始化缓冲区 */voidringbuf_init(ringbuf_t*rb){rb-head0;rb-tail0;rb-overflow_cnt0;}/* 检查是否为空 */boolringbuf_is_empty(constringbuf_t*rb){return(rb-headrb-tail);}/* 检查是否为满保留一个字节 */boolringbuf_is_full(constringbuf_t*rb){return(((rb-head1)%RINGBUF_SIZE)rb-tail);}/* 获取可读字节数 */uint32_tringbuf_available(constringbuf_t*rb){/* 注意head 和 tail 都是 volatile需整体读取一次 */uint32_thrb-head;uint32_ttrb-tail;if(ht){returnh-t;}else{returnRINGBUF_SIZE-th;}}/* 获取空闲空间大小 */uint32_tringbuf_free_space(constringbuf_t*rb){returnRINGBUF_SIZE-1-ringbuf_available(rb);}/* 写入一个字节中断安全但若中断嵌套需临界区保护 */boolringbuf_put(ringbuf_t*rb,uint8_tdata){if(ringbuf_is_full(rb)){rb-overflow_cnt;returnfalse;/* 缓冲区满写入失败 */}rb-buffer[rb-head]data;/* 写指针递增需确保操作原子性部分架构需关中断 */rb-head(rb-head1)%RINGBUF_SIZE;returntrue;}/* 读取一个字节返回实际读取字节数0 或 1 */intringbuf_get(ringbuf_t*rb,uint8_t*data){if(ringbuf_is_empty(rb)){return0;}*datarb-buffer[rb-tail];rb-tail(rb-tail1)%RINGBUF_SIZE;return1;}/* 批量读取数据 */uint32_tringbuf_read(ringbuf_t*rb,uint8_t*out,uint32_tmax_len){uint32_tavailringbuf_available(rb);uint32_tread_len(max_lenavail)?max_len:avail;uint32_ti;for(i0;iread_len;i){/* 此处未使用 ringbuf_get 以避免重复计算可用长度 */out[i]rb-buffer[rb-tail];rb-tail(rb-tail1)%RINGBUF_SIZE;}returnread_len;}/* 清空缓冲区 */voidringbuf_clear(ringbuf_t*rb){/* 为保证中断安全应先关中断 */rb-tailrb-head;}/* 获取溢出次数 */uint32_tringbuf_get_overflow(ringbuf_t*rb){returnrb-overflow_cnt;}3. 在串口中断服务函数中使用示例以下展示如何将环形缓冲区集成到 USART 接收中断中以 STM32 HAL 为例原理通用。#includeringbuf.h#includestm32f1xx_hal.h/* 全局环形缓冲区实例 */ringbuf_tuart_rx_ringbuf;/* 初始化 */voiduart_init(void){ringbuf_init(uart_rx_ringbuf);// ... 配置 USART 并使能接收中断 ...HAL_UART_Receive_IT(huart1,rx_byte,1);}/* 接收中断回调HAL 库方式 */voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){if(huart-InstanceUSART1){uint8_tdatahuart-Instance-DR;/* 读取数据寄存器 */ringbuf_put(uart_rx_ringbuf,data);/* 存入环形缓冲区 */HAL_UART_Receive_IT(huart1,rx_byte,1);/* 重新开启中断 */}}/* 主循环中读取数据 */voidmain_loop(void){uint8_tbuf[64];uint32_tlen;while(1){lenringbuf_read(uart_rx_ringbuf,buf,sizeof(buf));if(len0){/* 处理收到的数据 */process_data(buf,len);}// ... 其他任务 ...}}4. 临界区保护说明在中断与主循环共享head和tail时以下操作需注意原子性写操作head递增前可能被中断打断若中断也写缓冲区会导致数据覆盖。解决方案在修改head前关中断修改后恢复或在单写单读且指针为整型时利用天然原子性但依赖架构。读操作tail的修改可能被中断读取到不一致的中间状态。解决方案在ringbuf_read循环中如果担心中断干扰可在读head和tail时关中断或使用内存屏障。简单示例以 ARM Cortex-M 为例boolringbuf_put_safe(ringbuf_t*rb,uint8_tdata){bool ret;__disable_irq();retringbuf_put(rb,data);__enable_irq();returnret;}5. 总结使用环形缓冲区能有效缓冲串口突发数据避免丢失。代码采用“保留一个字节”的方式区分空/满逻辑清晰。流程图直观展示写入和读取的决策流程。实际工程中需根据平台增加临界区保护。若需支持动态大小或使用 2 的幂次方大小以位运算加速可进一步优化。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2504615.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!