【问题标题】:Splitting 32 bit register in variable size bytes以可变大小字节拆分 32 位寄存器
【发布时间】:2016-09-08 10:22:11
【问题描述】:

我想把一个通用寄存器分成三个独立的寄存器;两个 8 位寄存器和一个 16 位寄存器。这是我正在使用的方法,但我认为它可能是错误的。

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;

这是错误的方法还是他们更清洁的解决方案?

【问题讨论】:

  • 如果您的平台的 ABI 保证正确的布局,该方法是可以的。无论如何,这取决于平台。例如例如 ARM AAPCS。但是,不要创建单个字段volatile。要么限定struct,要么限定演员。
  • 看起来答案已被拥有所有 cmets 的人删除。简而言之,我目前使用的解决方案是否足够?

标签: c embedded bit


【解决方案1】:

对于诸如此类的所有类型的内存映射,最重要的是验证 C 代码是否产生了预期的结果。您必须考虑填充和对齐。

您的示例中的一些小问题:

  • 始终确保代码中的所有十六进制文字都具有有意义的类型。在您的情况下,0xA040000C 是签名类型,很可能是unsigned int。但是,如果您使用像 0x5040000C 这样的文字,它将是(签名的)int 类型。在大多数系统上,签名地址是没有意义的。 “草率类型”可能会导致各种微妙的错误,尤其是在与各种形式的整数提升结合使用时。
  • 将 volatile 限定符移出结构。尽可能避免在结构中使用类型限定符 - 它们通常会导致很多类型兼容性问题。
  • 硬件寄存器的指针符号有点特殊。最常见的风格是将寄存器视为已声明的变量。

考虑到上述说明,您的代码可以重写为:

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;

【讨论】:

  • SIdenote:0xA040000C 很可能是带有CHAR_BIT == n * 8 (n = 1 ..N) 的平台上的无符号类型。但为了澄清和统一,最好明确说明。例如ARM CMSIS 使用外设寄存器structs 的指针。这只是平台的约定问题。
猜你喜欢
  • 1970-01-01
  • 2018-06-20
  • 1970-01-01
  • 2014-11-18
  • 2011-03-02
  • 2020-11-21
  • 1970-01-01
  • 2015-06-01
相关资源
最近更新 更多