【问题标题】:Bit fields for reading from H/W registers用于从 H/W 寄存器读取的位域
【发布时间】:2013-07-09 06:24:49
【问题描述】:

我想从 32 位寄存器中读取第 2、第 5 和第 6 位。我决定使用 struct bit fields 来存储它们。下面的数据结构正确吗?

struct readData
{
    int unwanted:1;
    int reqbit1:1;
    int unwanted1:2;
    int reqbit2:2;
    int unwanted2:26;
};

我不确定位域是如何创建的。我将使用一个 API,它将字节从 h/w 寄存器直接复制到这个结构中。在那种情况下, reqbit1 会包含第二位吗?根据我的理解,编译器将第一位分配给一个 int 变量,第二位分配给另一个 int 变量,因此 reqbit1 不会从寄存器读取任何数据。下面的联合不是更适合这种情况吗?

union readData
{
    struct readBits
    {
        bool unwanted:1;
        bool reqbit1:1;
        xxx unwanted1:2;
        short reqbit2:2;
        xxx unwanted2:26;
    };

    int regValue;
};

如果这是正确的,我应该将不需要的 2 声明为什么?

【问题讨论】:

  • 使用 1 位有符号位域通常是一个非常糟糕的主意。您可以在那里存储的值将只是-10,这只是令人困惑。让他们unsigned
  • @unwind 如果我只是将结构中的所有 int 更改为 unsigned int,可以吗?我不必使用联合来读取寄存器吗?

标签: c bit-manipulation bitwise-operators unions bit-fields


【解决方案1】:

通常使用以下联合:

union readData {
   struct {
      unsigned int unwanted:1;
      unsigned int reqbit1:1;
      unsigned int unwanted1:2;
      unsigned int reqbit2:2;
      unsigned int unwanted2:26;
   } readBits;
   unsigned int regValue;
};

编辑:

用法是:

 #define REG_ADDRESS 0x12345678 // e.g.

 union readData* rd = (union readData*)REG_ADDRESS;

 char bit2 = rd->readBits.reqbit1;
 char bits56 = rd->readBits.reqbit2;

【讨论】:

  • 这个联合和我声明的结构有什么区别?我可以将我的结构类型转换为一个 int 指针并从寄存器中读取值,对吧?
  • @linuxfreak 首先,寄存器的类型通常是unsigned integer,所以我更喜欢在位域中使用这种类型。第二,请看编辑(答案不适合评论)
  • 我的问题是我的结构 struct readData 有什么问题?
【解决方案2】:

来自 C 标准:“单元内位域的分配顺序(高位到低位或低位到高位)是实现定义的。”

所以你不应该在顺序重要的地方使用位域。

改用显式掩码和移位:

reqbit1 = (w >> 1) & 1;    
reqbit2 = (w >> 4) & 3;

reqbit1 = (w & 0x00000002) >> 1;    
reqbit2 = (w & 0x00000010) >> 4;

另外一个方向

w = (reqbit1 << 1) | (reqbit2 << 4);

“不需要的”部分通常命名为 reserved1 等等。

【讨论】:

  • 在嵌入式系统中,联合体和结构体经常以这种方式使用。
【解决方案3】:

为什么不使用sizeof() 并检查分配给每个结构成员的字节数? 结构体成员alignment can be found out here

为什么要将 h/w 寄存器复制到结构中,然后处理结构位?可能会出现 h/w 寄存器使用新值更新但结构仍未更新的情况。

如果要读取第2、5、6位,可以通过以下方式实现:

#define  myregister  ((unsigned long*)address of register)   
unsigned long ret=0;
ret = myregister & (1<<2);
if(ret)
   { ... }
ret = myregister & (1<<5);
if(ret)
   { ... }                   //... and so on

恕我直言,亲吻(保持简单,愚蠢)。

【讨论】:

  • 当然,这是最简单的解决方案。
  • @D.7 我无法直接从寄存器中读取。我必须调用一个用寄存器数据填充结构的 API。我的问题是我声明的结构是否。 struct readData 会正确包含这些位吗?
  • 为了获得更大的信心,您可以使用'attribute__((__packed))。在您的联合中,如果在内存中分配了 4 个字节,则“regValue”可以是 int 类型。这将确保至少分配不超过 4 字节的内存。否则使用 long。
猜你喜欢
  • 1970-01-01
  • 2012-04-11
  • 2010-10-13
  • 2020-12-14
  • 2020-11-13
  • 2022-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多