1.寄存器操作的要求(特定位改变而不影响其他位)
(1)arm是统一编址的,arm中有很多内部外设,soc通过向这些内部外设的寄存器写入一些特定的值来完成操作。这个内部外设进而操控硬件,所以说读写寄存器就是在操控硬件。
(2)在设定特定位时不改变其他位,而且寄存器的特点就是按位进行规划和使用。
(3)而修改寄存器中的特定值的一般步骤是,读-改-写。读取一次寄存器的值时32位的(一个int),而要想修改其中的一位必须全部读出32位的内容。修改完以后再统一写入寄存器中。
位与:(任何数,其实就是1或者0)与1位与无变化,与0位与变成0
位或:(任何数,其实就是1或者0)与1位或变成1,与0位或无变化
位异或:(任何数,其实就是1或者0)与1位异或会取反,与0位异或无变化
2.特定位清零用&
(1)回顾:二进制位和1位与无变化,与0位与变成0。
(2)若要把其中特定位改成0,其他位不变,可以使用位与1的方法。
(3)举例:把0xaaaaaaaa的第八位到第十五为清零,
分析:
第一步:要把0xaaaaaaaa这个十六进制数的第8到15位清零,就得让8-15位和0位与,而其他位不能发生变化。
第二步:要得到一个十六进制数,让该数的8-15位是0,其他位是1,先写出这样的二进制数的0-31位是:1111 1111 1111 1111 0000 0000 1111 1111
第三步:把这个二进制数转换为十六进制数得到0xffff00ff
第四步:让之前寄存器中的数(0xaaaaaaaa)和我们现在得到的数(0xffff00ff)进行位与,即可完成8-15位清零。
a = 0xaaaaaaaa; //1010 1010 1010 1010 '1010 1010' 1010 1010
b = 0xffff00ff; //1111 1111 1111 1111 '0000 0000' 1111 1111
c = a & b
c == 0xaaaaaa00aa; //1010 1010 1010 1010 '0000 0000' 1010 1010
//这样8-15位就被清零了。结果为0xaaaa00aa
//十六进制一位等于二进制四位
3.特定位置1用|
(1)回顾:位或:(二进制数)与1位或变成1,与0位或无变化。
(2)特定位置1,其他位不变采用位或。
(3)举例:把0xaaaaaaa0八位二进制数的0-3位置1,其他位不变。
分析:
第一步:要把0xaaaaaaa0这个十六进制数的第0到3位清零,就得让0-3位和1位或,而其他位不能发生变化。
第二步:要得到一个十六进制数,让该数的0-3位是1,其他位是0,先写出这样的二进制数的0-31位是:0000 0000 0000 0000 0000 0000 0000 1111
第三步:把这个二进制数转换为十六进制数得到0xf
第四步:让之前寄存器中的数(0xaaaaaaa0)和我们现在得到的数(0xf)进行位或,即可完成0-3位置一。
a = 0xaaaaaaa0;//1010 1010 1010 1010 1010 1010 1010 '0000'
b = 0xf; //0000 0000 0000 0000 0000 0000 0000 '1111'
c = a | b;
c = 0xaaaaaaaf //1010 1010 1010 1010 1010 1010 1010 '1111'
//这样0-3位就被置一了。结果为0xaaaaaaaf
4.取反~
(1)把一个数按照二进制位进行取反
(2)举例:把0xf0取反
分析:
第一步:要给一个数取反首先要得到这个数的二进制。0xf0的二进制数是:1111 0000
第二步:按照每一位进行取反,0变1,1变0。
第三步:取反后得到0000 1111,把之转换成十六进制是:0xf
a = 0xf0;// 1111 0000
c = ~b;
c == 0xf;//0000 1111
//注意:这里的变量都占32位,这里省略了8-31位,用不到的位,编译器自动补0。
5.特定位取反用^
(1)回顾:位异或,(二进制位)与1位或会取反,与0异或无变化。
(2)位异或真值表:
00111
11100
^(位异或)
—————-
11011
(3)举例:把0xa的0-1位取反,其他位不改变
分析:
第一步:把要被取反的数转换为二进制数:0xa–>1010
第二步:构建一个数让他的特定位为1,这个特定位是指上面的要被取反的数的要被取反的位。比如我要向上面的数的0-1位取反,我构建的数的0-1位就为1,其他位为0,所以我构建的数位 :0011
第三步:把我构建的二进制数转换成十六进制的,0011–>0x
a = 0xa; //10'10'
b = 0x3 //00'11'
c = a^b;
c == 0xb; //10'11'
//这样0xa的0-1位就取反了,而其他位没有发生变化
总结:
(1)十六进制一位等于二进制四位,32位平台下int数据类型占32位,寄存器也是32位的,分配的变量占32位,我们用不到的位,编译器自动补0。
(2)注意~和^的区别。前者是给所有位取反的。后者可以完成给特定位取反。
(3)在单片机或者嵌入式中,位运算符号是很常用的,尤其是在Linux内核中。
位运算结合左移右移操作的对于控制寄存器的配置的举例
1.让第三位置一
a | 0x8 ==> a = a | (0x1<<3)
2.让第三位清零
1111 1111 ... 0111
b = b & 0xfffffff7 ====>b = b &( ~(0x1<<3))
3.让b的第三位和第五位都清零
1111 1111 1111 1111 .... 1101 0111======(0xffffffd7)16
b = b & 0xffffffd7 ====> (两步):
b = b & (~(0x1<<3))
b = b & (~(0x1<<5))
一步:b = (b & (~(0x1<<3)) ) & (~(0x1<<5))
练习:
1.将a的第三位和第四位置一 1100
a =a | (0x1<<3) a =a | (0x1<<4)
2.将a的第二位和第三位先清零然后置一
a = a & (~(0x1<<2)) a = a & (~(0x1<<3)) ===> a & (~(0x1<<2))& (~(0x1<<3)) ==>
a = a & (~(0x3<<2));
a = a | (0x1<<2) | (0x1<<3); ===> a = a | (0x3<<2)
3.将a的第5,4,3,2这四个位分别设置为 0101
a = a & (~(0x1<<5));
a = a | (0x1<<4);
a = a & (~(0x1<<3));
a = a | (0x1<<2);
/******************************************************************************/
/******************************************连续的寄存器置一清零(位域)*********************************************/
位域:操作需要先清零再操作
1.将a的第5,4,3,2这四个位分别设置为 0101
a = a & (~(0xf<<2)) ; //先清零 f指的是连续4位为1 2代表从第二位开始 取反之后又为4个0
a = a | (0x5<<2); //设置 5指的是0101 2代表从第二位开始