記事一覧へ戻る

C言語2級リングバッファ

C言語初級 |

リングバッファ完全実装+トレース問題

なぜリングが必要か

  • バッファを使い回せる

  • 古いデータを上書きできる

  • 通信受信で必須構造


実装モデル(1バイト受信想定)

#define BUF_SIZE 5

int buf[BUF_SIZE];
int w = 0; // write
int r = 0; // read
int count = 0;

void push(int data){
if(count < BUF_SIZE){
buf[w] = data;
w = (w + 1) % BUF_SIZE;
count++;
}
}

int pop(void){
int data = -1;
if(count > 0){
data = buf[r];
r = (r + 1) % BUF_SIZE;
count--;
}
return data;
}

トレース問題

初期状態:w=0, r=0, count=0

操作:

  1. push(10)

  2. push(20)

  3. pop()

  4. push(30)

  5. push(40)

  6. pop()

操作wrcount返却
初期000-
push10101-
push20202-
pop21110
push30312-
push40413-
pop42220

割り込み競合(排他制御)の考え方

問題の本質

  • 割り込みとメインが同じ変数を触る

  • 途中で割り込まれると値が壊れる


危険例

count++; // 実際は load → add → store

途中で割り込みが入ると更新が消える。


対策1:割り込み禁止区間

disable_irq();
count++;
enable_irq();

→ クリティカルセクション


対策2:フラグ分離

  • 割り込み:書き込み専用

  • メイン:読み取り専用


試験・実務共通ポイント

  • 配列+インデックス更新は要注意

  • count, w, r は同時更新NG


通信プロトコル受信ステートマシン

想定フレーム

[0xAA][LEN][DATA...][CHK]

状態定義

#define WAIT_HEAD 0
#define WAIT_LEN 1
#define WAIT_DATA 2
#define WAIT_CHK 3

受信処理モデル

int state = WAIT_HEAD;
int len = 0;
int pos = 0;
int buf[10];

void rx_byte(int d){
switch(state){
case WAIT_HEAD:
if(d == 0xAA) state = WAIT_LEN;
break;
case WAIT_LEN:
len = d;
pos = 0;
state = WAIT_DATA;
break;
case WAIT_DATA:
buf[pos++] = d;
if(pos >= len) state = WAIT_CHK;
break;
case WAIT_CHK:
/* チェック処理 */
state = WAIT_HEAD;
break;
}
}

実務ポイント

  • switch + state で必ず書く

  • フレーム破損時はHEAD待ちに戻す


Watchdog 暴走リセット設計

目的

  • ソフト暴走時に自動リセット


基本設計

  • 正常周期でキック

  • キックされなければリセット


モデルコード

int wdCounter = 0;

void timer_interrupt(void){
wdCounter++;
}

int main(void){
while(1){
if(task_ok()){
wdCounter = 0; // キック
}
if(wdCounter > 5){
system_reset();
}
}
}

設計ミス例

  • 常にキック → 意味なし

  • 割り込みでキック → 暴走検出不可


実務で評価される設計視点

観点意味
状態管理switchで整理
排他制御壊れない設計
非同期処理割り込みと分離
フェイルセーフwatchdog

次のステップ(完全実務)

  • DMA受信設計

  • RTOSタスク設計

  • デッドロック回避

  • 通信再送制御