在C中直接和间接访问易失性对象有什么区别?

问题描述 投票:0回答:1

我正在处理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;

(输出中还有更多指令)

c pointers gcc stm32 volatile
1个回答
0
投票

问题是在不需要时打包结构。您还过度使用了volatile。

请非常小心地包装结构和联合,因为它会阻止许多代码优化。不要“以防万一”。

这里您使用的是正确的版本。

https://godbolt.org/z/GNjmUX

© www.soinside.com 2019 - 2024. All rights reserved.