C语言基础之位运算(一)
C语言基础之位运算(二)
C语言基础之位运算(三)
1.寄存器的读写
- ARM中内存与IO是统一编址的,ARM中有很多内部外设,Soc中CPU通过这些外设的寄存器写入一些特定的值来操控这个内部外设,进而操控硬件设备动作。即读、写寄存器就是操控硬件。
- 寄存器的特点是:按位进行规划和使用,读、写是32位整体进行操作的;
- 寄存器读写的要求:在改变特定位时,不可改变和影响其他位;
- 变更寄存器中特定值的步骤:
读
-改
-写
;
读
:要变更寄存器中某些特定位,先读取寄存器中整体原来的值;
改
:修改读取的整体原来值中的某些特定位;
写
:将修改过特定位的整体,统一写入寄存器;
-
注意
:读取一次寄存器的值是32位的,不论修改几位特定值,都必须整体读出32位的内容,在不影响其他原来位值的情况下,将目标特定位进行修改,然后统一写入寄存器;
2.寄存器的读写方式
使用C语言对寄存器赋值时,常常需要用到C语言的位操作符。常用的操作手法有以下几种:
2.1、把寄存器某位清零 &
若希望寄存器中某些特定位变成0,而不影响其他位,可以构造一个合适的二进制数和寄存器中的值进行位与&操作,实现特定位清零。
位与&运算符特点:(二进制数)与1位与无变化;与0位与变为0;
把0xaaaa aaaa的低8-15位清零。
第一步
:要把0xaaaa aaaa这个十六进制数的低8到15位清零,就得让8-15位和0位与,而其他位不能发生变化;
第二步
:要得到一个十六进制数,让该数的8-15位是0,其他位是1,即二进制数的0-31位是:1111 1111 1111 1111 0000 0000 1111 1111;
第三步
:把这个二进制数转换为十六进制数得到0xffff 00ff;
第四步
:让寄存器中的目标数(0xaaaa aaaa)和清零指定位(0xffff 00ff)进行位与,即可完成8-15位清零;//1010 1010 1010 1010 '1010 1010' 1010 1010 十六进制一位等于二进制四位
a = 0xaaaa aaaa;
//1111 1111 1111 1111 '0000 0000' 1111 1111
b = 0xffff 00ff;
c = a & b;
//1010 1010 1010 1010 '0000 0000' 1010 1010 这样8-15位就被清零了, 结果为0xaaaa 00aa
c == 0xaaaa 00aa;
2.2、对寄存器某几位赋值 |
寄存器位经过清零操作后,可以构造一个合适的二进制数和寄存器中的值进行位或|操作,实现对清零位的赋值;
位或|运算符特点:(二进制数)与1位或变为1,与0位或无变化;
把0xaaaa 00aa清零的低8-15位置1,其他位不变。
第一步
:要对0xaaaa 00aa这个十六进制数的第8-15位赋值,就得让8-15位和1位或,而其他位不能发生变化;
第二步
:要得到一个十六进制数,让该数的8-15位是1,其他位是0,即二进制数的0-31位是:0000 0000 0000 0000 1111 1111 0000 0000;
第三步
:把这个二进制数转换为十六进制数得到0xff00;
第四步
:将寄存器中的数(0xaaaa 00aa)和赋值指定位(0xff00)进行位或,即可完成8-15位置1。//1010 1010 1010 1010 '0000 0000' 1010 1010
a = 0xaaaa 00aa;
//0000 0000 0000 0000 '1111 1111' 10000 0000
b = 0xff00;
c = a | b;
//1010 1010 1010 1010 '1111 1111' 1010 1010 这样8-15位就被置1了。结果为0xaaaa ffaa
c = 0xaaaa ffaa;
2.3、对寄存器某位取反 ~或^
要对寄存器的某个位进行取反操作,即 1 变 0 ,0 变 1;
有以下两种方式:
2.3.1、按位取反 ~
位取反运算符特点:(二进制数)按位取反,1变为0,0变为1;
把0xf0取反。
第一步
:将0xf0取反,首先要得到它的二进制。0xf0的二进制数是:1111 0000;
第二步
:对二进制每一位进行取反,0变1, 1变0;
第三步
:取反后得到0000 1111,转换成十六进制是:0xfa = 0xf0;// 1111 0000
c = ~b;
c == 0xf;//0000 1111 注意:这里的变量都占32位,这里省略了8-31位,用不到的位,编译器自动补0
2.3.2、特定位取反用 ^
位异或运算符特点:(二进制数)与1异或位取反,与0异或无变化;异或就是相异为1,相同则为0。
把0xa的0-1位取反,其他位不改变。
第一步
:把0xa转换为二进制数:0xa->1010;
第二步
:可以构造一个合适的二进制数和寄存器中的值进行位异或操作,实现对特定位的取反;为对二进制数1010的0-1位取反,则这里构建的对应二进制数位 :0011;
第三步
:将构建的二进制数转换成十六进制的,0011->0x3;a = 0xa;//10'10'
b = 0x3; //00'11'
c = a^b;
c == 0x9; //10'01' 这样0xa的0-1位就取反了,而其他位没有发生变化
2.4、总结:
1、十六进制一位等于二进制四位,32位平台下int数据类型占32位,寄存器也是32位的,分配的变量占32位,我们用不到的位,编译器自动补0。
2、注意位取反~
和位异或^
的区别。前者是给所有位取反的;后者可以完成给特定位取反。
3.位运算构建特定二进制数
对寄存器位操作,经常需要特定位给特定值或者取反;一般通过事先构建一个特别的值,将这个值和原来的值进行位与、位或、位异或、位取反等操作,来达到我们对寄存器操作的要求。构建特定位二进制数值,一般通过以下两种方式
:
3.1、构建特定位为1的二进制数:左位移、位或
构建目标特定位为1的二进制数:(0xT_V << T_Low_Bit_Index)
解释:
0x
:16进制的前缀;
T_V
:Target_Value 根据一个低位到高位连续T_L个1构建出来的二进制数,进而转换出来的十六进制数;
T_L
: Target_Length 目标特定位长度, 目标特定位的((最高位inedx - 最低位index) + 1) = T_L;
T_Low_Bit_Index
:目标特定位的最低位的下标
实现步骤:
1、计算出目标特定位的个数T_L;
2、根据T_L数量,先构建最低位为T_L个连续1的二进制数;
3、将上步骤中构建的二进制数,转换成十六进制数0xT_V;
4、然后将十六进制数0xT_V,左位移T_Low_Bit_Index个位;
5、得出构建目标特定位为1的二进制数:(0xT_L << T_Low_Bit_Index)
⚠️注意:目标特定位必须是连续性的,若不连续就将目标特定位的个数分成多个部分计算并列出来,构建出多个二进制数进行位或运算;
举例:
- 构建一个bit3~bit7为1(其他位全部为0)的二进制数。
1、计算目标特定位个数:T_L = ((7 - 3) + 1) = 5;// bit3、bit4、bit5、bit6、bit7等5位
2、构建最低位为T_L个连续1的二进制数:0000 0000 0000 0000 0000 0000 0001 1111;// T_L = 5;构建低位往高位连续5个1的二进制数
3、将上步骤中构建的二进制数,转换成十六进制数:T_V = 0x1f; // 0x1f代表11111,在16进制数来看,低4个位为一个f,加上第5位1,就是0x1f
4、代入目标特定位的最低位的下标:T_Low_Bit_Index = 3;// bit3~bit7的最低位bit3
5、得出构建bit3~bit7位为1的二进制数:
(0xT_V << T_Low_Bit_Index) = (0x1f << 3) = 0000 0000 0000 0000 0000 0000 1111 1000 = 0xf8;
- 构建bit3~bit7,bit23~bit25两段目标特定位为1的二进制数。
// 第一段目标特定位为1的二进制数
1、var_num_1 = (0x1f << 3) = 0xf8 = 0000 0000 0000 0000 0000 0000 '1111 1'000;
// 第二段目标特定位为1的二进制数
2、var_num_2 = (0x7 << 23) = 0x3800000 = 0000 00'11 1'000 0000 0000 0000 0000 0000;
// 两段目标特定位为1的二进制数结合起来的二进制; 位或运算符特点:与1位或变成1,与0位或无变化;
3、result = (0x1f << 3) | (0x7 << 23) = 0x38000f8 = 0000 00'11 1'000 0000 0000 0000 '1111 1'000;
3.2、构建特定位为0的二进制数:左位移、位或、位取反
有以下两种实现方法
:
1、可以根据构造
多个特定位为1
的二进制数,进行位或
操作:(0xT_V << T_Low_Bit_Index) | (0xT_V << T_Low_Bit_Index)
;
2、先将目标特定位为0的二进制数,取反变成构造目标特定位为1的二进制数;然后取反得到目标特定位为0的二进制数:~(0xT_V << T_Low_Bit_Index)
;
举例:
- 构建一个bit3~bit7为0(其他位全部为1)的二进制数。
方法一:
目标特定位为0的二进制数:(0xT_V << T_Low_Bit_Index) | (0xT_V << T_Low_Bit_Index)
=(0x7<<0) | (0xffffff<<8)
;// bit0-bit2,bit8-bit31两部分二进制数进行位或,得出目标特定位为0,其他位全位1
方法二:// 推荐
目标特定位为0的二进制数:~(0xT_V << T_Low_Bit_Index)
=~(0x1f << 3)
// 直接将特定位为1的二进制数,取反
3.3、总结
1、寄存器中二进制数位:1少0多,可通过连续多个1左移n位
,构造特定位为1二进制数;
2、寄存器中二进制数位:0少1多,先构建其位反二进制
,再按位取反
;
3、寄存器中二进制数位:连续1(0)不止一个,则通过分段
分别构建,再彼此位或
即可;