在单片机编程的过程中,经常会遇到位操作进行赋值,例如
//程序1
int a = 0,b = 0x5b,c = 0; //1
for (i = 0; i < 8; i ++) { //2
a = b & (0x80 >> i)); //3
c |= (0x80 >> i); //4
}
这些位操作是什么意思呢?
我们写程序将过程打印出来就会一目了然:
//程序2
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char uint8_t;
int main(void) {
uint8_t i = 0, b = 0x5b, b_cover = 0, a = 0, c = 0;
char b_cover2[10], b2[10], a2[10], c2[10], c1[10]; //分别存放其二进制
itoa(b, b2, 2); //将b从十六进制转换为二进制输出,该函数头文件为<stdlib.h>
itoa(c, c1, 2);
printf("b 十六进制 = %x\n", b);
printf("b 二进制 --> %08s\n\n", b2);
for (i = 0; i < 8; i ++) {
printf("i = %d\n", i);
b_cover = 0x80 >> i;
itoa(b_cover, b_cover2, 2);
printf("0x80 >> %d = %08s\n", i, b_cover2);
a = b & (0x80 >> i);
itoa(a, a2, 2);
printf("a = b & (0x80 >> %d) 即:%08s & %08s = %08s\n", i, b2, b_cover2, a2);
c |= (0x80 >> i);
itoa(c, c2, 2);
printf("c |= (0x80 >> %d) 即:%08s |= %08s = %08s\n", i, c1, b_cover2, c2);
printf("\n");
}
return 0;
}
看下程序输出:
可以看到,对于一个字节来说,0x80 >> i [ i 取值0-7] 即为依次将该字节的第7位、第6位、第5位、第4位、……第0位副赋值为1,其余位赋值为0。这样做的目的是方便进行掩码操作,掩码操作就是通过与掩码进行位操作从而达到屏蔽指定位的操作。
现在再看程序1的第三行,a = b & (0x80 >> i)); &操作含义是同1才1,有0则0;不难理解这句代码的意思是b依次和 (0x80 >> i)) 进行&的位操作,如果b的第 i 位为1,则&出的结果第i位保持1;如果b的第i位为0,则&出的结果第i位为0;
同样,对于c值,c |= (0x80 >> i); 表示 c = (c|=(0x80 >> i)),| 操作特性是有1即1,全0才0。但是一般在使用的过程中通常会在前面加一个if语句,即如果接收到1,才会执行该操作,目的是将相应位 置1。
例如在SPI通信中,发送和接收同步进行,常见的代码如下:
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
uint8_t i, ByteReceive = 0x00; //定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
for (i = 0; i < 8; i ++) //循环8次,依次交换每一位数据
{
MySPI_W_MOSI(ByteSend & (0x80 >> i)); //使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线
MySPI_W_SCK(1); //拉高SCK,上升沿移出数据
if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);} //读取MISO数据,并存储到Byte变量
//当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0
MySPI_W_SCK(0); //拉低SCK,下降沿移入数据
}
return ByteReceive; //返回接收到的一个字节数据
}
这时候在看这些代码应该就很易读了吧。