【STM32】标准库与HAL库对照学习教程六--位带操作
一、前言二、准备工作三、位带介绍1、位带操作2、STM32位带及位带别名区域 四、位带区与位带别名区地址转换五、GPIO的位带操作六、标准库位带操作1、工程配置2、主程序3、实验效果 七、HAL库位带操作1、配置工程2、主程序3、实验效果 八、总结位带操作优点一、前言
二、准备工作
STM32开发板(我使用的是普中的STM32F103ZE的Z200系列)STM32cubemx软件、keil5(MDK)Cortex M3权威指南(中文)(非必要)使用位带需要了解GPIO的使用与LED的操作,可以看这两篇文章:
【STM32】标准库与HAL库对照学习教程特别篇–GPIO详讲
【STM32】标准库与HAL库对照学习教程三–使用库函数配置GPIO点亮LED灯
三、位带介绍
1、位带操作
在我们学习51单片机的时侯就已经使用过位操作,比如使用sbit对单片机IO口的定义。
但是STM32中并没有这类关键字,控制引脚输入输出是通过控制寄存器IDR与寄存器ODR地址取出32位数据中相应的位实现的。
如果将这32个位数据都取一个地址,当我们要访问32位数据中的某一位数据时,我们访问相应的地址就行了。
那么寄存器所在的地方是位带区,32个地址所在的地方是位带别名区。
比方说引脚输出寄存器ODR有32个位,PA8的控制位是第8位,32个位可以映射到32个地址上,当我们去访问这32个地址的第8个地址时就达到访问ODR寄存器第8个位的目的。
2、STM32位带及位带别名区域
支持位带操作的区域是SRAM区的最低1MB范围(APB1/2,AHB外设)和外设区的最低1MB范围。
四、位带区与位带别名区地址转换
外设位带区与外设位带别名区的地址转换公式:
AliasAddr = 0x42000000+ (A-0x40000000)* 8* 4 +n*4
别名区的地址 = 别名区的基地址+(外设寄存器地址-外设寄存器基地址)*32+要控制寄存器的相应位的序号 *32/8(一个字节8位)
SRAM位带区与SRAM位带别名区的地址转换公式:
AliasAddr = 0x22000000+ (A-0x20000000)* 8* 4 +n*4
别名区的地址 = 别名区的基地址+(SRAM寄存器地址-SRAM寄存器基地址)*32+要控制寄存器相应位的序号 *32/8(一个字节8位)
根据上述两个公式的规律,将其统一为一个公式表示:
AliasAddr = ((A & 0xF0000000)+0x02000000+((A&0x000FFFFF)<<5)+(n<<2))
A:寄存器地址FFFFF=1M左移5位相当于乘以32,左移2位相当于乘以4
五、GPIO的位带操作
#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+12) //0x4001080C #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08//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) //输入
不懂的可以去b站搜索海创电子,在up主的32系列视频中,找到位带操作的视频,里面有详讲,我文字描述实在有限。
六、标准库位带操作
1、工程配置
**(1)**复制一个操控LED的工程,命名为6、位带操控LED实验。
(2)进入工程文件,进入Public文件,新建System文件夹用来存放位带的文件。
(3)进入工程,新建System.h与System.c文件。
①
文件内容为:
#include "System.h"
②
文件内容为:
#ifndef SYSTEM_H_#define SYSTEM_H_#include "stm32f10x.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+12) //0x4001080C #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C//IO口输入寄存器地址映射#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08//IO口操作,只对单一的IO口!//确保n的值为0~15#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) //输入#endif
(4)添加文件到目录并添加文件路径。
①
②
2、主程序
#include "LED.h"#include "Delay.h"//#include "exti.h"#include "System.h"/**************************************************函数名: main*函数功能: 主函数*输入:无 *返回值: 无**************************************************/int main(){SysTick_Init(72);//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //抢占式优先级与响应式优先级的分组LED_Init();//KEY_Init();//My_EXTI_Init();while(1){PBout(5)=1;Delay_ms(1000);PBout(5)=0;Delay_ms(1000);}}
3、实验效果
七、HAL库位带操作
1、配置工程
(1)打开cubemx新建工程,选择芯片(我的是stm32F103ZE)。
(2)配置RCC。
(3)设置LED的引脚为输出模式。
(4)配置GPIO。
(5)配置时钟树。
**(6)**配置工程文件,并生成工程。
①
②
(7)在工程文件夹中新建Public文件夹,在Public文件夹内新建System文件夹用来存放位带相关的文件。
①
②
(8)进入工程,新建System.h与System.c文件。
①
内容为;
#include "System.h"
②
内容为:
#ifndef SYSTEM_H_#define SYSTEM_H_#include "stm32f1xx_hal.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+12) //0x4001080C #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C//IO口输入寄存器地址映射#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08//IO口操作,只对单一的IO口!//确保n的值为0~15#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) //输入#endif
可以看到标准库与HAL的寄存器地址是一样的,只是封装函数不一样。
(9)添加文件到目录,并添加文件路径。
①
②
2、主程序
①
②
代码:
PEout(5)=1;HAL_Delay(1000);PEout(5)=0;HAL_Delay(1000);
3、实验效果
八、总结位带操作优点
1、控制GPIO口输入输出非常简单。2、操作串行接口芯片非常方便。3、代码简洁,阅读方便。4、在多任务中,用于实现共享资源在任务间的“互锁”访问。到这里就结束啦!
如果觉得《【STM32】标准库与HAL库对照学习教程六--位带操作》对你有帮助,请点赞、收藏,并留下你的观点哦!