目次
リングバッファーとは
リングバッファーは、固定サイズのメモリー領域を循環的に利用するデータ構造です。配列は直線的であるため実際にリング状になっているわけではありません。これを書き込みポインターと読み出しポインターという2つのポインターを使用し、配列の末尾に達すると先頭に戻ることで循環的な動作を実現します。
主に通信データのバッファーとして用いられます。

リングバッファーの仕組み
リングバッファーはFIFO(First In First Out、先入れ先出し)の特性を持ちます。FIFOではデータを入れることをエンキュー、データを取り出すことをデキューと呼びます。
リングバッファーへの書き込み(エンキュー)が発生すると、書き込みポインターの位置にデータを格納し、書き込みポインターを1つ進めます(下図2、3、4、6)。そして読み出し(デキュー)が発生すると、読み出しポインターの位置にあるデータを取り出し、読み出しポインターを1つ進めます(下図5、7、8)。これを繰り返すのがリングバッファーです。書き込みポインターと読み出しポインターが同じ値のとき、データが空なのかフルデータなのかを識別する手段にさえ気を付けておけば、永遠にデータを循環処理できます。

あわせて読みたい


スタックメモリーの基礎 | 組み込み
スタックメモリーとは スタック(stack)メモリーとは関数の引数や戻り値、ローカル変数に関数の呼び出し元へ戻るためのアドレスを保存するために用いられます。スタッ...
リングバッファーの注意点
- バッファーサイズ以上のデータは保持できません。
- 読み出しより書き込みの方が速い場合、データを消失します。
サンプルコード (C言語)
ringbuffer.h
#ifndef RING_BUFFER_H
#define RING_BUFFER_H
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------------------------------------------------------
// include
//------------------------------------------------------------------------------
#include <stdbool.h>
#include <stdint.h>
//------------------------------------------------------------------------------
// define
//------------------------------------------------------------------------------
#define RING_BUFFER_SIZE (128) //!< リングバッファーのサイズ
//! リングバッファー
typedef struct
{
char buffer[RING_BUFFER_SIZE]; //!< バッファー
uint16_t head; //!< 記録データの先頭位置
uint16_t tail; //!< 記録データの終了位置
uint16_t count; //!< 記録データ数
} RING_BUFFER;
//------------------------------------------------------------------------------
// function
//------------------------------------------------------------------------------
void initRingBuffer(RING_BUFFER *rb);
bool isFull_RingBuffer(const RING_BUFFER *rb);
bool isEmpty_RingBuffer(const RING_BUFFER *rb);
uint8_t enqueueRingBuffer(RING_BUFFER *rb, char data);
uint8_t dequeueRingBuffer(RING_BUFFER *rb, char *data);
#ifdef __cplusplus
}
#endif
#endif
ringbuffer.c
/**
* @file ringbuffer.c
* @brief リングバッファー処理
*/
//------------------------------------------------------------------------------
// include
//------------------------------------------------------------------------------
#include "ringbuffer.h"
/** ----------------------------------------------------------------------------
* @brief リングバッファーの初期化
*
* @param [in,out] *rb : リングバッファー
*/
void initRingBuffer(RING_BUFFER *rb)
{
rb->head = 0;
rb->tail = 0;
rb->count = 0;
}
/** ----------------------------------------------------------------------------
* @brief バッファーが満杯かの確認
*
* @param [in] *rb : リングバッファー
* @retval true : 満杯
* @retval false : 空きあり
*/
bool isFull_RingBuffer(const RING_BUFFER *rb)
{
if (rb->count == RING_BUFFER_SIZE) {
return true;
} else {
return false;
}
}
/** ----------------------------------------------------------------------------
* @brief バッファーが空かの確認
*
* @param [in] *rb : リングバッファー
* @retval true : 空
* @retval false : データあり
*/
bool isEmpty_RingBuffer(const RING_BUFFER *rb)
{
if (rb->count == 0) {
return true;
} else {
return false;
}
}
/** ----------------------------------------------------------------------------
* @brief エンキュー (リングバッファーへの追加)
*
* @param [in,out] *rb : リングバッファー
* @param data : 追加データ
* @retval 0 : OK
* @retval 1 : NG (フル)
*/
uint8_t enqueueRingBuffer(RING_BUFFER *rb, char data)
{
if (isFull_RingBuffer(rb) == true) {
return 1;
}
rb->buffer[rb->tail] = data;
rb->tail = (rb->tail + 1) % RING_BUFFER_SIZE;
rb->count++;
return 0;
}
/** ----------------------------------------------------------------------------
* @brief デキュー (リングバッファーからの取得)
*
* @param [in,out] *rb : リングバッファー
* @param [out] *data : 取得データ
* @retval 0 : OK
* @retval 1 : NG (空)
*/
uint8_t dequeueRingBuffer(RING_BUFFER *rb, char *data)
{
if (isEmpty_RingBuffer(rb) == true) {
return 1;
}
*data = rb->buffer[rb->head];
rb->head = (rb->head + 1) % RING_BUFFER_SIZE;
rb->count--;
return 0;
}
サンプルコードの使用例
使用例代わりに、CppUTestによるテストコードを掲載しておきます。
#include "CppUTest/TestHarness.h"
#include "ringbuffer.h"
// clang-format off
TEST_GROUP(ringbuffer){
TEST_SETUP(){
}
TEST_TEARDOWN(){
}
};
// clang-format on
TEST(ringbuffer, Test_RingBuffer)
{
char data;
char buffer[50] = {0};
uint8_t ret;
uint16_t i;
RING_BUFFER rb;
initRingBuffer(&rb);
// 書き込んで読み出すテスト
data = 'A';
for (i = 0; i < 16; i++) {
ret = enqueueRingBuffer(&rb, data);
UNSIGNED_LONGS_EQUAL(ret, 0);
data++;
}
for (i = 0; i < 16; i++) {
ret = dequeueRingBuffer(&rb, &data);
UNSIGNED_LONGS_EQUAL(ret, 0);
buffer[i] = data;
}
STRNCMP_EQUAL(buffer, "ABC", 3);
// リングバッファーを1周させる
data = 'A';
for (i = 0; i < RING_BUFFER_SIZE; i++) {
ret = enqueueRingBuffer(&rb, data);
UNSIGNED_LONGS_EQUAL(ret, 0);
}
for (i = 0; i < RING_BUFFER_SIZE; i++) {
ret = dequeueRingBuffer(&rb, &data);
UNSIGNED_LONGS_EQUAL(ret, 0);
}
// 1周後の書き込み/読み出しテスト
data = 'a';
for (i = 0; i < 16; i++) {
ret = enqueueRingBuffer(&rb, data);
UNSIGNED_LONGS_EQUAL(ret, 0);
data++;
}
for (i = 0; i < 16; i++) {
ret = dequeueRingBuffer(&rb, &data);
UNSIGNED_LONGS_EQUAL(ret, 0);
buffer[i] = data;
}
STRNCMP_EQUAL(buffer, "abc", 3);
}
TEST(ringbuffer, Test_FullRingBuffer)
{
char data;
uint8_t ret;
uint16_t i;
RING_BUFFER rb;
initRingBuffer(&rb);
data = 'A';
for (i = 0; i < RING_BUFFER_SIZE; i++) {
ret = enqueueRingBuffer(&rb, data);
UNSIGNED_LONGS_EQUAL(ret, 0);
}
ret = enqueueRingBuffer(&rb, data);
UNSIGNED_LONGS_EQUAL(ret, 1);
}
TEST(ringbuffer, Test_EmptyRingBuffer)
{
char data;
uint8_t ret;
uint16_t i;
RING_BUFFER rb;
initRingBuffer(&rb);
data = 'A';
for (i = 0; i < 16; i++) {
ret = enqueueRingBuffer(&rb, data);
UNSIGNED_LONGS_EQUAL(ret, 0);
}
for (i = 0; i < 16; i++) {
ret = dequeueRingBuffer(&rb, &data);
UNSIGNED_LONGS_EQUAL(ret, 0);
}
ret = dequeueRingBuffer(&rb, &data);
UNSIGNED_LONGS_EQUAL(ret, 1);
}
コメント