STM32中的位带(bit-band)操作与原理解析.docx
STM32中的位带(bit-band)操作支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在CM3中,有两个区中实现了位带。其中一个是SRAM区的最低IMB范围,第二个则是片内外设区的最低IMB范围。这两个区中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。位带操作的概念其实30年前就有了,那还是8051单片机开创的先河,如今,CM3将此能力进化,这里的位带操作是8051位寻址区的威力大幅加强版。CM3使用如下术语来表示位带存储的相关地址:位带区:支持位带操作的地址区、位带别名:对别名地址的访问最终作用到位带区的访问上(这中途有一个地址映射过程)在位带区中,每个比特都映射到别名地址区的一个字一一这是只有1.SB有效的字。当一个别名地址被访问时,会先把该地址变换成位带地址。对于读操作,读取位带地址中的一个字,再把需要的位右移到1.SB,并把1.SB返回。对于写操作,把需要写的位左移至对应的位序号处,然后执行一个原子的“读-改-写”过程。图5.3B位带区与位带别名区的膨胀对应关系图B举例:欲设置地址0x20OOJ)OOO中的比特2,划使用位带操令的设置过程如下图所示:不使用位带功能使用位带功能读取0x200(1.OOOO处的值到寄行器中置位寄存SS的bit2阳寄存器的值写回§)0x20000000读取0x20000000处的值到内部缓冲区位置bit2后,再犯僵g0×2000.0000图5.4写数据到位带别名区对应的汇编代码如图5.5所示WithoutBit-Band1.DRR0,020000000;S<tupaddr1.DRRl,(R0;ReadORR.WRl,»0x4;ModifybitSTRRl,(R0;WritebackresultWithBit-Band1.DRRO,0x22000008SetupaddressMOVRl,1iSetupdataSTRRlt(R0;Write图5.5位带操作与普通操作的对比,在汇编程序的珀度上位带读操作相对简单些:无Bit-BandRead0x200000toregister把bit2右移到1.SB再簿蔽其它的位S*Bit-Band映射成3次Readfrom总线传0x2200CXX)8从0x20000000读出数据后,再阳bit2攫取出来$5.6从位带别名区中读取比特无位带有位带1.DRRO,-0x20000000;建立地址1.DRRO,-0x22000008;建立地址1.DRRlf(RO);Read1.DRRl,RO;ReadUBFX-WR1,R1,#2,#1;提取除2图5.7读取比特时传统方法与位带方法的比较支持位带操作的两个内存区的范围是:0x2000-0000-0x200F_FFFF(SRAM区中的最低1MB)0x4000_0000-0x400F_FFFF(片上外设区中的最低IMB)对SRAM位带区的某个比特,记它所在字节地址为A,位序号为n(0<=n<=7),则该比特在别名区的地址为:AliasAddr=0x22000000+(A-0x20000000)*8+n)*4=0x22000000+(A-0x20000000)*32+nM对于片上外设位带区的某个比特,记它所在字节的地址为A,位序号为n(0<=n<=7),则该比特在别名区的地址为:AliasAddr=0x42000000+(A-0x40000000)*8+n)*4=0x42000000+(A-0x40000000)*32+nM上式中,“*4”表示一个字为4个字节,“*8”表示一个字节中有8个比特。这里再不嫌啰嗦地举一个例子:1.在地址0x20000000处写入0x3355AACC2.读取地址0x22000008。本次读访问将读取0x20000000,并提取比特2,值为1。3.往地址0x22000008处写Oo本次操作将被映射成对地址0x20000000的“读-改-写”操作(原子的),把比特2清Oo4.现在再读取0x20000000,将返回0x3355AAC8(bitl2已清零)。位带别名区的字只有1.SB有意义。另外,在访问位带别名区时,不管使用哪一种长度的数据传送指令(字/半字/字节),都把地址对齐到字的边界上,否则会产生不可预料的结果。Icpplviewplaincopy位带操作,实现51类似的GPIO控制功能具体实现思想,参考第五章(87页92页).IO口操作宏定义#defineBlTBAND(addr,bitnum)(addr&0xF0000000)+0x2000000+(addr&0xFFFFF)«5)+(bitnum«2)#defineMEM_ADDR(addr)*(volatileunsignedlong*)(addr)#defineBIT_ADDR(addr,bitnum)MEM_ADDR(BITBAND(addr,bitnum)/IO口地址映露#defineGP10A_0DR_Addr(GP10A_BASE+12)0x4001080C#defineGP10B_0DR_Addr(GPI0B_BASE+12)0x4001OCOC#defineGP10C_0DR_Addr(GPI0C_BASE+12)0x4001100C#defineGPI0D_0DR_Addr(GP10D_BASE+12)0x4001140C#defineGP10E_0DR_Addr(GP10E_BASE+12)0x4001180C#defineGP10F_0DR_Addr(GP10F_BASE+12)0x40011AOC#defineGPI0G_0DR_Addr(GP10G_BASE+12)0x40011EOC#defineGP!OAJDR_Addr(GPIOA_BASE+8)/0x40010808#defineGP!OBJDR_Addr(GPIOB_BASE+8)/0x40010C08#defineGPIOCJDR_Addr(GPIOC_BASE+8)0x40011008#defineGPIODJDR_Addr(GPIOD_BASE+8)0x40011408#defineGPIOE_IDR_Addr(GPIOE_BASE+8)/0x40011808#defineGP!OFJDR_Addr(GPIOF_BASE+8)/0x40011A08#defineGPIOGJDR_Addr(GPIOG_BASE+8)/0x40011E08IO口操作,只对单一的IO口确保n的值小于16!#definePAout(n)BIT_ADDR(GPIoAjDDR_Addr,n)输出#definePAin(n)BIT_ADDR(GPIOADR_Addr,n)输入#definePBout(n)BIT_ADDR(GPlOBJDDR_Addr,n)输出#definePBin(n)BIT_ADDR(GPlOBDR_Addr,n)输入#definePCout(n)BIT_ADDR(GPIOC1.oDR_Addr,n)输出#definePCin(n)BIT_ADDR(GPIOCDR_Addr,n)输入#definePDout(n)BIT_ADDR(GPIOD_ODR_Addr,n)输出#definePDin(n)BIT_ADDR(GPIoDDR_Addr,n)输入#definePEout(n)BIT_ADDR(GPlOE_ODR_Addr,n)输出#definePEin(n)BIT_ADDR(GPIOEDR_Addr,n)输入#definePFout(n)Bn1.ADDR(GPIoFJDDR_Add,n)输出#definePFin(n)Bn1.ADDR(GPIOFDR_Addr,n)输入#definePGout(n)Bn1.ADDR(GPIoG_ODR_Addr,n)输出#definePGin(n)BIT_ADDR(GPIoGDR_Addr,n)输入STM32单片机位带操作原理解析STM32单片机的SRAM有两个区支持位带(bit-band)操作。那么,什么是位带,位带操作的原理是怎样的呢?在介绍位带操作之前,先看一看ARMCroteXt-M3的存储器映射。CM3的地址空间是4GB,程序可以在代码区,内部SRAM区以及外部RAM区中执行。STM32单片机的程序存储器、数据存储器、寄存器和输入输出端口,被组织在同一个4GB的线性地址空间内。数据字节以小端格式存放在存储器中。由芯片供应陶定义外眸有外设总线内部外出总线片外外iflIOGBOx43FFFF”IOGB0*E01000Oveoofffff0x(0040000OxE003FFFFOMfoOOoOOOOxdfffffffOxAOOOOOOOOx%FFFFFF片外RAM位南剧名区32MB片上外设0140100000C0x4000000002)FFFFFF片上SRAM0142000000041FFFFFFOv201000000x220000000m21FFFFFF512MB512MB5I2MB0x60000000Oxsfffffff0x400000000x3FFFFFFF0x20000000OxifffffffCM3使用如下术语来表示位带存储的相关地址。位带区:支持位带操作的地址区位带别名:对别名地址的访问最终会变换成对位带区的访问(注意:有一个地址映射过程)Cortex-M3存储器映像包括两个位段(bit-band)区。这两个位段区将别名存储器区中的每个字映射到位段存储器区的一个位,在别名存储区写入一个字具有对位段区的目标位执行读-改-写操作的相同效果。两个位带区分别为:第一个是SRAM区的最低IMB范围,地址范围为:0x2000_0000-0x200F_FFFF第二个是片内外设区谣最低IMB范围,地址范围0x4000_0OOo-OX400F_FFFF这两个位带中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。位带区与位带别名区的膨胀对应关系如下图在位带区中,每个比特都映射到别名地址区的一个字,这是个只有1.SB才有效的字。当一个别名地址被访问时,会先把该地址变换成位带地址。在STM32FIOxxx里,外设寄存器和SRAM都被映射到一个位段区里,这允许执行单一的位段的写和读操作。下面的映射公式给出了别名区中的每个字是如何对应位带区的相应位的:bit_word_addr=bit_band_base+(byte_offsetx32)+(bit_numberx4)其相:bit_word_addr是别名存储器区中字的地址,它映射到某个目标位。bit_band_base是别名区的起始地址。byte_offset是包含目标位的字节在位段里的序号bit_number是目标位所在位置(0-31)举个例子,如何映射别名区中SRAM地址为0x20000300的字节中的位2:0x22006008=0x22000000+(0x300×32)+(2×4)对0x22006008地址的写操作与对SRAM中地址0x20000300字节的位2执行读-改-写操作有着相同的效果。位带操作的优越性如果没有位带操作,那么访问内存的操作流程如下:对于读操作,先读取位带地址中的一个字,再把需要的位右移到1.SB,并把1.SB返回。对于写操作,把需要写的位左移至对应的位序号处,然后执行一个原子的“读一改写有了位带操作以后,对于读/写位带别名区就可以完成以上操作。位带操作的优越性,最容易想到的就是通过GPIO的管脚来单独控制每盏1.ED的点亮与熄灭,也对操作串行接口器件提供了很大的方便。位带操作对于硬件I/O密集型的底层程序最有用处了。对于大范围使用位标志的系统程序来说,位带机制也是一大福音。位带操作还能用来化简跳转的判断。当跳转依据是某个位时,以前必须这样做:读取整个寄存器掩蔽不需要的位比较并跳转现在只需:从位带别名区读取状态位比较并跳转位带操作还有个重要的好处是,在多任务中,用于实现共享资源在任务间的“互锁”访问。多任务的共享资源必须满足一次只有一个任务访问它,亦即所谓的“原子操作”。多任务的共享资源必须满足一次只有一个任务访问它(所谓的“原子操作以前的读一改一写需要3条指令,导致这中间留有两个空当能被中断,于是可能会出现如下图所示的紊乱危象:HandlefrIftIsR智位bitl中断累与例程主埋序的出端口的值通过使用CM3的位带操作,就可以消灭上例中的紊乱危象°CM3把这个“读一改一写”做成一个硬件级别支持的原子操作,不能被中断。C语言操作位带在C编译器中并没有直接支持位带操作。比如,C编译器并不知道同一块内存能够使用不同的地址来访问,也不知道对位带别名区的访问只对1.SB有效。欲在C中使用位带操作,最简单的做法就是#define一个位带别名区的地址。#defineDEVICE_REG0(volatileunsignedlong*)(0x40000000)#defineDEVICE_REG(1.Brn)(VOlatiIeUnSignedlOng*)(0x42000000)#defineDEVICE_REG0_B!Tl(volatileunsignedlong*)(0x42000004)我们可以建立一个把“位带地址+位序号”转换成别名地址的宏,再建立一个把别名地址转换成指针类型的宏:把“位带地址+位序号”转换成别名地址的宏#defineBlTBAND(addr,bitnum)(addr&0xF0000000)+0x2000000+(addr&0xFFFFF)<<5)+(bitnum<<2)把该地址转换成一个指针#defineMEM_ADDR(addr)*(volatileunsignedlong*)(addr)注意当使用位带功能时,要访问的变量必须用VOIatiIe来定义。因为C编译器并不知道同一个比特可以有两个地址。所以就要通过VOlatiIe,使得编译器每次都如实地把新数值写入存储器,而不再会出于优化的考虑,在中途使用寄存器来操作数据的复本,直到最后才把复本写回这会导致按不同的方式访问同一个位会得到不一致的结果。最后在STM32单片机实际开发中,一般会使用标准库或者HA1.库,我们直接使用库提供的操作APl即可完成相应的工作,也就是库帮我们完成了位带转换。我们掌握了位带操作的原理,知其然、知其所以然,假如遇到相关问题,可以从原理上分析、并解决问题。