シリーズ:実験シリーズ(フェーズ0)
対応ロードマップ:フェーズ0 / E0-06
この記事で扱う範囲:状態とエラー番号を定義し、ログで「今どの状態か」「なぜ止まったか」を追えるようにする。
1. 目的
前回までで、UARTログを出せる状態になりました。
ただ、ログが出るだけでは、動かないときに原因を追いにくいです。
たとえば、ただ error とだけ出ても、
- 初期化で失敗したのか
- 動作中に異常になったのか
- 何の異常なのか
が分かりません。
そこで今回は、プログラムを次の4つの状態に分けます。
INITIDLERUNERROR
さらに、異常時の原因を表す エラー番号 も決めます。
今回のゴールは、UARTログを見て、
- 今どの状態か
- どのエラーで止まったか
を分かるようにすることです。
2. 回路・配線
今回は新しい外付け回路は使いません。
E0-05 のUARTログ出力ができる配線のまま 行います。
今回使うもの
- RA8M2評価ボード
- PC:Windows + TeraTerm
- IDE:e² studio + FSP
- 接続:USB-UART変換(3.3V系)
- 「E0-05 ログ出力(UART):HelloログをTeraTermに出す(EK-RA8M2)」で使用したUARTログ出力の接続一式
ポイント
今回の主役は配線ではなく、ソフト側で状態とエラーを定義することです。
そのため、ハード構成は最低限で構いません。
3. 手順
3-1. 状態を決める
今回は次の4状態を使います。
INIT:初期化中IDLE:待機中RUN:動作中ERROR:異常状態
3-2. エラー番号を決める
今回は例として次を使います。
0:エラーなし1:初期化失敗2:テスト用異常3:想定外の状態
※エラー番号の詳しい決め方や分類方法は、この記事では扱いません。
3-3. 状態番号とエラー番号をUARTログに出せるようにする
UARTログで確認しやすいように、状態ごとに送信する文字列と、エラーごとに送信する文字列を用意します。
これにより、PC側のTeraTermで INIT、IDLE、RUN、ERROR や、エラー内容を確認できるようにします。
文字列と送信処理の内容は、4章のコード(最小構成)で示します。
3-4. 正常系と異常系を確認する
まずは、そのままデバッグ実行または書き込み後に動作させ、UARTログでINIT → IDLE → RUN
と進むことを確認します。
次に、異常系の確認として、4章の int error_test = 0;の0 → 1 に変更します。
これにより、RUN中にテスト用異常が発生するようになります。
この状態で再ビルドして実行し、UARTログでRUN → ERROR
へ遷移し、エラー番号が出ることを確認します。
※確認後は、int error_test = 0; に戻します。
e² studioを使用したデバッグ方法は次を参照してください。
⇒「初心者のためのデバッグ入門:ブレークポイントと変数を見る」
4. コード(最小構成)
最小構成の例です。
hal_entry.cにあるhal_entry関数を以下のように修正します。
void hal_entry(void)
{
fsp_err_t err;
int state_no = 0; /* 0=INIT, 1=IDLE, 2=RUN, 3=ERROR */
int error_no = 0; /* 0=なし, 1=UART_OPEN_FAILED, 2=TEST_ERROR */
int error_test = 0; /* 異常確認したいときだけ 1 にする */
static const uint8_t msg_init[] = "[STATE] INIT\r\n";
static const uint8_t msg_idle[] = "[STATE] IDLE\r\n";
static const uint8_t msg_run[] = "[STATE] RUN\r\n";
static const uint8_t msg_error[] = "[STATE] ERROR\r\n";
static const uint8_t msg_err1[] = "[ERROR] 1 : UART_OPEN_FAILED\r\n";
static const uint8_t msg_err2[] = "[ERROR] 2 : TEST_ERROR\r\n";
/* UART Open */
err = R_SCI_B_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);
if (FSP_SUCCESS != err)
{
while (1) {;}
}
while (1)
{
if (state_no == 0)
{
err = R_SCI_B_UART_Write(&g_uart0_ctrl, msg_init, (uint32_t)(sizeof(msg_init) - 1));
if (FSP_SUCCESS != err)
{
error_no = 1;
state_no = 3;
}
else
{
for (volatile uint32_t i = 0; i < 1000000U; i++)
{
__asm volatile ("nop");
}
error_no = 0;
state_no = 1;
}
}
if (state_no == 1)
{
err = R_SCI_B_UART_Write(&g_uart0_ctrl, msg_idle, (uint32_t)(sizeof(msg_idle) - 1));
if (FSP_SUCCESS != err)
{
error_no = 1;
state_no = 3;
}
else
{
for (volatile uint32_t i = 0; i < 1000000U; i++)
{
__asm volatile ("nop");
}
state_no = 2;
}
}
if (state_no == 2)
{
err = R_SCI_B_UART_Write(&g_uart0_ctrl, msg_run, (uint32_t)(sizeof(msg_run) - 1));
if (FSP_SUCCESS != err)
{
error_no = 1;
state_no = 3;
}
else
{
for (volatile uint32_t i = 0; i < 1000000U; i++)
{
__asm volatile ("nop");
}
if (error_test == 1)
{
error_no = 2;
state_no = 3;
}
else
{
while (1) {;}
}
}
}
if (state_no == 3)
{
err = R_SCI_B_UART_Write(&g_uart0_ctrl, msg_error, (uint32_t)(sizeof(msg_error) - 1));
if (FSP_SUCCESS != err)
{
while (1) {;}
}
for (volatile uint32_t i = 0; i < 1000000U; i++)
{
__asm volatile ("nop");
}
if (error_no == 1)
{
err = R_SCI_B_UART_Write(&g_uart0_ctrl, msg_err1, (uint32_t)(sizeof(msg_err1) - 1));
if (FSP_SUCCESS != err)
{
while (1) {;}
}
}
if (error_no == 2)
{
err = R_SCI_B_UART_Write(&g_uart0_ctrl, msg_err2, (uint32_t)(sizeof(msg_err2) - 1));
if (FSP_SUCCESS != err)
{
while (1) {;}
}
}
for (volatile uint32_t i = 0; i < 1000000U; i++)
{
__asm volatile ("nop");
}
while (1) {;}
}
}
}5. 実行結果
5-1. 正常時のログ
[STATE] INIT
[STATE] IDLE
[STATE] RUNこれで、
- 初期化が完了した
- 待機に入れた
- 動作中まで進んだ
と分かります。
5-2. 異常時のログ
int error_test = 1; にすると、次のようになります。
[STATE] INIT
[STATE] IDLE
[STATE] RUN
[STATE] ERROR
[ERROR] 2 : TEST_ERRORこれで、
- RUN中に
- エラー2が発生して
- ERRORへ遷移した
と読めます。
5-3. 今回確認できればよいこと
今回の段階では、次が確認できれば十分です。
- 状態名がログに出る
- 正常時に
INIT → IDLE → RUNと進む - 異常時に
ERRORに入る - エラー番号とエラー名が出る
6. トラブルと対処方法
6-1. TeraTermに何も表示されない
原因:UART設定や配線が正しくない可能性があります。
対処方法:先に UARTの設定や接続を見直し、次を確認します。
- COM番号が正しいか
- TeraTermの通信条件が一致しているか
- GND / TX / RX の配線が正しいか
- FSP Configurator の UART 設定と Pins 設定が正しいか
※UARTログの設定については、次の記事を参考にしてください。
⇒E0-05 ログ出力(UART):HelloログをTeraTermに出す(EK-RA8M2)(リンク)
6-2. INITは出るが、その先が出ない
原因:状態番号が次の値に更新されていない、またはUART送信でエラーになっている可能性があります。
対処方法:state_no が 0 → 1 → 2 と切り替わるコードになっているか確認します。あわせて、R_SCI_B_UART_Write() の戻り値チェックがあるか確認します。
6-3. IDLEまでは出るが、RUNまで進まない
原因:state_no が 1 から 2 に切り替わっていない可能性があります。
対処方法:コードを最初から順に見て、state_no = 2; の処理があるか確認します。
6-4. ERRORに入らない
原因:error_test が 0 のままの可能性があります。
対処方法:異常系を確認したいときは、error_test = 1; に変更して再ビルドします。確認後は 0 に戻します。
6-5. ERRORに入ったが、どのエラーか分からない
原因:error_no に値を入れる前に、[STATE] ERROR だけを出している可能性があります。
対処方法:先に error_no を設定してから、ERRORのログを出すようにします。
6-6. 同じログが何度も出る
原因:同じ状態で何度も UART 送信している可能性があります。
対処方法:状態を進めたら次の state_no に切り替えるようにし、同じ状態のログを繰り返し送らないようにします。
6-7. ビルドは通るのに期待したログにならない
原因:文字列や状態番号の対応が、自分で決めた内容とずれている可能性があります。
対処方法:記事内で決めた対応と、コード内の値が一致しているか確認します。
状態番号
0 = INIT1 = IDLE2 = RUN3 = ERROR
エラー番号
0 = エラーなし1 = UART_OPEN_FAILED2 = TEST_ERROR
7. 今回わかったこと
今回の実験で大事なのは、高度な状態機械を作ることではありません。
大事なのは次の3つです。
- 状態を名前で分ける
- エラーを番号で分ける
- それをログで見えるようにする
これを先にやっておくと、後の実験で
「動かない」ではなく「どこで」「なぜ」止まったか を追えるようになります。
8. 次回へのメモ
次回以降は、今回の状態とエラーの考え方を土台にして、
タイマや入力やセンサの処理にも広げていきます。
たとえば次のような発展ができます。
- ボタン入力で
IDLE → RUNを切り替える - タイマ周期で状態を更新する
- センサ異常で
ERRORへ入る - エラー番号を増やして切り分けしやすくする
フェーズ0では、まず
状態とエラーをログで見えるようにする ができれば十分です。