糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > 【STM32】标准库与HAL库对照学习教程六--位带操作

【STM32】标准库与HAL库对照学习教程六--位带操作

时间:2019-08-17 06:48:30

相关推荐

【STM32】标准库与HAL库对照学习教程六--位带操作

【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库对照学习教程六--位带操作》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。