我正在处理STM32控制器上的硬件寄存器。
我已经定义了一堆类似于以下的结构:
#define PACKED __attribute__ ((packed))
#define ASSERT(cond) _Static_assert(cond, #cond)
typedef union {
struct PACKED {
uint16_t value;
uint16_t reserved;
};
uint32_t bits;
} LOL_LPTIM_16_BIT_VALUE;
ASSERT(sizeof(LOL_LPTIM_16_BIT_VALUE) == sizeof(uint32_t)); // OK
然后,我有一个这样的结构:
typedef struct PACKED {
// ...
volatile LOL_LPTIM_16_BIT_VALUE autoreload;
// ...
} LOL_LPTIM;
autoreload
字段在结构中的偏移量与文档一致。我还可以使用以下对象(遵循ST提供的文档和头文件):
#define LOL_LPTIM1_BASE (LOL_APB1PERIPH_BASE + 0x7C00UL)
#define LOL_LPTIM2_BASE (LOL_APB1PERIPH_BASE + 0x9400UL)
#define LOL_LPTIM1 ((volatile LOL_LPTIM *) LOL_LPTIM1_BASE)
#define LOL_LPTIM2 ((volatile LOL_LPTIM *) LOL_LPTIM2_BASE)
我有一个static const
结构,用于存储这些指针:
static const struct {
volatile LOL_LPTIM *lptim;
} timer[2] = {
{ .lptim = LPTIM1 },
{ .lptim = LPTIM2 }
}
现在,当我写
*(uint32_t *) &(timer[0].lptim->autoreload.bits) = 0xffff;
或
*(uint16_t *) &(timer[0].lptim->autoreload.value) = 0xffff;
代码正常工作,但是在我写的时候
timer[0].lptim->autoreload.bits = 0xffff;
(应该完全相等)或
lptim[0].lptim->autoreload.value = 0xffff;
然后,它不能按预期方式工作-它与间接变体的工作方式不同,并且autoreload
寄存器的值似乎未正确设置(外围设备的行为不同)。
这种差异的可能原因是什么?
[Godbolt显示编译器针对这两种情况生成了非常不同的一组操作:https://godbolt.org/z/cno9yf
当间接版本更改为它们时,它们会变得更加相似
*(volatile uint32_t *) &(timer[0].lptim->autoreload.bits) = 0xffff;
(输出中还有更多指令)
问题是在不需要时打包结构。您还过度使用了volatile。
请非常小心地包装结构和联合,因为它会阻止许多代码优化。不要“以防万一”。
这里您使用的是正确的版本。