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


STM32全部教程【STM32】标准库与HAL库对照学习系列教程大全

一、前言

本篇文章讲解位带以及位带的操作,通过位带操作可以极大简化IO的操作,以使用位带操控LED灯为例,通过本篇文章可以加深你对位带的理解。

二、准备工作

  • 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、在多任务中,用于实现共享资源在任务间的“互锁”访问

到这里就结束啦!
在这里插入图片描述

  • 33
    点赞
  • 104
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
STM32标准库HAL库都是针对STM32系列微控制器的,但是它们的设计思路和使用方法有所不同。STM32标准库是由ST公司提供的一套基于寄存器操作的低层驱动,而HAL库则是基于标准库的封装,提供了更加高级的API接口,使得开发者可以更加方便地进行开发。 具体来说,STM32标准库主要包括以下几个部分: 1. CMSIS(Cortex Microcontroller Software Interface Standard):这是一套由ARM公司提供的针对Cortex-M系列微控制器的标准接口,包括了一些通用的API接口和一些与具体芯片相关的头文件和链接脚本等。 2. 寄存器操作:这是STM32标准库的核心部分,通过对芯片内部寄存器的直接操作,实现了对外设的控制和数据传输等功能。 3. 外设驱动:这是对一些常用外设(如USART、SPI、I2C等)的封装,提供了更加方便的API接口,使得开发者可以更加方便地进行开发。 相比之下,HAL库则提供了更加高级的API接口,使得开发者可以更加方便地进行开发。HAL库主要包括以下几个部分: 1. HAL库核心:这是HAL库的核心部分,提供了一些通用的API接口,如时钟配置、中断控制等。 2. 外设驱动:这是对一些常用外设(如USART、SPI、I2C等)的封装,提供了更加方便的API接口,使得开发者可以更加方便地进行开发。 3. 扩展:这是一些额外的功能,如USB、文件系统等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

修成真

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值