topmenu

トップコンピュータカメラ雑記プロフィールこのページについて

2018/11/30

リングバッファモドキの実装

前置き

バッファは、一時的になにかを蓄える場所のことである。
機械では、緩衝器のことであり、油圧、空気などをためておく場所を指し、
化学では中和するという意味である。
コンピュータでは、データを蓄えるメモリのことである。

そもそもなぜバッファが必要なのかといえば、
例えば、「データを得て、データに対して何らかの処理を行う。」とする。
しかし、その処理の演算時間が長くなると、データを得るという処理が間に合わなくなってしまいとりこぼしてしまう。そのため、ある程度データをバッファにためておいて、あとでまとめて処理する。

「データを得て、データに対して何らかの処理を行う。」はバッファを用意した場合、
「データをバッファに蓄えて、バッファから取り出したデータに対して何らかの処理を行う。」ことになる。
つまり、「データをバッファに蓄える」、「バッファから取り出したデータに対して何らかの処理をする」の2つに分離できる。前者を行いつつも、後者を適時行えばよい。

ただし、単純なコンピュータでは、一つの処理しかできなく、同時に何かをするということはできない。
もし、同時に何かしようと思うと、2つのコンピュータ(CPU)を用意するしかない。
そのため、実際には、複数の処理を高速に切り替えながら行っているにすぎない。
この処理を切り替える行為は、かなり強引に言えば、ハードウェア的には割込み、ソフトウェア的にはタスクによって行う(われている)。

Arduino は、組込みコンピュータの周辺機器に楽にアクセスできるライブラリ群であるが、
バッファは、Serial クラス内に存在する(と思われる,実際に読んでいない)。
具体的には、シリアルポートからデータが受信されると、バッファに蓄えられる。
このため、いつデータが受信されようとも気にしないでよく、データを取り出して処理することだけに注視できる。
タスクは、基本的にサポートされていないので、作るか、別途ライブラリを探して入れるしかない。

バッファの仕方はいろいろあり、どうメモリが蓄えられるか、どう取り出されるか、用意されたバッファのためのメモリを超えたらどうなるかによって、分類できる。
リングバッファというのは、FIFO(First In First Out) 方式である。
FIFO は、バッファの先頭に新しいデータを入れて、そのデータを取り出すときは先頭から取り出すという意味である。
リングは輪であるが、輪のように循環することを表し、バッファとして用意されたメモリ上限を超えてデータが蓄えられると、
バッファメモリの初期位置に戻るということを指す。もちろん上限を超えると、どんどん上書きされていく。
プログラム的に言うと、バッファは配列でとられるが、用意された配列の大きさを超えてしまう前に[0] の位置に戻すことになることで輪つなぎ(リング)となる。

本題

そもそもなぜ作ろうと思ったのかというと、前にNTPサーミスタで温度を測るプログラムを記事にしたときにもすこし書いたが、デジタルフィルタを使いたいがためだ。
AD 変換器で得たデータに対して、デジタルフィルタを通そうとするときに、配列は大きく取れないので、バッファをデータのプールとして使うしかなくなる。
また、デジタルフィルタするにはデータ取得の時間を確定する必要があるので、
バッファメモリにデータを入れたほうが扱いやすいと思われる。

プログラム

どんなものを実装するかは、C++ の Boost のリファレンスなどを参考にすればよい。
が、モドキなので、使う予定の AD 変換値のバッファとして必要そうなものだけを実装して
(バッファ最初の基点から左の要素に代入するメソッド[関数]は不要、 head, tail などの用語は使わない)、
リング状になるように配列を処理するというだけにした。
せっかくの C++ らしく記述したいが、また、C 言語だけになった。不格好であるが、以下に貼っておく。
(バッファの型を変更できるようにテンプレートを使って再度、実装しようかな・・・)

/*
 * PRETEND Circular buffer(Ring buffer).
 * (c) Flapper @ decentdress.blogspot.com
 */
#define AD_BUF_SIZE 10
typedef struct _circler_buff {
    const size_t size;
    int idx;
    int dat[AD_BUF_SIZE];
} CIRCULAR_BUFF;

#define DECLARE_CIRCULAR_BUFFER(x) CIRCULAR_BUFF x = {AD_BUF_SIZE}

void c_buff_push(CIRCULAR_BUFF *ps, const int data)
{
    if (ps->idx < ps->size) {
        ps->dat[ps->idx++] = data;

    } else {
        ps->idx = 0;
        ps->dat[ps->idx] = data;
    }
}

int c_buff_pop(CIRCULAR_BUFF *ps)
{
    if (ps->idx == 0) {
        ps->idx = ps->size - 1;
        return ps->dat[ps->idx];
    } else {
        return ps->dat[--(ps->idx)];
    }
}

void c_buff_clear(CIRCULAR_BUFF *ps)
{
    size_t i;
    for (i = 0; i < ps->size; i++) {
        ps->dat[i] = 0;
    }
}

void c_buff_init(CIRCULAR_BUFF *ps)
{
    c_buff_clear(ps);
    ps->idx = 0;
}

int c_buff_get_data(CIRCULAR_BUFF *ps)
{
    if (ps->idx == 0) {
        return ps->dat[ps->size-1];
    } else {
        return ps->dat[ps->idx-1];
    }
}

// バッファの宣言
CIRCULAR_BUFF ad = { AD_BUF_SIZE }; // or DECLARE_CIRCULAR_BUFFER(ad);
// てきとうにデータをバッファに入れる
for (int i = 0; i < 10; i++) {
    c_buff_push(&ad, 100+(i*10));
}
// バッファから最新の値を取り出す
int data = c_buff_get_data(&ad);