CのI²Cレジスタマップに関するベストプラクティス、または他の人がよく使用する/好むものについて疑問に思っています。
この時点まで、私は通常、すべてのレジスターに対して 1 つ、すべてのビット、マスク、シフトなどに対して 1 つ、多くの定義を行ってきました。しかし、最近、一部のドライバーが定義済みの代わりに (おそらくパックされた) 構造体を使用しているのを見てきました。これらはLinuxカーネルモジュールだったと思います。
とにかく、彼らは
struct i2c_sensor_fuu_registers {
uint8_t id;
uint16_t big_register;
uint8_t another_register;
...
} __attribute__((packed));
次に、offsetof (またはマクロ) を使用して i2c レジスタを取得し、sizeof を使用して読み取るバイト数を取得します。
どちらのアプローチにもメリットがあることがわかりました。
構造体アプローチ:
- (+) レジスタ オフセットはすべて、定義で各レジスタを綴る代わりに、論理的に構造体内に含まれます。
- (+) エントリのサイズは、適切なサイズのデータ型を使用して明示的に記述されています。
- (-) これは、広く使用されているビット フィールドを考慮していません。
- (-) これは、オフセット n+0x00 から 2 バイトを読み取るが、n+0x01 は別のレジスタであり、レジスタ n の上位/下位バイトではない、バイト マップされていないレジスタ マップ (LM75 など) を考慮していません。 +0x00
- (-) これは、アドレス空間の大きなギャップ (たとえば、0x00、0x01、0x80、0xAA のレジスター、中間がないなど) を考慮しておらず、(私が思うに?) 構造体を取り除くためにコンパイラーの最適化に依存しています。 .
アプローチを定義する:
- (+) 各レジスタは通常、そのビットとともにブロックで定義されるため、適切なシンボルを簡単に見つけることができ、命名規則に依存します。
- (+) アドレス空間のギャップを透過的/認識しない。
- (-) ギャップがない場合でも、各レジスタを個別に定義する必要がある
- (-) 定義はグローバルになる傾向があるため、通常、名前は非常に長くなり、ソース コードには何十もの長いシンボル名が散らばっています。
- (-) 読み取るデータのサイズは、通常、ハードコードされたマジック ナンバーか、長いシンボル名を持つ (終了 - 開始 + 1) スタイルの計算のいずれかです。
- (o) データ サイズとマップ内のアドレスを透過的/認識しない。
基本的に、私はこれらのケースを処理するためのよりスマートな方法を探しています。私はよく、すべてのレジスターと各ビット、そして場合によってはマスクとシフト (データ型に応じて後者の 2 つ) に対して、非常に長いシンボル名を大量に入力して、それらのほんの一部を使用することになります (ただし、不足しているシンボルを後で再定義するため、1 つのセッションですべてを入力するのはそのためです)。それでも、読み書きするバイトのサイズはほとんどがマジック ナンバーであり、通常、最も基本的な相互作用を理解するには、データシートとソース コードを並べて読む必要があることに気付きました。
他の人はこの種の状況をどのように処理するのだろうか? 私はオンラインでいくつかの例を見つけましたが、人々はすべてのレジスター、ビットなどを大きなヘッダーに熱心に入力しましたが、決定的なものは何もありませんでした...しかし、上記の2つのオプションはどちらも現時点ではあまりスマートではないようです:(