Binary 位屏蔽缓冲区索引如何导致环绕

Binary 位屏蔽缓冲区索引如何导致环绕,binary,embedded,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

有人能用循环缓冲区索引来解释位屏蔽是如何工作的吗。特别是在以下代码中:

#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\u RX\u BUFFER\u SIZE”必须是2的幂

多谢各位


Joe

要理解这一点,你必须理解一些二进制代码,你必须理解二进制操作

你可能知道,计算机中的所有东西都是以二进制存储的,即1和0的序列。这意味着从理论上讲,内存中的任何数据字符串都可以被视为数字。由于您的代码使用的是
char
s,因此我将重点介绍它们

在C语言中,
char
s要么是有符号的,要么是无符号的,因此使用unsigned是很重要的。我不会讨论二的补码表示法,但我只想说,如果使用带符号的
char
s,它将被破坏。字符是一个单字节,通常被认为是8位,如下所示:

00000000 -> 0
00001001 -> 9
基本上,每一位代表二的幂(我在这里首先使用MSB),所以第二个数字是
2^1+2^3=1+8=9
。因此,您可以看到如何使用它来索引到数组中

按位操作对某些数据的各个位进行操作。在这种情况下,您使用的是二进制
&
),应用二进制的行为称为位掩蔽

data   - 00101100
mask   - 11110110
       ----------
result - 00101100
如您所见,仅当
数据
掩码
都有1时,结果的位才设置为1

现在回到我们的二进制表示。由于每一位是二的幂,二进制中的二的幂可以用0中的一个1表示

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的幂,如果不是,则掩码将无法正确计算,并且必须使用模(
%
)或比较,而不是位掩码。这一点很重要,因为位运算符速度非常快,因为在大多数处理器中它们通常只是一条指令,
&
需要很少的周期。模可以是一条指令,但它可能是整数除法,在大多数平台上,这通常相当慢。比较需要几个指令、寄存器和至少一个跳转。

要理解这一点,你必须理解一些二进制代码,你必须理解二进制操作

你可能知道,计算机中的所有东西都是以二进制存储的,即1和0的序列。这意味着从理论上讲,内存中的任何数据字符串都可以被视为数字。由于您的代码使用的是
char
s,因此我将重点介绍它们

在C语言中,
char
s要么是有符号的,要么是无符号的,因此使用unsigned是很重要的。我不会讨论二的补码表示法,但我只想说,如果使用带符号的
char
s,它将被破坏。字符是一个单字节,通常被认为是8位,如下所示:

00000000 -> 0
00001001 -> 9
基本上,每一位代表二的幂(我在这里首先使用MSB),所以第二个数字是
2^1+2^3=1+8=9
。因此,您可以看到如何使用它来索引到数组中

按位操作对某些数据的各个位进行操作。在这种情况下,您使用的是二进制
&
),应用二进制的行为称为位掩蔽

data   - 00101100
mask   - 11110110
       ----------
result - 00101100
如您所见,仅当
数据
掩码
都有1时,结果的位才设置为1

现在回到我们的二进制表示。由于每一位是二的幂,二进制中的二的幂可以用0中的一个1表示

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的幂,如果不是,则掩码将无法正确计算,并且必须使用模(
%
)或比较,而不是位掩码。这一点很重要,因为位运算符速度非常快,因为在大多数处理器中它们通常只是一条指令,
&
需要很少的周期。模可以是一条指令,但它可能是整数除法,在大多数平台上,这通常相当慢。比较需要几个指令、寄存器和至少一个跳转。

约翰写道:


我支持关于模的评论,我们在我们的微计算机上发现了它 “a%=模数”比(a>b)长10-20倍 a/=模数;–约翰大学2012年8月3日17:01

但仍然存在除法:a/=模,因此效率与模运算相同,我假设..

约翰写道:


我支持关于模的评论,我们在我们的微计算机上发现了它 “a%=模数”比(a>b)长10-20倍 a/=模数;–约翰大学2012年8月3日17:01


但仍然有除法:a/=模,因此效率和模运算相同,我想。

很好的答案,我现在明白了!!谢谢