STM32入门基础篇(四)

(四)位带操作

1.位带操作原理

  • STM32将每个比特位膨胀成为32字,访问这些字就实现了访问位带别名区。膨胀的新地址使用的是未使用的Reserved区域。

  • Bit band alias 位带别名区,大小为2000000;Bit band region 位带区域,大小为100000。二者之间呈32倍关系。

    注:1字节(Byte)=8位(bit);位带区的1位经过膨胀后在位带别名区变为32位(即4Byte)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nfPbFMMM-1635770009393)(C:\Users\86134\AppData\Roaming\Typora\typora-user-images\image-20210824144212906.png)]

1.1.STM32位带及位带别名区域

  • 支持位带操作的区域是 SRAM区的最低1MB范围(APB1/2、AHB1外设)和片内外设区的最低1MB范围。
  • 一般是对GPIO进行位带操作。

1.2.位带区和位带别名区地址转换计算公式

  • 外设位带区与外设位带别名区

    AliasAddr = 0x42000000+ (A-0x40000000)*8*4 +n*4
    
  • SRAM位带区与SRAM位带别名区

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

    其中,A表示我们要操作的那个位所在的寄存器地址,n是位序号。

综上,总结出统一公式为:

((A & 0xF0000000)+0x02000000+((A &0x000FFFFF)<<5)+(n<<2))

2.位带操作编程

使用KEIL5新建相关文件

  1. 在KEIL5中新建文件夹与程序文件夹中,命名为“Public”,并在其内部新建文件名为“system.c”和“system.h”的两个文件。

  2. “system.h”编写如下:

    #ifndef _system_H
    #define _system_H
    
    #include "stm32f4xx.h"
    
    #endif
    
  3. 将如下路径中的TXT文件内容复制到“system.h”,最终效果如下:

    #ifndef _system_H
    #define _system_H
    
    #include "stm32f4xx.h"
    
    #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
    #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
    #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
    //IO口地址映射
    #define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
    #define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
    #define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
    #define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
    #define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
    #define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
    #define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
    #define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
    #define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     
    
    #define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
    #define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
    #define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
    #define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
    #define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
    #define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
    #define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
    #define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
    #define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
     
    //IO口操作,只对单一的IO口!
    //确保n的值小于16!
    #define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
    #define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 
    
    #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
    #define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 
    
    #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
    #define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 
    
    #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
    #define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 
    
    #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
    #define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入
    
    #define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
    #define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入
    
    #define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
    #define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入
    
    #define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
    #define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入
    
    #define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
    #define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入
    
    
    #endif
    

    __路径为:__D:\BaiduNetdiskDownload\32单片机\普中STM32-PZ6808L-F4开发板资料\普中STM32-PZ6808L-F4开发板资料–B\普中STM32-PZ6808L-F4开发板视频教程\视频讲解PPT\15. STM32位带操作

    注:该部分内容完成的是位带操作的膨胀及GPIO端口定义。
  4. 在“system.c”内编写如下:

    #include "system.h"
    
  5. 在“zs_pro_standard.h”内补充对"system.h"的引用,并删除对"stm32f4xx.h"的引用,最终效果如下:

    #ifndef _zs_pro_standard_H
    #define _zs_pro_standard_H
    
    #include "system.h"
    
    #endif
    
    
  6. 全部完成后应在main.c中引用"system.h",而在"system.h"中引用过"stm32f4xx.h",所以在main.c程序中替换即可。

    #include "system.h"
    #include "zs_pro_standard.h"
    
    int main()
    {
        LED_Init();//调用该函数对其进行初始化
        
        while(1)
        {
            GPIO_ResetBits(GPIOF,GPIO_Pin_9);//低电平设置函数
        }
    }
    
  7. 在工程组中将“Public”文件夹添加进入、在魔术锤的C/C++中将头文件所在文件夹(即“Public”文件夹)添加进入。

  8. 在"zs_pro_standard.h"中定义所使用的的管脚,最终效果如下:

    #ifndef _zs_pro_standard_H
    #define _zs_pro_standard_H
    
    #include "stm32f4xx.h"
    
    void LED_Init(void); //声明时添加本行即可
    
    #define led1 PFout(9)
    #define led2 PFout(10)
    
    #endif
    
  9. 对应于步骤8,修改"main.c"中对应的函数

    #include "system.h"
    #include "zs_pro_standard.h"
    
    int main()
    {
        LED_Init();//调用该函数对其进行初始化
        
        while(1)
        {
            led1=!led1;
            delay(0xFFFFFF);//低电平设置函数
        }
    }
    

    但是"zs_pro_standard.c"文件中的函数是__不用改变的__,依然如下

    #include "zs_pro_standard.h"
    //以点亮LED为例,写如下程序
    void LED_Init()
    {
        GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量,此行必须要在GPIO端口时钟使能程序的上方
        
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);//使能对应的GPIO端口时钟
    	
    	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //输出模式
    	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//管脚设置F9
    	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度为100M
    	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
    	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//上拉
    	GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化结构体
        GPIO_SetBits(GPIOF,GPIO_Pin_9)//此行用于事先预置管脚的高低电平
    }
    
    注:使用位带操作与51单片机相同,可使用下面的程序来控制端口电平的高低
    led1=1;
    led2=0;
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值