美文网首页
2018-06-25 STM32中的位带操作

2018-06-25 STM32中的位带操作

作者: 酝锦 | 来源:发表于2019-02-20 17:23 被阅读0次

        支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在 CM3 中,有两个区中实现了位带。其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB范围。这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

        位带操作的概念其实 30 年前就有了,那还是8051单片机开创的先河,如今,CM3 将此能力进化,这里的位带操作是 8051 位寻址区的威力大幅加强版。

        CM3 使用如下术语来表示位带存储的相关地址:

            位带区    支持位带操作的地址区

            位带别名  对别名地址的访问最终作用到位带区的访问上(这中途有一个地址映射过程)

        在位带区中,每个比特都映射到别名地址区的一个字——这是只有 LSB 有效的字。当一个别名地址被访问时,会先把该地址变换成位带地址。对于读操作,读取位带地址中的一个字,再把需要的位右移到 LSB,并把 LSB 返回。对于写操作,把需要写的位左移至对应的位序号处,然后执行一个原子的“读-改-写”过程。

        支持位带操作的两个内存区的范围是:

            0x2000_0000‐0x200F_FFFF(SRAM 区中的最低 1MB)

            0x4000_0000‐0x400F_FFFF(片上外设区中的最低1MB)

        对 SRAM 位带区的某个比特,记它所在字节地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:

            AliasAddr=0x22000000+((A-0x20000000)*8+n)*4=0x22000000+(A-0x20000000)*32+n*4

        对于片上外设位带区的某个比特,记它所在字节的地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:

            AliasAddr=0x42000000+((A-0x40000000)*8+n)*4=0x42000000+(A-0x40000000)*32+n*4

        上式中,“*4”表示一个字为 4 个字节,“*8”表示一个字节中有 8 个比特。

        举例:

        位带操作的作用:

            1、GPIO的管脚单独控制(常用,重要)

            2、使用标志位时简化判断(理解,可以尝试使用)

            3、多任务中,用于实现共享资源在任务间的“互锁”访问(不懂)

        在C编译器中并没有直接支持位带操作,所以,C编译器并不知道同一块内存能够使用不同的地址来访问,也不知道对位带别名区的访问只对LSB有效。欲在C中使用位带操作,最简单的做法就是#define一个位带别名区的地址。

        以设置GPIOA,bit2为例理解位带操作:

        GPIOA基址   0x4001 0800

        端口输入数据寄存器  0x4001 0800+0x8=0x4001 0808

        端口输出数据寄存器  0x4001 0800+0xC=0x4001 080C

        输入寄存器位带别名

              Addr=0x4200 0000+((0x40010808-0x4000 0000)*32+2*4=0x4221 0108

        输出寄存器位带别名

              Addr=0x4200 0000+((0x4001080C-0x4000 0000)*32+2*4=0x4221 0188

    #define PA2in  ((volatileunsigned long *)(0x4221 0108))

    #define PA2out ((volatile unsigned long *)(0x4221 0118))

    *PA2out=1;                //PA2输出1

        为简化位带操作,将位带别名计算定义成一个宏:

    #define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x2000000 +

    ((addr & 0xFFFFF) << 5)+(bitnum << 2))

        将该地址转化成一个指针:

    #define MEM_ADDR(addr)  *((volatileunsigned long  *)(addr))

        实现位带操作的宏:

    #define BIT_ADDR(addr, bitnum)  MEM_ADDR(BITBAND(addr, bitnum))

        I/O口寄存器地址映射:

    #define GPIOA_ODR_Addr   (GPIOA_BASE+12) //0x4001080C,PA输出寄存器

    #define GPIOA_IDR_Addr     (GPIOA_BASE+8) //0x40010808,PA输入寄存器

        对单一I/O的操作

    #define PAout(n)  BIT_ADDR(GPIOA_ODR_Addr,n)  //输出

    #define PAin(n)     BIT_ADDR(GPIOA_IDR_Addr,n)  //输入

        定义I/O口

    #define LED1 PAout(2)

        则实现点亮LED只需

        LED1=1;or LED1=0;

        注意:当使用位带功能时,要访问的变量必须用volatile来定义。因为C编译器并不知道同一个比特可以有两个地址。所以就要通过volatile,使得编译器每次都如实地把新数值写入存储器,而不再会出于优化的考虑,在中途使用寄存器来操作数据的复本,直到最后才把复本写回——这会导致按不同的方式访问同一个位会得到不一致的结果。

    相关文章

      网友评论

          本文标题:2018-06-25 STM32中的位带操作

          本文链接:https://www.haomeiwen.com/subject/ryohyqtx.html