サンプルプログラムなんかを読んでいて16進数を見るとうんざりすることがあります。とくにペリフェラル設定となると、16進数を頭の中で分解してどのビットの状態が0でどのビットの状態が1か考えなければなりません。残念ながら私の頭はそれほどよくありません。これはサンプルを読むときだけではなく、自分が作ったプログラムを読むときですらそうです。
こういうときはマクロを使うのが一般的です。以前はアナログデバイセズの開発ツールときたらビットマクロすら満足に供給されていませんでしたが、最近はましになってきています。しかし、ツールから開発されるマクロが膨大になったため、ユーザー定義のシンボルとの衝突が不安になってきました。
日本語技術資料のEEJ-017にはこの問題を解決するインクルードファイルも入っています。regBF53x.hがそれです。
regBF53x.hはペリフェラル・レジスタのビット定義をすべて構造体で実現しています。例えば、EBIU_AMBCTL0レジスタはrEBIU_AMBCTL0型として定義されており、B0RATフィールドには.b0ratメンバー変数としてアクセスできます。このフィールドに2を代入したければ、
rbf::rEBIU_AMBCTL0 ambctl0; ambctl0.b0rat = 2;
のように書きます。rbfはレジスタ定義を含む名前空間です。このようにビットフィールドアクセスも簡単にできるのが構造体方式のいいところです。では、このような構造体変数とレジスタの間の値のやり取りはどうするのでしょうか。これには特殊な補助メソッドを使います。すべてのレジスタ構造体には二つの補助メソッドが用意されています。これらの名前は同じで型が少し違うだけです。rEBIU_AMBCTL0を例にとって見ると
となっています。そしてこれらを使うとレジスタのread-modify-writeは次のようになります。
rbf::rEBIU_AMBCTL0 ambctl0; ambctl0.all( mmr::pEBIU_AMBCTL0 ); // 構造体にレジスタの値を設定 ambctl0.b0rat = 2; *mmr::pEBIU_AMBCTL0 = ambctl0.all();// レジスタに値を書き込む
これをマクロで書くと次のようになります。
unsigned int ambctl0; ambctl0 = mmr::pEBIU_AMBCTL0; ambctl0 &= ~B0RAT_15; // B0RATフィールドをクリア ambctl0 |= B0WAT_2; // フィールドに2を代入 *mmr::pEBIU_AMBCTL0 = ambctl0;
ビットフィールドへの代入の場合は事前にこれをクリアしなければなりません。また、代入を直接行うと変数が壊れるのでビット論理和を行わなければなりません。しかし構造体を使ったアクセス方法ではそのような面倒はありません。
このように、構造体定義を行うと16進数やマクロを使った場合よりわかりやすくなります。欠点を挙げるとしたら、プログラムが長くなりがちな点でしょう。