C语言位运算应用实例

C语言位运算应用实例

正在准备2019年RM比赛,遇到这样一个场景:Tx2图像采集之后要把目标位置传给stm32控制板,位置信息有x/y这一属性,准备用串口做数据传输。

传输数据范围是[0,560]且为整数,但是查了一下发现stm32的串口一次最多读8位数据,也就是[0,255],没有办法,只能把数据拆成两个8位的来传输。

下面根据一应用实例总结C语言中位运算使用。


一、位操作规则

共有6种位操作

运算符含义
&按位与运算
i按位或运算
^按位异或运算
~按位取反运算
<<左移
>>右移
  • 按位运算:指把参与运算的两个数据都换位二进制后,每两个对应位进行运算并得到新二进制数*
  • 若把存储运算结果的变量设为unsigned类型,则所有位都是数据位;否则以补码形式换算为带符号十进制数。
  • 以下都是以unsigned型为例

1、与 (&)

运算结果
0 & 00
0 & 10
1 & 00
1 & 11

规则:有0为0,全1为1

1、实例:

  • 3&5 = 011 & 101 = 001=1

2、常用用法:

  • 对某些位清零。如要对3:0位清零,只需将数据与1111 0000 &运算即可
  • 取数据中某些位。如取0110 0110中2:1位,(0110 0110 & 0000 0110>>1=11=3

2、或 (|)

运算结果
00
01
10
11

规则:有1为1,全0为0

1、实例:

  • 3|5 = 011 | 101 = 111=7

2、常用用法:

  • 对某些位赋值。如给0111 0000的3:0位赋1111:0111 0000|0000 1111 =0111 1111

3、与或 (^)

运算结果
0 ^ 00
0 ^ 11
1 ^ 01
1 ^ 10

规则:相同为0,不同为1

1、实例:

  • 3^5 = 011 ^ 101 = 110 = 6

2、常用用法:

  • 使特定位翻转。将要翻转的位与1异或运算,如翻转1010的1:0位:1010^0011=1001

4、取反 (~)

运算结果
~01
~10

规则:1变0,0变1

1、实例:

  • ~3 = ~011 = 100 = 4

2、常用用法:

  • 使全部位翻转

5、左移、右移(<< 、>>)

运算结果
a<<n把数据a左移n位(换成二进制形式移),溢出部分舍去,末尾补0
a>>n把数据a右移n位(换成二进制形式移),移出末位部分舍去,高位补0

1、实例:

  • 3<<2 = 011<<2 = 01100 = 12
  • 3>>2 = 011>>2 = 0 = 0

2、常用用法:

  • 配合&运算把取出的位移到最低。
  • a<<n == a*2^n
  • a>>n == a/2^n

二、位操作应用实例

要处理文章开头提出的实际问题,设计数据传输格式为:

  • 每帧数据长8位,且表示正整数(unsigned char型)
  • 每两帧数据拼一个真实数据,最多表示12位 ( [0,4095] )
含义
7指示x,y属性(1-x 0-y)
6指示拼数据时高低属性(1-高 0-低)
5:0数据

1、编码

思路:分别取数据 5:0 和 11:6 位域,根据数据属性补充最高两位数据。实例如下:

#include <iostream>
using namespace std;

#define DATA 1200 //待编码数据
#define Low 0     //用于补齐高位信息
#define High 1
#define X_ctrl 1
#define Y_ctrl 0

unsigned char set_flag_bit(unsigned char data,int X_Y,int H_L) //填高2位
{
	data &= 0X3F;  //高2位清零 0x3F=0011 1111 
	
	if(X_Y==Y_ctrl && H_L==Low)		//Y-L 00
		data |= 0X00;				//0x00=0000 0000
	
	else if(X_Y==X_ctrl && H_L==Low)	//X-L 10
		data |= 0X80;				//0x80=1000 0000

	else if(X_Y==Y_ctrl && H_L==High)	//Y-H 01
		data |= 0X40;				//0x40=0100 0000

	else if(X_Y==X_ctrl && H_L==High)	//X-H 11
		data |= 0XC0;				//0xC0=1100 0000
		
	return data;	
}

unsigned char Ldata,Hdata;
void data_processing(int data,int X_Y)
{
	Ldata=data & 0XFF;//取低8(1-8)位 0xFF = 1111 1111 
	Ldata=set_flag_bit(Ldata,X_Y,Low);
	
	Hdata=(data & 0X3FC0)>>6;//取高7-14位 0xFF = 1111 1111 0000 0000
	Hdata=set_flag_bit(Hdata,X_Y,High);
}

int main()
{
	int data = DATA; 
	data_processing(DATA,Y_ctrl);
	
//	cout<<hex;	//选择是否以16进制输出
	cout<<(unsigned int)Hdata<<" "<<(unsigned int)Ldata<<endl;
	return 0;
} 

2、解码

思路:先取数据高二位判断属性,根据其信息拼接数据并存入x、y数据buff中。实例如下:

#include<iostream>
using namespace std;

unsigned int x_real_data_buff[10];//存储x、y数据的buff,用于滑动平均滤波
unsigned int y_real_data_buff[10];
int x_real_data_cnt=0;//buff中数据个数计数
int y_real_data_cnt=0;

unsigned char data_decode(unsigned char data)
{
	unsigned int real_data;
	
	static int x_cnt=0;     
	static int y_cnt=0;     
	static unsigned char Hdata;				
	static unsigned char Ldata;				
	static unsigned char update_flag=0;//标记是否已收到高段数据(是-1,不是-0)
	static float x_aver=0;
	static float y_aver=0;	
	
	unsigned char X_Y_decide;          //1->X
	unsigned char H_L_decide;          //1->H
	
	switch((data & 0xc0)>>6)//0xc0=1100 0000,这里取高2位
	{
		case 0x00:				//00 
			X_Y_decide=0;
			H_L_decide=0;
			Ldata=(data & 0X3F);//0X3F=0011 1111,这里取出该数据帧中有效数据部分
			break;
		
		case 0x01:        //01
			X_Y_decide=0;
			H_L_decide=1,update_flag=1;
			Hdata=(data & 0X3F);
			break;
		
		case 0x02:        //10
			X_Y_decide=1;
			H_L_decide=0;
			Ldata=(data & 0X3F);
			break;
		
		case 0x03:        //11
			X_Y_decide=1;
			H_L_decide=1,update_flag=1;
			Hdata=(data & 0X3F);
			break;	
	}
	
	if(update_flag==1 && H_L_decide==0//已收到高位,又收到低位,一个数据接收完成
	{
		update_flag=0;
		real_data=Hdata;//这里直接左移会溢出丢数据,所以应先给16位的real_data再左移
		real_data=(real_data<<6) | Ldata;	
		
		if(X_Y_decide==1)
			x_real_data_buff[x_real_data_cnt++]=real_data;
		else 
			y_real_data_buff[y_real_data_cnt++]=real_data;
	}
	
}

void show_data(unsigned int *buff,int CNT)
{
	for(int i=0;i<CNT;i++)
		cout<<buff[i]<<" ";
	cout<<endl;
}

int main()
{
	unsigned char data_buff[]={0x52,0x30,0xd2,0xb0};//待解码数据写在这里
	for(int i=0;i<sizeof(data_buff)/sizeof(data_buff[0]);i++)
		data_decode(data_buff[i]);
	
	cout<<"x_data:";
	show_data(x_real_data_buff,x_real_data_cnt);
	
	cout<<"y_data:";
	show_data(y_real_data_buff,y_real_data_cnt);
	
	return 0;
} 
  • 经测试,上述代码可以稳定实现自定义数据协议的编码解码工作。
  • 抗传输丢包效果良好,除非恰好连续丢 “此数据低段” 和“下一数据高段” ,否则不会读取到错误信息。
  • 16
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值