【问题标题】:reading 2 bits off a register从寄存器中读取 2 位
【发布时间】:2012-10-17 11:45:05
【问题描述】:

我正在查看 NIC 的数据表规范,上面写着:

寄存器的位 2:3 包含 NIC 速度,4 包含链接状态等。如何使用按位隔离这些位?

例如,我看到隔离链接状态的代码类似于:

(link_reg & (1 >4

但我不太明白为什么是正确的转变。我必须说,我仍然对按位运算不太满意,即使我了解如何转换为二进制以及每个运算的作用,但它并不实用。

【问题讨论】:

    标签: c bit-manipulation


    【解决方案1】:

    这取决于你想用那个位做什么。链接状态,称它为 L 在某处的变量/寄存器中

        43210
    xxxxLxxxx
    

    用 1 隔离你想要的那个位,按位运算:

      xxLxxxx
    & 0010000
    =========
      00L0000
    

    1

    status&(1<<4) 
    

    这将给出零或 0b10000 的结果。您可以进行布尔比较以确定它是假(零)还是真(非零)

    if(status&(1<<4))
    {
       //bit was on/one
    }
    else
    {
       //bit was off/zero
    }
    

    如果你想让结果为 1 或 0,你需要将结果移到个列

      (0b00L0000 >> 4) = 0b0000L
    

    如果 and 的结果为零,则移位仍然为零,如果结果为 0b10000,则右移 4 给出 0b00001

    所以

    (status&(1<<4))>>4 gives either a 1 or 0;
    
    (xxxxLxxxx & (00001<<4))>>4 =
    (xxxxLxxxx & (10000))>>4 = 
    (0000L0000) >> 4 = 
    0000L
    

    另一种使用更少操作的方法是

    (status>>4)&1;
    xxxxLxxxx >> 4 = xxxxxxL
    xxxxxxL & 00001 = 00000L
    

    【讨论】:

      【解决方案2】:

      最容易查看一些二进制数。

      这是一个可能的寄存器值,下面是位索引:

        00111010
        76543210
      

      所以,第 4 位是 1。我们如何得到那个位?我们构造一个只包含那个位的掩码(我们可以通过将 1 移到正确的位置来做到这一点,即1&lt;&lt;4),并使用&amp;

        00111010
      & 00010000
      ----------
        00010000
      

      但我们想要 0 或 1。因此,一种方法是将结果向下移动:00010000 &gt;&gt; 4 == 1。另一种选择是!!val,它将 0 变为 0,将非零变为 1(请注意,这仅适用于单个位,而不适用于链接速度等两位值)。

      现在,如果您想要位 3:2,您可以使用设置了这两个位的掩码。你可以写3 &lt;&lt; 2 来得到00001100(因为 3 有两位设置)。然后我们&amp; 用它:

        00111010
      & 00001100
      ----------
        00001000
      

      并向下移动 2 得到10,即所需的两位。因此,获得两位链接速度的语句将是(link_reg &amp; (3&lt;&lt;2))&gt;&gt;2

      【讨论】:

      • 升档时,您通常需要确保您正在换档unsigned 号码。这将在此答案中将升档更改为3U &lt;&lt; 21U &lt;&lt; 4。否则,当您升档足够大时(例如,如果您的 int 是 32 位类型,则为 31),您将面临有符号溢出及其伴随的未定义行为的风险。
      • 感谢您的解释,但您能否更好地解释一下有关检查值中任意位范围(特别是 2:3)的这一点?
      【解决方案3】:

      如果您想将第 2 位和第 3 位(从 0 开始计数)视为一个数字,您可以这样做:

      unsigned int n = (link_get & 0xF) >> 2;
      

      按位加 15(二进制为 0b1111)将除底部四位以外的所有位设置为零,然后右移 2 得到位 2 和位 3 中的数字。

      【讨论】:

        【解决方案4】:

        您可以使用它来确定pos 位置的位是否在val 中设置:

        #define CHECK_BIT(val, pos) ((val) & (1U<<(pos)))
        
        if (CHECK_BIT(reg, 4)) {
            /* bit 4 is set */
        }
        

        如果两个操作数的相应位设置为 1,则按位与运算符 (&) 将结果中的每个位设置为 1。否则,结果位为 0。

        【讨论】:

          【解决方案5】:

          问题在于隔离位是不够的:您需要移动它们以获得正确的值大小顺序。

          在您的示例中,您的大小为第 2 位和第 3 位(我假设最低有效位是第 0 位),这意味着它是 [0,3] 范围内的值。现在您可以使用reg &amp; (0x03&lt;&lt;2) 或转换后的(reg &amp; 0x12) 屏蔽这些位,但这还不够:

          reg   0110 1010 &
          0x12  0000 1100
          ---------------
          0x08  0000 1000
          

          如您所见,结果为1000b,即8,超出范围。要解决这个问题,您需要将结果移回,以便您感兴趣的值的最低有效位对应于包含字节的最低有效位:

          0000 1000 >> 2 = 10b = 3
          

          现在是正确的。

          【讨论】:

          • 最小位通常被称为“最不重要的重要位”,而不是最不相关的位。它与 MSB 一样重要 ;)
          • 不幸的是英语不是我的母语,在我自己的语言中,重要的可以在许多情况下用来代替类似的语言,这就是为什么:)
          猜你喜欢
          • 2012-04-11
          • 2010-10-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-13
          • 2022-07-18
          • 1970-01-01
          • 2020-12-14
          相关资源
          最近更新 更多