6

Atmel SAM3X8E を使用して組み込みシステム プロジェクトに取り組んでいるときに、いくつかの CMSIS ヘッダー ファイルに次のようなコードがあることに気付きました。

#ifndef __cplusplus
typedef volatile const uint32_t RoReg; /**< Read only 32-bit register (volatile const unsigned int) */
#else
typedef volatile       uint32_t RoReg; /**< Read only 32-bit register (volatile const unsigned int) */
#endif

C++ の typedef に が含まれないのはなぜconstですか? C++ はランタイム メモリに整数の const 変数を格納しないという言及をどこかで見ました。これが真の場合、constマイクロコントローラ レジスタがメモリ マップされる方法のために削除する必要があることを意味しますが、他にそれを言っているものを見つけることができないようです。 C++ はそれを行います (私の検索は確かにかなり簡単でしたが)。C++ の経験があまりないので、C++ ではconst構造体メンバーが許可されていないのではないかと思いました。これらの typedef は主にレジスターのコレクションの構造体 typedef で使用されるためですが、そうではないようです。

4

3 に答える 3

5

const で宣言すると、C++ 標準では、変数の内容を初期化することが義務付けられます。マイクロコントローラーのレジスターの場合、それはしたくありません。

于 2013-03-14T16:57:58.467 に答える
3

オブジェクトがインスタンス化されることはないため、typedef で修飾子RoRegを省略する正当な理由はありません。const

のすべての使用はRoReg、型へのポインターを定義するマクロのいずれかです...

#define REG_WDT_SR (*(RoReg*)0x400E1A58U) /**< \brief (WDT) Status Register */

...またはstruct同様のマクロを使用してアクセスされる宣言。

typedef struct {
  WoReg WDT_CR; /**< \brief (Wdt Offset: 0x00) Control Register */
  RwReg WDT_MR; /**< \brief (Wdt Offset: 0x04) Mode Register */
  RoReg WDT_SR; /**< \brief (Wdt Offset: 0x08) Status Register */
} Wdt;

#define WDT        ((Wdt    *)0x400E1A50U) /**< \brief (WDT) Base Address */

修飾子があってもconst、コードは C と C++ の両方で同じように動作するはずです。

おそらく、著者は標準を誤解しました。C++ 構造体が C と同じレイアウトであることを保証するには、クラスが「すべての非静的データ メンバーに対して同じアクセス制御 (条項 11) を持つ」必要があります。作成者は、アクセス制御指定子constと間違っている可能性があります。volatileそうである場合、C と C++ (およびハードウェア) レイアウト間の互換性を確保するために、すべての構造体メンバーに同じ cv 修飾子を持たせる必要があります。しかし、アクセス制御を定義するのpublicは 、protected、およびです。private

于 2013-03-15T00:00:53.773 に答える
2

@fanl が述べたように、const実際に C++ のグローバルのデフォルト リンケージを変更し、初期化せずに変数を定義することを防ぎます。

しかし、 を削除するよりも、外部リンクを取得するためのより良い方法がありますconst。Chris がリンクしたヘッダー ファイルでの予約済み配列の使用法も非常に脆弱です。このコードには改善の余地がたくさんあると思います。エミュレートしないでください。

さらに、これらの変数は定義されず (コンパイラとリンカがアドレスを選択する原因となります)、常にポインタを介してアクセスされ、アドレスはメモリ マップに従って固定されます。

純粋に C++ で使用することを意図したヘッダーの場合、これが私のやり方です (メモリ マップは TI Stellaris チップと一致します)。

複雑に見えますが、最適化コンパイラは、アクセスごとに 1 つの命令にまで減らします。また、アドレス オフセットは、構造内のフィールドの順序やパディングに依存せずにコード化されるため、脆弱性が大幅に軽減され、データシートとの照合が容易になります。

template<uintptr_t extent>
struct memory_mapped_peripheral
{
    char data[extent];
    volatile       uint32_t* offset( uintptr_t off )       { return reinterpret_cast<volatile       uint32_t*>(data+off); }
    volatile const uint32_t* offset( uintptr_t off ) const { return reinterpret_cast<volatile const uint32_t*>(data+off); }
};

struct LM3S_SYSTICK : private memory_mapped_peripheral<0x1000>
{
    volatile       uint32_t& CTRL   (void)             { return offset(0x010)[0]; }
    volatile       uint32_t& RELOAD (void)             { return offset(0x014)[0]; }
    volatile       uint32_t& CURRENT(void)             { return offset(0x018)[0]; }
}* const SYSTICK = reinterpret_cast<LM3S_SYSTICK*>(0xE000E000);

struct LM3S_NVIC : private memory_mapped_peripheral<0x1000>
{
    volatile       uint32_t& EN    (uintptr_t i)       { return offset(0x100)[i]; }
    volatile       uint32_t& DIS   (uintptr_t i)       { return offset(0x180)[i]; }
    volatile       uint32_t& PEND  (uintptr_t i)       { return offset(0x200)[i]; }
    volatile       uint32_t& UNPEND(uintptr_t i)       { return offset(0x280)[i]; }
    volatile const uint32_t& ACTIVE(uintptr_t i) const { return offset(0x300)[i]; }
    volatile       uint32_t& PRI   (uintptr_t i)       { return offset(0x400)[i]; }
    volatile       uint32_t& SWTRIG(void)              { return offset(0xF00)[0]; }
}* const NVIC = reinterpret_cast<LM3S_NVIC*>(0xE000E000);
于 2013-03-14T19:44:29.790 に答える