位操作(寄存器)

**

位操作(寄存器)

**新手,根据自己理解和参考其他人的资料写成,希望对大家有所帮助。第一次写文章,不足请多指教,谢谢!

位带操作的原理:

STM32中CPU是32位的。最方便快捷的方法是直接操作32位的地址,对某个地址直接赋值是最快的操作,只需要一个指令。

【附录1】

在32位的系统中
1字(word) = 4字节(byte)
1字节(byte) = 8位(bit)
1B = 8bit
1KB = 1024B = 2^10B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB

计算机中一个二进制位就是一个bit,8个bit组成一个字节(byte)。一个存储单元可以储存一个字节,也就是8个二进制位。计算机的存储器是以字节B为最小单元来计算的。因此内存往往是1KB、1MB、1GB、1TB这样描述的
存储器有多少个字节就是有多少个存储单元,每个存储单元都是从0开始顺序编号,如一个存储器有1KB大小,那么它就有1024个存储单元,编号为0~1023。
存储地址就是一个编号,代表一个存储单元,也就是一个位(8个字节)
32位的CPU最多支持4G的内存空间,也就是2^32个存储单元,如果用十进制来对所有的存储单元编号需要0~4,294,967,295,所以为了使用方便就用十六进制来对内存单元编号0000 0000~FFFF FFFF(因为2^32 = FFF FFFF,)。为了混淆十进制与十六进制就在十六进制的序列编号前面加上0x,或者在最后加上一个h
位操作(寄存器)

以上关系如上图所示。重点注意:不要将内存地址与存储内容搞混!

由此,引出一个重要的CPU原件寄存器,寄存器的作用有三个,我们只需要了解前两个:
1.可将寄存器内的数据执行算术及逻辑运算
2.存于寄存器内的地址可用来指向内存的某个位置,即寻址
操作寄存器,可以找到相应的内存单元,再对内存单元进行操作。在STM32中,有232个存储单元,也就有232个内存地址。而每个寄存器占用4个字节(32位)

【附录2】
CPU:32位;即32位计算机的CPU一次最多能处理32位数据。32位处理器每次处理 4个字节(4Byte),最多支持4G的内存空间
位带区(bit_band region):以位为单位,每1个字节(8位)有个地址
别名区(Alias region):以字为单位,一个字等于4个字节(32位)

支持位操作位带区和别名区都有两个,分别位于SRAM区(片内内存区域)和Peripheral区( 片上外设区域 )
· SRAM区:
位带区中的地址:0x2000 0000~0x2010 0000 (1MB的绑定区)
别名区中的地址:0x2200 0000~0x23FF FFFF (30MB的绑定区)
·片上外设:
位带区中的地址:0x4000 0000-0x4010 0000 (1MB的绑定区)
别名区中的地址:0x4000 0000 ~ 0x400F FFFF (30MB的绑定区)

别名区一个单位(字)对应位带区一个单位(位),所以相当于将位带区膨胀32倍才是别名区的大小,所以上面1MB的绑定区,对应到别名区为32M
(1)位带本质上是一块地址区(例如每一位地址位对应一个寄存器)映射到另一片地址区(实现每一位地址位对应一个寄存器中的一位),该区域就叫做位带别名区,将每一位膨胀成一个32位的字。
(2)位带区的4个字节对应实际寄存器或内存区的一个位,虽然变大到4个字节,但实际上只有最低位有效(代表0或1)
如下图:
位操作(寄存器)
位操作(寄存器)
位操作(寄存器)

如上图:
位带区0x2000 0000的第0位对应到别名区0x22000000~0x2200 0003这4个字节。需要指出的是0x2200 0001、0x2200 0002、0x2200 0003这样的地址是不能直接被访问的,只有0x2200 0000、0x2200 0004、0x2200 0008…这样的起始地址(其实是4的倍数)才能被访问,其实我们所说的别名区的地址映射就是计算这样的起始地址。理解这一点也有助于后面地址计算公式的掌握。
好了,上面是理解的基础,下面才是具体的别名区起始地址的计算。首先我们要有一个概念是,一般计算应用关于寻址的计算都这样的:
要计算的地址=基地址+偏移量

·SRAM区映射的地址:
AliasADDr = 0x22000000 + ((A - 0x20000000) * 8 + n) * 4)
= 0x22000000 + (A - 0x20000000) * 8*4+ n * 4

·片上外设区映射的地址:
AliasADDr = 0x42000000 + ((A - 0x40000000) * 8 + n) * 4)
= 0x42000000 + (A - 0x40000000) * 8*4 + n * 4

A就是要进行映射的寄存器的地址,n 的取值范围是0~7

官方给出的公式:
bit_word_addr = bit_band_base + (byte_offset32) +(bit_number4)
bit_word_addr:映射在位带别名区的新地址(即为所求的地址)
bit_band_base:是别名区的起始地址
byte_offset:该字节相对于位带区起始地址的偏移。
bit_number:是目标位所在的位的位置(0-7)
SRAM区所对应的起始地址为 0x2200 0000
片上外设区所对应的起始地址为 0x4200 0000
这两项都是一种规定,记住就行
在SRAM位带区的偏移量为:(A-0x2000 0000),A为包含目标位字节的地址
在片上外设区所对应的偏移量为:(A-0x4000 0000),A为包含目标位字节的地址
byte_offset32:是因为位带区的一个位要扩张到别名区的32个位,同样,一个位带区的地址也会扩张到别名区的32个地址。byte_offset32表示前面已经占用的地址。
bit_number4是因为 1bit位要占用四个地址单元(四个字节,32 位)
实这个公式还有一个变异形式,也是有助于理解这一公式的:
it_word_addr = bit_band_base + ((byte_offset
8) + bit_number)4
由于位带区每一个地址是8个位,所以(byte_offset
8)+ bit_number相当计算出了一个以字节为单位的偏移,再乘以4就是以字为单位的偏移了。
很明显,不管是哪个公式,都是基地址+偏移

位带别名区把每个比特(1bit)膨胀成一个 32 位(4个字节)。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。1MB位带区对应32MB位带别名区(1byte=8bit映射成8*4byte=32byte)。

位带区和位带别名区的映射如下图:
位操作(寄存器)

位带区:支持位带操作的地址区
位带别名:对别名地址的访问最终作用到位带区的访问上(这中途有一个地址映射过程)
映射过程举例如下:
要设置0x2000 0000这个字节的第二个位bit2为1,使用位带操作的步骤有:1、将1写入位 带别名区对应的映射地址(即0x22000008,因为1bit对应4个byte);2、将0x2000 0000的值 读取到内部的缓冲区(这一步骤是内核完成的,属于原子操作,不需要用户操作);3、将bit2 置1,再把值写 回到0x2000 0000(属于原子操作,不需要用户操作)。
位操作(寄存器)
映射过程总结:在位带区中,每个比特都映射到别名地址区的4个字节——且4个字节只有最低位有效。当一个别名地址被访问时,会先把该地址变换成位带地址。对于读操作,读取位带地址中的4个字节(因为数据总线是32位的,寄存器也是32位的,所以是读4个字节),再把需要的位右移到最低有效位,并把最低有效位返回(相当于将位带区的值右移再做与操作)。对于写操作,把需要写的位左移至对应的位序号处(相当于把1(或0)左移n(n为该bit位所在的位置)位再和位带区的值做与操作),然后执行一个原子的“读-改-写”过程。

统一后的公式 :
( A & 0xF0000000)+0x2000000+(( A &0xFFFFF)<<5)+(n<<2))
( GPIOA_ODR_Addr & 0xF0000000)+0x2000000+(( GPIOA_ODR_Addr &0xFFFFF)<<5)+(n<<2))