C语言位运算应用实例
正在准备2019年RM比赛,遇到这样一个场景:Tx2图像采集之后要把目标位置传给stm32控制板,位置信息有x/y这一属性,准备用串口做数据传输。
传输数据范围是[0,560]且为整数,但是查了一下发现stm32的串口一次最多读8位数据,也就是[0,255],没有办法,只能把数据拆成两个8位的来传输。
下面根据一应用实例总结C语言中位运算使用。
一、位操作规则
共有6种位操作
| 运算符 | 含义 |
|---|---|
| & | 按位与运算 |
| i | 按位或运算 |
| ^ | 按位异或运算 |
| ~ | 按位取反运算 |
| << | 左移 |
| >> | 右移 |
- 按位运算:指把参与运算的两个数据都换位二进制后,每两个对应位进行运算并得到新二进制数*
- 若把存储运算结果的变量设为unsigned类型,则所有位都是数据位;否则以补码形式换算为带符号十进制数。
- 以下都是以unsigned型为例
1、与 (&)
| 运算 | 结果 |
|---|---|
| 0 & 0 | 0 |
| 0 & 1 | 0 |
| 1 & 0 | 0 |
| 1 & 1 | 1 |
规则:有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、或 (|)
| 运算 | 结果 |
|---|---|
| 0 | 0 |
| 0 | 1 |
| 1 | 0 |
| 1 | 1 |
规则:有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 ^ 0 | 0 |
| 0 ^ 1 | 1 |
| 1 ^ 0 | 1 |
| 1 ^ 1 | 0 |
规则:相同为0,不同为1
1、实例:
- 3^5 = 011 ^ 101 = 110 = 6
2、常用用法:
- 使特定位翻转。将要翻转的位与1异或运算,如翻转1010的1:0位:1010^0011=1001
4、取反 (~)
| 运算 | 结果 |
|---|---|
| ~0 | 1 |
| ~1 | 0 |
规则:1变0,0变1
1、实例:
- ~3 = ~011 = 100 = 4
2、常用用法:
- 使全部位翻转。
5、左移、右移(<< 、>>)
| 运算 | 结果 |
|---|---|
| a<
|
把数据a左移n位(换成二进制形式移),溢出部分舍去,末尾补0 |
| a>>n | 把数据a右移n位(换成二进制形式移),移出末位部分舍去,高位补0 |
1、实例:
- 3<<2 = 011<<2 = 01100 = 12
- 3>>2 = 011>>2 = 0 = 0
2、常用用法:
- 配合&运算把取出的位移到最低。
- a<
- 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
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<
cout
<<
(
unsigned
int
)Hdata
<<
" "
<<
(
unsigned
int
)Ldata
<<endl
;
return
0
;
}
2、解码
思路:先取数据高二位判断属性,根据其信息拼接数据并存入x、y数据buff中。实例如下:
#include
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; }
- 经测试,上述代码可以稳定实现自定义数据协议的编码解码工作。
- 抗传输丢包效果良好,除非恰好连续丢 “此数据低段” 和“下一数据高段” ,否则不会读取到错误信息。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/217245.html原文链接:https://javaforall.net
