嵌入式linux C语言(一)——位运算的使用

2019-07-12 18:33发布

   嵌入式linux C语言(一)——位运算的使用    ARM是内存与IO统一编址,SoC中有很多控制寄存器,通过对这些寄存器进行位运算对这些控制寄存器进行设置,进而控制外设功能。在修改寄存器某些位的过程中不能修改其他的位。

一、位运算基础

C语言基本的位操作符有与、或、异或、取反、左移、右移六种位运算符。如下表所示:符号 描述 运算规则                        &       两个位都为1时,结果才为1|       两个位都为0时,结果才为0^     异或两个位相同为0,相异为1~    取反0110<<  左移各二进位全部左移若干位,高位丢弃,低位补0>> 右移各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)位运算使用说明:1、六种位运算只能用于整型数据,对float和double类型进行位操作会被编译器报错。
2、逻辑运算与位运算的区别:逻辑运算是将参与运算的两个表达式整体的结果进行逻辑运算,而位运算是将参与运算的两个数据,按对应的二进制数逐位进行逻辑运算。逻辑运算符有逻辑与&&、逻辑或||、逻辑非!,位运算则有六种运算符,位与&、位或|、位异或^、位取反~、位左移<<、位右移>>
3如果左移位数>=类型长度,在GCC环境下,GCC编译器会报警告,但实际左移位数为左移位数%(8 * sizeof(int))。例如:int i = 1, j = 0x80000000; //int32i = i << 33;   // 33 % 32 = 1 左移1,i变成2j = j << 33;   // 33 % 32 = 1 左移1,j变成0,最高位被丢弃 4C语言中,左移是逻辑/算术左移(两者完全相同),右移是算术右移,会保持符号位不变。左移时总是移位和补零。右移时无符号数是移位和补零,此时称为逻辑右移;而有符号数大多数情况下是移位和补最左边的位(也就是补最高有效位),移几位就补几位,此时称为算术右移。 算术移位是相对于逻辑移位,它们在左移操作中都一样,低位补0即可,但在右移中逻辑移位的高位补0而算术移位的高位是补符号位右移对符号位的处理和左移不同,对于有符号整数来说,比如int类型,右移会保持符号位不变。符号位向右移动后,正数的话补0,负数补1,也就是汇编语言中的算术右移。当移动的位数超过类型的长度时,会取余数,然后移动余数个位int i = 0x80000000;i = i >> 1;  //i的值不会变成0x40000000,而会变成0xc0000000
5位操作符的运算优先级比较低,因为尽量使用括号来确保运算顺序,否则很可能会得到莫明其妙 的结果。比如要得到像1,3,5,9这些2^i+1的数字。写成int a = 1 << i + 1;是不对的,程序会先执行i + 1,再执行左移操作。应该写成int a = (1 << i) + 1

二、位运算的使用

1、位与运算&

    位与运算的实质是将参与运算的两个数据,按对应的二进制数逐位进行逻辑与运算。典型应用如下:A特定数据段清零快速对某一段数据单元的数据清零unsigned int a = 0x00FF1278;a &= 0xFFFF0FFF;//abit12--bit15位进行清零,a=0x00FF0278//a &= ~(0xF<<12) ;//abit12--bit15位进行清零,a=0x00FF0278 B保留数据区的特定位unsigned int a = 0x00FF1278;a &= (0xF<<12);//保留abit12--bit15位,其他清零,a=0x00001000 C、判断奇偶数    只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数 

2、位或运算符|

位或运算的实质是将参与运算的两个数据,按对应的二进制数逐位进行逻辑或运算。典型应用如下:A对数据位置1unsigned int a = 0x00FF0278;a |= 0x0000F000;//a的第12-15位置1a=0x00FFF278//a |= (0xF<<12);//a的第12-15位置1a=0x00FFF278 

3、位异或^

    位异或运算的实质是将参与运算的两个数据,按对应的二进制数逐位进行逻辑异或运算。只有当对应位的二进制数互斥的时候,对应位的结果才为真。典型应用如下:A、特定位取反    设定一个数据的指定位,将1换为00换为1。例如整型数a=321,,将其低八位数据进行翻位的操作为a=a^0XFFB、数值交换a=a^b;b=b^a;a=a^b;不使用第三方变量可以用位操作来实现交换两数

4、位非~

位非运算的实质是将参与运算的两个数据,按对应的二进制数逐位进行逻辑非运算。A、变换符号变换符号只需要取反后加1

5、位左移<<

左移运算的实质是将对应的数据的二进制值逐位左移若干位,并在空出的位置上填0,最高位溢出并舍弃。

6、位右移>>

位右移运算的实质是将对应的数据的二进制值逐位右移若干位,并舍弃出界的数字。如果当前的数为无符号数,高位补零。如果当前的数据为有符号数,在进行右移的时候,根据符号位决定左边补0还是补1。如果符号位为0,则左边补0;但是如果符号位为1,则根据不同的计算机系统,可能有不同的处理方式。可以看出位右移运算,可以实现对除数为2的整除运算。提示 将所有对2的整除运算转换为位移运算,可提高程序的运行效率。A、求绝对值int i = a >> 31;  return i == 0 ? a : (~a + 1);  int i = a >> 31;  return ((a ^ i) - i); 

7、嵌入式开发中常用位操作

A将寄存器指定位(第n位置为1GPXX |= (1<GPXX |= (1<< 7) | (1<< 4 ) | (1<< 0)//047位置1,其他保留B将寄存器指定位(第n置为0GPXX &= ~1<将寄存器的第n位清0,而又不影响其它位的现有状态。GPXX &= ~1<<4 )C、嵌入式开发位操作实例    unsigned int i = 0x00ff1234;    //i |= (0x1<<13);//bit131    //i |= (0xF<<4);//bit4-bit71    //i &= ~(1<<17);//清除bit17     //i &= ~(0x1f<<12);//清除bit12开始的5        //取出bit3-bit8    //i &= (0x3F<<3);//保留bit3-bit8,其他位清零    //i >>= 3;//右移3     //给寄存器的bit7-bit17赋值937    //i &= ~(0x7FF<<7);//bit7-bit17清零    //i |= (937<<7);//bit7-bit17赋值     //将寄存器bit7-bit17的值加17   // unsigned int a = i;//a作为i的副本,避免i的其他位被修改   // a &= (0x7FF<<7);//取出bit7-bit17   //a >>= 7;//   // a += 17;//17   // i &= ~(0x7FF<<7);//ibit7-bit17清零   // i |= (a<<7);//+17后的数写入bit7-bit17,其他位不变     //给一个寄存器的bit7-bit17赋值937,同时给bit21-bit25赋值17    i &= ~((0x7FF<<7) | (0x1F<<21));//bit7-bit17bit21-bit25清零    i |= ((937<<7) | (17<<21));//bit7-bit17bit21-bit25赋值 

三、位操作的宏定义

//用宏定义将32位数x的第n(bit0为第1)置位#define SET_BIT_N(x,n) (x | (1U<<(n-1)))//用宏定义将32位数x的第n(bit0为第1)清零#define CLEAR_BIT_N(x,n) (x & (~(1U<<(n-1))))//用宏定义将32位数x的第n位到第m(bit0为第1)置位#define SET_BITS_N_M(x,n,m) (x | (((~0U)>>(32-(m-n+1)))<<(n-1)))//用宏定义将32位数x的第n位到第m(bit0为第1)清零#define CLEAR_BITS_N_M(x,n,m) (x & (~(((~0U)>>(32-(m-n+1)))<<(n-1))))//用宏定义获取32位数x的第n位到第m(bit0为第1)的部分#define GET_BITS_N_M(x,n,m) ((x & ~(~(0U)<<(m-n+1))<<(n-1))>>(n-1))
本文出自 “生命不息,奋斗不止” 博客,转载请与作者联系!