【问题标题】:Struct and bitfield strange behaviour结构和位域奇怪的行为
【发布时间】:2018-11-22 09:36:17
【问题描述】:

我正在尝试修改寄存器中的位域。这是我定义了位域的结构:

struct GROUP_tag
{
    ...
    union
    {
        uint32_t R;
        struct
        {
            uint64_t bitfield1:10;
            uint64_t bitfield2:10;
            uint64_t bitfield3:3;
            uint64_t bitfield4:1;
        } __attribute__((packed)) B;
    } __attribute__((aligned(4))) myRegister;
    ...
}

#define GROUP (*(volatile struct GROUP_tag *) 0x400FE000)

当我使用以下行时:

GROUP.myRegister.B.bitfield1 = 0x60;

它不仅会改变 bitfield1,还会改变 bitfield2。该寄存器的值为0x00006060

代码被编译为以下汇编代码:

ldr r3,[pc,#005C]
add r3,r3,#00000160
ldrb r2,[r3,#00]
mov r2,#00
orr r2,#00000060
strb r2,[r3,#00]
ldrb r2,[r3,#01]
bic r2,r2,#00000003
strb r2,[r3,#01]

如果我尝试直接操作寄存器:

int volatile * reg = (int *) 0x400FE160;
*reg = 0x60

寄存器的值为0x00000060

我正在使用 GCC 编译器。

为什么我使用结构体和位域时值重复?

编辑

我发现了另一个奇怪的行为:

GROUP.myRegister.R = 0x12345678; // value of register is 0x00021212
*reg = 0x12345678; // value of register is 0x0004567, this is correct (I am programming microcontroller and some bits in register can't be changed)

我更改寄存器值的方法(使用结构和位域)被编译为:

ldr r3,[pc,#00B4]
ldrb r2,[r3,#0160]
mov r2,#00
orr r2,#00000078
strb r2,[r3,#0160]
ldrb r2,[r3,#0160]
mov r2,#00
orr r2,#00000056
strb r2,[r3,#0161]
ldrb r2,[r3,#0162]
mov r2,#00
orr r2,#00000034
strb r2,[r3,#0162]
ldrb r2,[r3,#0163]
mov r2,#00
orr r2,#00000012
strb r2,[r3,#0163]

【问题讨论】:

  • 可能不相关(你的代码可以编译,是吗?),但 register 是 C 中的关键字,所以你不应该使用它作为名称。跨度>
  • 我在代码中重命名了一些变量,却忘记了 register 是一个关键字。我编辑了这个问题。我的代码编译了。
  • 如果您尝试 GROUP.myRegister.B.bitfield2 = 0x60; 会发生什么? bitfield1 也会改变吗?
  • 它会产生非常奇怪的结果。寄存器的值变成0x00818181
  • @Mark 没那么奇怪,0x8181 非常接近0x6060 左移两次...

标签: c gcc assembly struct bit-fields


【解决方案1】:

啊,我明白了。编译器使用strb 两次将两个最低有效字节写入特殊功能寄存器。但是硬件每次都执行一个字写入(大概是 32 位),因为不支持对特殊功能寄存器的字节写入。难怪它不起作用!

至于如何解决这个问题,这取决于您的编译器,以及它对 SFR 的了解程度。作为一个快速而肮脏的修复,您可以在R 上使用位操作;而不是

GROUP.myRegister.B.bitfield1 = 0x60;

使用例如

GROUP.myRegister.R = (GROUP.myRegister.R & ~0x3FF) | 0x60;

PS 另一种可能性:您似乎关闭了优化(我看到那里有一条多余的ldrb r2,[r3,#00] 指令)。也许如果你打开它,编译器就会恢复正常?值得一试...

PPS 请将uint64_t 更改为uint32_t。牙疼!

PPPS 想一想,packed 可能会导致编译器关闭,导致它假设位域结构可能不是字对齐的(从而强制逐字节访问)。你试过删除它吗?

【讨论】:

  • 删除__attribute__((packed)) 解决了这个问题。谢谢!
猜你喜欢
  • 2019-05-10
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 2019-08-06
  • 1970-01-01
  • 1970-01-01
  • 2017-06-02
  • 1970-01-01
相关资源
最近更新 更多