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结构成员,因为这些类型定义主要用于结构类型定义中用于寄存器集合,但似乎也并非如此。

4

3 回答 3

5

如果您使用 const 声明,C++ 标准将强制您初始化变量的内容。在微控制器寄存器的情况下,您不想这样做。

于 2013-03-14T16:57:58.467 回答
3

因为没有RoReg对象被实例化,所以没有充分的理由const在 typedef 中省略限定符。

每次使用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 条)”。作者可能弄错了constvolatile为访问控制说明符。如果是,那么您会希望所有结构成员都具有相同的 cv 限定符,以确保 C 和 C++(以及硬件)布局之间的兼容性。但它是public, protected, andprivate定义访问控制。

于 2013-03-15T00:00:53.773 回答
2

正如@fanl 所提到的,const确实改变了 C++ 中全局变量的默认链接,并且确实阻止了在没有初始化的情况下定义变量。

但是有比删除更好的方法来获得外部链接const。Chris 链接的头文件中保留数组的使用也很脆弱。我会说这段代码有很大的改进空间——不要模仿它。

此外,这些变量没有被定义(这将导致编译器和链接器选择一个地址),它们总是通过指针访问,地址根据内存映射固定。

对于纯粹供 C++ 使用的头文件,我就是这样做的(内存映射匹配 TI Stellaris 芯片)。

看起来很复杂,但优化编译器将其减少到每次访问一条指令。并且地址偏移被编码,不依赖于结构内字段的顺序和填充,因此它不那么脆弱并且更容易根据数据表进行验证。

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 回答