組み込み機器・ハードウェアの設計開発から装置製作まで社内一貫対応!

組み込み機器・ハードウェア設計製作.com
組み込み機器・ハードウェア設計製作.com

produced by株式会社アイディアイ

設計のポイント

volatile修飾子を正しく使う

設計内容分類組み込みソフトウェアにおけるC言語記述

実現したい仕様・課題

ハードウェアのレジスタや割り込みハンドラが書き換える変数を通常の変数として扱うと、コンパイラの最適化により「値は変わらない」とみなされ、フラグ待ちのループが永久に抜けられない、レジスタの読み出しが省略される、といった最適化起因の誤動作が発生します。最適化を有効にしたまま、正しく動くコードにしたいというのが課題です。

設計のポイント

プログラムの外側(ハードウェアや割り込み)から値が変わり得る変数には、volatile修飾子を付けて宣言します。volatileは「この変数はいつ書き換わるか分からないため、最適化による読み書きの省略やキャッシュ(レジスタへの持ち込み)を行わず、参照のたびに必ずメモリへアクセスせよ」という指示です。付けるべき対象は、(1) メモリマップされたハードウェアレジスタ(ステータスレジスタ、データレジスタ等)、(2) 割り込みハンドラとメインループの間で共有するフラグ・データ、(3) 他の実行主体(RTOSの別タスクやDMA)が書き換えるメモリ、の3つが代表です。典型的な症状として、while (flag == 0); のような割り込み待ちループは、flagがvolatileでないと最適化時に無限ループへ変換されることがあります。デバッグビルド(最適化なし)では動き、リリースビルドで動かなくなる不具合の多くがこのパターンです。重要な注意点として、volatileは排他制御ではありません。読み出し・加算・書き戻しのような複合操作の途中で割り込まれる競合(アトミック性の欠如)はvolatileでは防げないため、複数バイトのデータや複合操作の共有には、割り込み禁止区間やフラグの受け渡し設計を併用します。逆に、すべての変数にvolatileを付けると最適化が全面的に阻害され性能が落ちるため、「外部から書き換わる変数だけに付ける」ことが正しい使い方です。

【コード例】

// ハードウェアレジスタ:volatile必須
#define UART_SR (*(volatile uint32_t*)0x40001000)

// 割り込みと共有するフラグ
volatile uint8_t rx_done = 0;

void uart_isr(void) { rx_done = 1; }   // 割り込みハンドラ

// メインループ側:volatileがないと最適化で無限ループ化の危険
while (rx_done == 0) { /* 待ち */ }
rx_done = 0;

補足情報

当事例のような、マイコンのファームウェアにおけるC言語記述のポイントをまとめた冊子の無料DLが可能です。
是非以下よりご確認ください。

マイコンのファームウェア C言語記述のポイント

>>ダウンロードはこちら!

お問い合わせ

組み込み機器・ハードウェア 設計製作.comを運営する株式会社アイディアイは、組み込み機器・ハードウェアの設計開発代行サービスを提供しています。
試験装置や制御機器、組み込みボードなど、設計・開発委託先をお探しの皆様、お気軽に当社に御相談ください。

技術資料のダウンロードはこちらから 技術資料ダウンロ―ド メールフォームでのお問い合わせはこちらから ご相談・お問合せ