Bit Masking Buffer Index如何导致Wrap

时间:2012-07-23 05:36:45

标签: binary embedded

有人可以解释位掩码如何在循环缓冲区索引方面起作用。具体在以下代码中:

#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的幂?

谢谢

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

如您所见,仅当datamask都为1时,结果才会将位设置为1。

现在回到我们的二进制表示。由于每个位是2的幂,因此使用0中的单个1来表示二进制的2的幂。

01000000 - 64

就像1000 - 1 = 99901000000 - 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,因此效率与模运算相同,我假设..