リングバッファ完全実装+トレース問題
なぜリングが必要か
バッファを使い回せる
古いデータを上書きできる
通信受信で必須構造
実装モデル(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
操作:
push(10)
push(20)
pop()
push(30)
push(40)
pop()
| 操作 | w | r | count | 返却 |
| 初期 | 0 | 0 | 0 | - |
| push10 | 1 | 0 | 1 | - |
| push20 | 2 | 0 | 2 | - |
| pop | 2 | 1 | 1 | 10 |
| push30 | 3 | 1 | 2 | - |
| push40 | 4 | 1 | 3 | - |
| pop | 4 | 2 | 2 | 20 |
割り込み競合(排他制御)の考え方
問題の本質
割り込みとメインが同じ変数を触る
途中で割り込まれると値が壊れる
危険例
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タスク設計
デッドロック回避
通信再送制御