C言語記述の際はアプリ層とドライバ層に分ける
| 設計内容分類 | 組み込みソフトウェアにおけるC言語記述 |
|---|
実現したい仕様・課題
ハードウェア(レジスタや周辺モジュール)へ直接アクセスするコードがmain関数やアプリケーション処理の中に散在すると、不具合時にアプリの論理とハード制御のどちらが原因か切り分けられず、デバッグが難航します。またマイコン変更時にはコード全体の見直しが必要になります。保守性と移植性のあるソフトウェア構造にしたいというのが課題です。
設計のポイント
ハードウェアに直接アクセスするコードはドライバ層として、Mainとは別の関数・別のファイルにまとめます。ドライバ層にはOpen/Close、Read/Write、割り込みハンドラといったハードウェア操作の基本機能を実装し、アプリ動作の記述ではドライバ層の関数を呼び出す形とします。この分離により、ドライバ層のデバッグが完了していれば、アプリ層の不具合調査でドライバ層の動作を疑う必要がなくなり、問題の切り分けが明確になります。複数のハードウェアモジュール(UART、SPI、タイマ等)にアクセスする場合は、モジュールごとに別ファイルとし、ドライバに必要なパラメータ(変数)は各ファイル内に閉じて記述します。将来的に再利用できるよう、ドライバにはアプリ固有の動作(業務ロジックや画面遷移への依存)を含めない記述とすることが重要です。この構造の利点は保守性にとどまりません。マイコンを変更する際は、ドライバ層のファイルを新しいマイコン向けに差し替えるだけでアプリ層を再利用でき、移植工数を大幅に削減できます。ドライバの呼び出しインターフェース(関数プロトタイプ)をヘッダファイルで定義しておけば、アプリ層とドライバ層を別担当者が並行開発することも、ドライバ層を単体でテストすることも容易になります。層の分離は、C言語による組み込み開発の品質・工数・再利用性を左右する、最も基本的な設計判断です。
【構成例】
main.c … アプリ層(ドライバ関数の呼び出しのみ)
drv_uart.c/.h … UARTドライバ(Open/Close/Read/Write/割り込み)
drv_spi.c/.h … SPIドライバ
drv_timer.c/.h … タイマドライバ
// アプリ層の記述イメージ
uart_open(BAUD_115200);
uart_write(cmd, sizeof(cmd));
len = uart_read(buf, sizeof(buf));

補足情報
設計のポイント 一覧
組み込みソフトウェアにおけるC言語記述のポイント
- リングバッファで受信データを取りこぼさない
- switch-caseによるステートマシンの実装
- 割り込みハンドラは短く書く
- constを活用する(ROM配置と書き換え防止)
- ビット演算によるレジスタ・フラグ操作
- 固定幅整数型(stdint.h)を使う
- volatile修飾子を正しく使う
- 構造体の宣言にpackedを指定する
- ソースコードのエンディアン変換
- sizeofを使用する
- 特殊文字の記述にエスケープ文字を使用する
- 変数の初期化漏れを防ぐ
- 外部バスの使用
- sprintf関数を使う
- 列挙型(enum)を使う
- 構造体を使う
- 標準関数を使う
- 条件演算子を使う
- インクリメント演算子を使う
- C言語記述の際はアプリ層とドライバ層に分ける



