与或左移右移操作在控制寄存器配置中的作用

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代表从第二位开始

发布了56 篇原创文章 · 获赞 37 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/weixin_42096901/article/details/103067921