我正在查看 NIC 的数据表规范,它说:
寄存器的位 2:3 包含 NIC 速度,4 包含链接状态等。如何使用按位隔离这些位?
例如,我已经看到了隔离链接状态的代码,类似于:
(link_reg & (1 << 4))>>4
但我不太明白为什么正确的转变。我必须说,我仍然对按位运算不太满意,即使我了解如何转换为二进制以及每个操作的作用,但它并不实用。
我正在查看 NIC 的数据表规范,它说:
寄存器的位 2:3 包含 NIC 速度,4 包含链接状态等。如何使用按位隔离这些位?
例如,我已经看到了隔离链接状态的代码,类似于:
(link_reg & (1 << 4))>>4
但我不太明白为什么正确的转变。我必须说,我仍然对按位运算不太满意,即使我了解如何转换为二进制以及每个操作的作用,但它并不实用。
这取决于你想用那个位做什么。链接状态,称之为 L 在某处的变量/寄存器中
43210
xxxxLxxxx
要使用 1 隔离您想要的那个位,按位运算:
xxLxxxx
& 0010000
=========
00L0000
1<<4 = 1 有 4 个零或 0b10000,你想要的数字。
status&(1<<4)
这将给出零或 0b10000 的结果。您可以进行布尔比较以确定它是假(零)还是真(非零)
if(status&(1<<4))
{
//bit was on/one
}
else
{
//bit was off/zero
}
如果您希望结果为 1 或零,则需要将结果移至个列
(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
最容易看一些二进制数。
这是一个可能的寄存器值,下面是位索引:
00111010
76543210
所以,第 4 位是 1。我们如何得到那个位?我们构造一个仅包含该位的掩码(我们可以通过将 1 移到正确的位置来做到这一点,即1<<4
),并使用&
:
00111010
& 00010000
----------
00010000
但我们想要一个 0 或 1。所以,一种方法是将结果向下移动:00010000 >> 4 == 1
。另一种选择是!!val
,它将 0 变为 0 并将非零变为 1(请注意,这仅适用于单个位,而不是像链接速度这样的两位值)。
现在,如果您想要位 3:2,您可以使用设置了这两个位的掩码。您可以写入3 << 2
获取00001100
(因为 3 设置了两个位)。然后我们&
用它:
00111010
& 00001100
----------
00001000
并向下移动 2 以获得10
所需的两位。因此,获得两位链接速度的语句将是(link_reg & (3<<2))>>2
.
您可以使用它来确定位置的位pos
是否设置在val
:
#define CHECK_BIT(val, pos) ((val) & (1U<<(pos)))
if (CHECK_BIT(reg, 4)) {
/* bit 4 is set */
}
如果两个操作数的相应位设置为 1,则按位与运算符 (&) 将结果中的每个位设置为 1。否则,结果位为 0。
如果您想将第 2 位和第 3 位(从 0 开始计数)视为一个数字,您可以这样做:
unsigned int n = (link_get & 0xF) >> 2;
按位加 15(二进制为 0b1111)将除底部四位以外的所有位设置为零,然后右移 2 位得到第 2 位和第 3 位中的数字。
问题是隔离位是不够的:您需要移动它们以获得正确的值大小顺序。
在您的示例中,您的大小为 2 位和 3 位(我假设最低有效位为 0 位),这意味着它是 [0,3] 范围内的值。reg & (0x03<<2)
现在您可以使用or, 转换来屏蔽这些位,(reg & 0x12)
但这还不够:
reg 0110 1010 &
0x12 0000 1100
---------------
0x08 0000 1000
如您所见,结果1000b
是 8,超出范围。要解决这个问题,您需要将结果移回,以便您感兴趣的值的最低有效位对应于包含字节的最低有效位:
0000 1000 >> 2 = 10b = 3
现在是正确的。