Bootstrap

STM32编写代码之嵌入式常用位操作

在单片机编程的过程中,经常会遇到位操作进行赋值,例如

//程序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;								//返回接收到的一个字节数据
}

这时候在看这些代码应该就很易读了吧。

;