有人可以解释位掩码如何在循环缓冲区索引方面起作用。具体在以下代码中:
#define USART_RX_BUFFER_SIZE 128 /* 2,4,8,16,32,64,128 or 256 bytes */
#define USART_RX_BUFFER_MASK ( USART_RX_BUFFER_SIZE - 1 )
ISR(USART_RX_vect)
{
unsigned char data;
unsigned char tmphead;
/* Read the received data */
data = UDR0;
/* Calculate buffer index */
tmphead = ( USART_RxHead + 1 ) & USART_RX_BUFFER_MASK;
USART_RxHead = tmphead; /* Store new index */
if ( tmphead == USART_RxTail )
{
/* ERROR! Receive buffer overflow */
}
USART_RxBuf[tmphead] = data; /* Store received data in buffer */
}
我知道索引掩盖索引的结果是索引包裹;我的问题是为什么?另外,为什么“USART_RX_BUFFER_SIZE”必须是2的幂?
谢谢
乔
答案 0 :(得分:4)
要理解这一点,你必须理解一些二进制文件,你必须理解二进制操作。
您可能知道,计算机中的所有内容都以二进制形式存储,包含1和0的序列。这意味着理论上可以将内存中的任何数据字符串视为数字。由于您的代码是char
,我将重点关注它们。
在C中,char
s有符号或无符号,为此请务必使用unsigned。我不会进入两个补码表示,但只要说使用签名char
s它会破坏就足够了。 char
是单字节,通常被认为是8位,如下所示:
00000000 -> 0
00001001 -> 9
基本上每个位代表2的幂(我在这里使用MSB优先),所以第二个数字是2^1 + 2^3 = 1 + 8 = 9
。所以你可以看到它如何用于索引数组。
按位运算对某些数据的各个位进行操作。在这种情况下,您使用二进制和(&
),应用二进制和的行为称为位掩码。
data - 00101100
mask - 11110110
----------
result - 00101100
如您所见,仅当data
和mask
都为1时,结果才会将位设置为1。
现在回到我们的二进制表示。由于每个位是2的幂,因此使用0中的单个1来表示二进制的2的幂。
01000000 - 64
就像1000 - 1 = 999
,01000000 - 1 = 00111111
一样,其中00111111
为63。
使用它我们可以发现在计算下一个索引时,我们执行以下操作:
(a + 1) & 00111111
如果a是(例如)10,那么我们得到
(00001010 + 1) = 00001011 (11)
00001011 & 00111111 = 00001011
因此掩蔽没有改变,但在63的情况下:
(00111111 + 1) = 01000000 (64)
01000000 & 00111111 = 00000000 (0)
所以,不要试图索引到64(这是第65个元素,因此是一个错误),而是回到开头。
这就是为什么缓冲区大小必须是2的幂,如果不是那么掩码不能正确计算你必须使用modulo(%
)或比较,而不是比特掩蔽。这很重要,因为按位运算符非常快,因为它们通常只是大多数处理器中的单个指令,&
只需要很少的周期。模数可以是单个指令,但它可能是整数除法,并且在大多数平台上传统上都很慢。比较需要几个指令,寄存器和至少一次跳转。
答案 1 :(得分:-1)
JOhn写道:
我是关于模数的评论,我们在我们的微观上找到了它 像“a%= MODULUS”那样做10-20倍的事情比做a(a> b)更长 一个/ =模量 - 约翰U于2012年8月3日17:01
但仍有分裂:a / = MODULUS,因此效率与模运算相同,我假设..