2016-09-08 9 views
2

汎用レジスタを3つの別々のレジスタに分割したい。 2つの8ビットレジスタ& 1つの16ビットレジスタ。これは私が使っているアプローチですが、間違っていると思います。可変サイズのバイトで32ビットレジスタを分割する

typedef struct { 
     volatile uint8_t reg_0; 
     volatile uint8_t reg_1; 
     volatile uint16_t reg_2; 
} reg_split; 

#define REG_BASE (0xA040000C) 
#define REG ((reg_split *)REG_BASE) 

そして、これは私がレジスタにアクセスしていますがどのようである:

REG->reg_0 = 0xFF; 

は、これは間違ったアプローチですか自分クリーナーソリューションですか?

+1

アプローチは、okです。これはとにかくプラットフォームに依存します。例えばたとえば、ARM AAPCSがそうです。ただし、単一のフィールドを「揮発性」にしないでください。 'struct'またはキャストを修飾します。 – Olaf

+0

回答がすべてのコメントのある人によって削除されたように見えます。要するに、私が現在使っている解決策は十分ですか? – homeGrown

答えて

3

これらのすべての種類のメモリマッピングにとって最も重要なのは、Cコードが期待どおりの結果をもたらすことを確認することです。あなたはパディングとアライメントを考慮する必要があります。

あなたの例でいくつかのマイナーなNIT-ピック:

  • 常にあなたのコード内のすべての進リテラルは理にかなっているタイプがあることを確認してください。あなたのケースの0xA040000Cは、署名されたタイプです。おそらく、unsigned intです。 0x5040000Cのようなリテラルを使用していた場合は、intタイプの(署名済みの)ものになります。ほとんどのシステムでは、署名付きアドレスを持つことは意味がありません。 「厄介な型」は、特に整数型宣伝のさまざまな形と組み合わされたときに、すべてのやり方で微妙なバグを引き起こす可能性があります。
  • volatile修飾子を構造体から移​​動します。可能であれば、構造体内に型修飾子を使用しないでください。通常、型互換性の問題が発生します。
  • ハードウェアレジスタのポインタ表記は少し特殊です。最も一般的なスタイルは、レジスタを宣言された変数と見なすことです。心の中で上記の発言で

、あなたのコードのように書き換えることができますお使いのプラットフォームのABIが正しいレイアウトを保証している場合

typedef struct { 
    uint8_t reg_0; 
    uint8_t reg_1; 
    uint16_t reg_2; 
} reg_split; 

#define REG_BASE (0xA040000Cu) 
#define REG (*(volatile reg_split *)REG_BASE) 

_Static_assert(sizeof(reg_split) == 4, "Unwanted padding detected"); 

... 

REG.reg_0 = 0xFF; 
+0

SI: '0xA040000C'は、' CHAR_BIT == n * 8'( 'n = 1 ..N')のプラットフォームでは符号なしの型です。しかし、明確化と一貫性のためには、それを明示的にすることをお勧めします。例えばARM CMSISは周辺レジスタ 'struct'にポインタを使用します。これは、プラットフォームのためのコンベンションの問題です。 – Olaf