使用移位寄存器的位运算

Bitwise Operations using shift registers

我开始学习更多关于 AVR ATMEGA 编程的知识。
阅读一篇关于 Arduinos 内部工作原理的文章,我正在学习 shiftOut 方法的结构。到目前为止我对按位运算有点熟悉,但是我有一个表达式我还不明白:

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
    uint8_t i;

    for (i = 0; i < 8; i++)  {
        if (bitOrder == LSBFIRST) {
            PORTD |= !!(val & (HIGH << i)); 
        } else {
            PORTD |= !!(val & (HIGH << (7 - i)));           
        }   
        PORTB |= (HIGH << clockPin);
        PORTB ^= (HIGH << clockPin);
    }
}

PORTD |= !!(val & (HIGH << i)); 行对我来说并不是 100% 清楚。我知道我在 PORTD 上设置了第 i 个高位,但是 !!val&(HIGH <<i))

是什么意思

我知道这听起来很基础,但你能帮帮我吗?

试试吧:

#include <stdio.h>
#define HIGH 0
int main ( void )
{

    unsigned char i;
    unsigned char PORTD;
    unsigned char val;

    PORTD=0;

    for(val=3;val;val<<=1)
    {
        printf("0x%02X\n",val);
        for(i=0;i<8;i++)
        {
            PORTD |= !!(val & (HIGH << i));
            printf("PORTD 0x%02X\n",PORTD);
        }
    }
    return(0);
}        

我不知道你对 HIGH 的定义是什么,但是因为他们在一个字节上走了 8 次,我假设它是一位,它可能允许端口上的正逻辑或负逻辑,所以可以是 0或 1 我在想。

如果 HIGH 为 1,我得到所有 0x01,如果 HIGH 为 0,我得到所有 0x00。所以它似乎没有做任何事情。例如,当你有 && 时,它是一个布尔表达式而不是按位逻辑,所以这可能是一个错字?

val&(HIGH<<i) 虽然非常简单,但您应该再次发布 HIGH 的定义,让我们假设它是 1,因为相对于代码的其余部分,这最有意义。有一个从 0 到 7 的循环 i。所以这意味着你正在和 1<<0 然后 1<<1 然后 1<<2 等等(按位)。 1<<0 就是 1 对吧?所以 val&(1<<0) 与 val&0x01 相同。 1<<1 = 0x02 所以 val&0x02。此代码一次一位,将 val 中的各个位隔离开来。你知道如果 val 是 0x07 那么 0x07&0x02 = 0x02?你把每一位排列起来 0x07 = 0b00000111, 0x02 = 0b00000010

 00000111
&00000010
=========
 00000010

你看看这个,一次垂直隔离一列,并使用 AND truth table 基本上说结果是 0,除非两个操作数都是 1,有一列两个操作数都是 1因此该列的结果为 1,其余列的一个或另一个操作数或两者均为零,因此该列的结果为零。

 00000111
&00000100
=========
 00000100

 00000111
&000010000
=========
 00000000

将 i 再增加两次并根据 0x07 的 val 进行评估,你至少会看到 val&(HIGH<<i) 发生了什么假设 HIGH 是 1,如果 HIGH 是 0 那么你将始终得到零这段代码出来的。

如果你想和一个步行者一起,为什么不做 val&HIGH,所以这再次没有意义,除非那个外围设备或那个端口另一端的东西一次需要这个。

双爆炸 (!!) 对我来说看起来像是一个合乎逻辑的操作,与 IMO 无关。

单次爆炸是合乎逻辑的操作

#include <stdio.h>

#define HIGH 1

int main ( void )
{

    unsigned char i;
    unsigned char PORTD;
    unsigned char val;

    PORTD=0;

    val=3;
    {
        printf("0x%02X\n",val);
        for(i=0;i<8;i++)
        {
            PORTD |= !(val & (HIGH << i));
            printf("PORTD 0x%02X\n",PORTD);
        }
    }
    return(0);
}

这里的希望是我们知道编译器将为假生成 0x00,但它为真生成什么? 0x01、0xFF,C语言大概有定义。所以上面的代码是根据我们的值

生成一个比特流
PORTD 0x00
PORTD 0x00
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01

在 lsbit 位置,循环使时钟发痒。

我个人的偏好不是用 C 语言规范玩游戏,而是明确说明你想做什么:

#include <stdio.h>
#define HIGH 1
int main ( void )
{

    unsigned char i;
    unsigned char PORTD;
    unsigned char val;

    PORTD=0;

    val=3;
    {
        printf("0x%02X\n",val);
        for(i=0;i<8;i++)
        {
            PORTD |= (~(val>>i))&1;
            printf("PORTD 0x%02X\n",PORTD);
        }
    }
    return(0);
}        

PORTD 0x00
PORTD 0x00
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01

使用所有按位运算,没有布尔 true/false 运算。

想知道他们是否试图连续执行两个布尔非操作以将非零位移动到零位位置或其他...而不是 lsbit first 或 7-i for msbit first。

#include <stdio.h>
#define HIGH 1
int main ( void )
{

    unsigned char i;
    unsigned char PORTD;
    unsigned char val;

    PORTD=0;

    val=3;
    {
        printf("0x%02X\n",val);
        for(i=0;i<8;i++)
        {
            PORTD |= (val>>i)&1;
            printf("PORTD 0x%02X\n",PORTD);
        }
    }
    return(0);
}