STM32 入门 —— 寄存器与 GPIO

这篇具有很好参考价值的文章主要介绍了STM32 入门 —— 寄存器与 GPIO。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

STM32 入门 —— 寄存器与 GPIO

STM32 总线构图:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

寄存器

什么是寄存器

根据百度百科介绍,寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。简单来说,寄存器就是存放东西的东西,存放的东西是指令、数据或地址

存放数据的寄存器最容易理解,不同的数据存在不同的寄存器下,不同的寄存器有不同的地址,要想获得数据,我们直接访问寄存器,就可以直接获得数据

指令、地址寄存器与数据寄存器相似,存放的都是 0/1 编码,由于单片机只认识机器码,机器码都是 0/1 ,只是在特别的规定下,数据寄存器中的 0/1 编码表示数据,而指令寄存器李存放的表示指令

如何找到寄存器地址

查找《STM32中文参考手册_V10》,在手册的第 28 页给出了不同寄存器的地址范围,但是手册并没有直接给出,需要稍加计算,可以得出寄存器地址

下面以读取 PB3 引脚电平为例,介绍查找步骤

首先,找到 GPIOB 的基地址:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

PB3 引脚相关信息可以查看《STM32F103x8, STM32F103xB数据手册》:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

可知 PB3 引脚既可以输入也可以输出

然后,需要找到输入端口寄存器的地址偏移:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

由手册可知,地址偏移为 0x08 ,最终可知地址为:0x40010c00 + 0x08 = 0x40010c08

最后找到对应的位置:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

这个寄存器的位数是 32 位,虽然高 16 位没有用到,每个寄存器都占据 4 个字节,32位,而 CPU 的总线一次可以操作 32 位

最后得出:PB3 的输入数据位于 0x4001 0C08 这个地址上,这个地址上存放数据的右起第 4 个位就是 PB3 引脚对应的高低电平

访问地址代码如下:

unsigned int *pGPIOB_IDR = (unsigned int *)0x40010C08;
 unsigned char PB3 = *pGPIOB_IDR & 0x8;//取出从右往左数的第4位

直接访问的操作并不好用,每操作一个寄存器就必须去查看数据手册,然后找找这个寄存器的地址
意法半导体公司为了方便大家使用,就把这些寄存器都起了一目了然的名字,把寄存器与地址映射关系放在他们提供的头文件里。这个文件就是 stm32f10x.h

寄存器映射原理

寄存器映射是在存储器的基础上进行的,所以在了解寄存器映射原理之前,需要了解存储器映射原理

存储器映射

存储器在产家制作完成后是一片没有任何信息的物理存储器,而 CPU 要进行访存就涉及到内存地址的概念,因此存储器映射就是为物理内存按一定编码规则分配地址的行为。值得注意,存储器映射一般是由产家规定,用户不能随意更改

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

注意:STM32 中,I-Code Bus与D-Code Bus 默认映射到 0x00000000 ~ 0x1FFFFFFF 内存地址段;AHB 系统总线默认映射到0x20000000 ~ 0xDFFFFFFF 和 0xE0100000 ~ 0xFFFFFFFF 两个内存地址段;APB 外设总线默认映射到 0xE0040000 ~ 0xE00FFFFF 内存地址段,但由于 TPIU、ETM 以及 ROM 表占用部分空间,实际可用地址区间为 0xE0042000~0xE00FF000

寄存器

寄存器映射是在存储器映射基础上进行的

STM32 中,对硬件操作,本质上就是对寄存器操作。在存储器片上外设区域,四字节为一个单元,每个单元对应不同的功能。

当我们控制这些单元时就可以驱动外设工作,我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元。

但若每次都是通过这种方式访问地址,不好记忆且易出错。这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名实质上就是寄存器名字。给已分配好地址(通过存储器映射实现)的有特定功能的内存单元取别名的过程就叫寄存器映射。

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

以 GPIO 寄存器 CRL 为例,给出 CRL 定义:

typedef struct
{
	__IO unit32_t CRL;
	__IO uint32_t CRL;
	__IO uint32_t CRH;
	__IO uint32_t IDR;
	__IO uint32_t ODR;
	__IO uint32_t BSRR;
	__IO uint32_t BRR;
	__IO uint32_t LCKR;
} GPIO_TypeDef;

在实际使用时,会有 GPIOA->CRL=0x0000 0000 这种写法,表示将 16 进制数 0 赋值给 GPIOA 的 CRL 寄存器所在的存储单元。而 GPIOA->CRL 就构造了一个寄存器映射。具体过程如下:

#define PERIPH_BASE	((unit32_t)0x40000000)

这里属于存储器级别的映射,将外设基地址映射到 0x40000000 ,可以对应下图:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

#define APB2PERIPH_BASE	(PERIPH_BASE + 0X10000)

这里对外设基地址进行偏移量为 0x10000 的地址偏移,偏移到 APB2 总线对应外设区:

#define GPIOA_BASE	(APB2PERIPH_BASE + 0x0800)

这里对 APB2 外设基地址进行偏移量为 0x0800 的地址偏移,偏移到 GPIOA 对应区域:

#define GPIOA	((GPIO_TypeDef *) 	GPIOA_BASE)

这里将 GPIOA 宏定义为 GPIOA 基地址经过强制类型转换为 GPIO_TypeDef 的指针,这样的作用是使 GPIOA 结构体内对应的成员按顺序填充内存区域,如图3所示。因此 GPIOA 的 CRL 寄存器就是作为 GPIOA 基地址后的第一个内存块,GPIOA->CRL 的本质就是这个内存块的地址,或者说是用 GPIOA->CRL 给这个地址取了个形象的别名,即寄存器映射。

地址映射原理

在 STM32 开发中,我们通常使用库进行开发。说白了,32 开发是从底层一层一层封装上去的。到我们开发者这里,就是使用最上层的接口进行开发。但是一层一层看下去,还是对寄存器的控制,要控制寄存器,就需要操作寄存器地址。

stm32 地址映射如下:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

在倒数第三紫色区域是片上外设的地址区域,这里反映了片上外设的地址,我们通过操作这些地址,便能操作这些外设寄存器。
在 stm32 中,有三大总线,AHB 总线,APB1 总线以及 APB2 总线。不同的外设挂载在不同的总线上。比如 GPIO,串口 1 ,ADC 以及部分定时器挂载在 APB2 总线上。
打开 stm32f10x.h 这个文件,这个文件主要包含 stm32 中寄存器地址和结构体类型定义,在使用到固件库的地方都要包含该头文件。这里通过一些宏定义代码来说明一下地址映射与挂载总线的关系。

#define GPIOC_BASE	(APB2PERIPH_BASE + 0x1000)
#define APB2PERIPH_BASE	(PERIPH_BASE + 0x10000)
#define PERIPH_BASE	((unit32_t)0x40000000)

从代码可以看到 APB2 之类的字眼,这不是总线么?注意,有一个 PERIPH_BASE 的地址为 0x40000000 ,这不是片上外设的首地址么。这里,这个地址称作外设基地址。同样,APB2PERIPH_BASE 称作 APB2 总线外设基地址,毕竟都有 base。

总线的基地址:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

也就是说,该总线上所挂载的模块都在这个地址区间内。下面的图是挂载在总线上面各寄存器以及寄存器组的地址:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

GPIO

对于一个单片机,对基本的代码操作就是点亮 LED 灯,也就是说要对 I/O 口进行操作,STM32 中的 I/O 端口就是 GPIO ,软件可以控制的通用输入输出端口,STM32 通过 GPIO 引脚链接外部设备,达到与外部通信,控制以及采集数据的功能

引脚分类与说明如下:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

基础知识

STM32F103C8 的开发板里总共有 5 组 I/O 口、 37 个IO口,分别是 GPIOA~GPIOG 。每个 I/O 端口位可以自由编程,可以由软件分别配置成多种模式,但 I/O 端口寄存器必须按 32 位字节访问,不允许半字或单字节访问。

GPIOx_BSRR 和 GPIOx_BRR 寄存器允许对任何 GPIO 寄存器的读/更改的独立访问;这样,在读和更改访问之间产生 IRQ 时不会发生危险。端口位配置 CNFx[1:0]=xxb,MODEx[1:0]=xxb

GPIO_InitTypeDef 是 GPIO 口的一个定义结构体,包含一个 16 位的变量 GPIO_Pin ;一个 GPIOSpeed_TypeDef 枚举结构体GPIO_Speed;一个 GPIOMode_TypeDef 枚举结构体 GPIO_Mode ;这 3 个变量可以在外部被定义,用于初始化或者改变某些 GPIO 的速度跟类型。

STM32 每个 GPI/O 端口有两个 32 位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR,GPIOx_ODR),一个 32 位置位/复位寄存器(GPIOx_BSRR),一个 16 位复位寄存器(GPIOx_BRR)和一个 32 位锁定寄存器(GPIOx_LCKR)。

GPIO 的工作模式主要有八种:4 种输入方式,4 种输出方式,分别为输入浮空,输入上拉,输入下拉,模拟输入;输出方式为开漏输出,开漏复用输出,推挽输出,推挽复用输出。同时,GPIO还支持三种最大翻转速度(2MHz、10MHz、50MHz)

I/O 端口位的基本结构:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

5v 兼容 I/O 端口位的基本结构:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

GPIO 电平标准

在 STM32 的数据手册中我们可以查到,STM32 单片机的 I/O 口电平兼容 CMOS 电平和 TTL 电平,逻辑电平 0 所代表得电压范围在 0.8v 以下,大于 2v 的话就代表逻辑 1 。

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

所有 I/O 端口都是 CMOS 和 TTL 兼容(不需软件配置),它们的特性考虑了多数严格的 CMOS 工艺或 TTL 参数:
● 对于 VIH:
− 如果 VDD 是介于 [2.00V~3.08V] ;使用CMOS特性但包含 TTL。
− 如果 VDD 是介于 [3.08V~3.60V] ;使用TTL特性但包含 CMOS。
● 对于 VIL:
− 如果 VDD 是介于 [2.00V~2.28V] ;使用TTL特性但包含 CMOS。
− 如果 VDD 是介于 [2.28V~3.60V] ;使用CMOS特性但包含 TTL。

由于本人不是专业学习电路的,所以这里仅作上述简单介绍

GPIO 端口初始化

GPIO 端口初始化大致可以分为三个步骤:

1.时钟配置

2.输入输出模式设置

3.最大速率设置

时钟配置

对于 STM32 有 5 个时钟源,如下:

简称 时钟源名称 频率范围
HSI 高速内部时钟 8MHZ
HSE 高速外部时钟 4MHZ~16MHZ
LSI 低速内部时钟 40KHZ
LSE 低速外部时钟 32.768KHZ
PLL 锁相环倍频输出 输出频率最大不得超过 72MHz

时钟树如下图:

寄存器和gpio,嵌入式开发,stm32,单片机,嵌入式硬件

程序刚启动的时候,stm32采用的为内部高速时钟。如果需要采用外部时钟,需要按照如下的方式配置:

  1. 时钟初始化,即将时钟的寄存器采用默认值。

  2. 开始外部时钟且外部时钟起震准备就绪。

  3. 设置 PLLXTPRE(只能在关闭PLL时才能写入此位),可选择分频不分频。

  4. 设置进入 PLL 的源时钟(只能在关闭 PLL 时才能写入此位)。因为采用外部时钟所以只有一种设置。

  5. 设置 PLL 倍频系数 PLLMUL(只有在 PLL 关闭的情况下才可被写入)。

  6. 开启 PLL ,且准备就绪。

  7. 设置 SW ,选择时钟源为系统时钟。

  8. 判断是否是预选的时钟为系统时钟。

输入输出模式设置
  1. GPIO_Mode_AIN 模拟输入 (应用ADC模拟输入,或者低功耗下省电)

  2. GPIO_Mode_IN_FLOATING 浮空输入 (浮空就是浮在半空,可以被其他物体拉上或者拉下,可以用于按键输入)

  3. GPIO_Mode_IPD 下拉输入 (IO内部下拉电阻输入)

  4. GPIO_Mode_IPU 上拉输入 (IO内部上拉电阻输入)

  5. GPIO_Mode_Out_OD 开漏输出(开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行)

  6. GPIO_Mode_Out_PP 推挽输出 (推挽就是有推有拉电平都是确定的,不需要上拉和下拉,IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的 )

  7. GPIO_Mode_AF_OD 复用开漏输出(片内外设功能(I2C的SCL,SDA))

  8. _Mode_AF_PP 复用推挽输出 (片内外设功能(TX1,MOSI,MISO.SCK.SS))

代码 模式
GPIO_Mode_IPU 上拉输入
GPIO_Mode_AIN 模拟输入
GPIO_Mode_IN_FLOATING 浮空输入
GPIO_Mode_IPD 下拉输入
GPIO_Mode_Out_OD 开漏输出
GPIO_Mode_Out_PP 推挽输出
GPIO_Mode_AF_OD 复用开漏输出
GPIO_Mode_AF_PP 复用推挽输出
输入模式名称 作用 原理
上拉输入模式 在默认状态下(GPIO引脚无输入),读取得的 GPIO 引脚数据为 1,高电平 与 VDD 相连的为上拉电阻。再连接到施密特触发器就把电压信号转化为1的数字信号存储在输入数据寄存器( IDR )
下拉输入模式 在默认状态下( GPIO 引脚无输入),读取得的 GPIO 引脚数据为 0,低电平 与 VSS 相连的为下拉电阻。再连接到施密特触发器就把电压信号转化为 0 的数字信号存储在输入数据寄存器( IDR )
浮空输入模式 配置成这个模式直接用电压表测量其引脚电压为1点几伏,这是个不确定值 没有接上拉,也没有接下拉电阻,经由触发器输入
模拟输入模式 把电压信号传送到片上外设模块,如传送至给 ADC 模块,由 ADC 采集电压信号 关闭了施密特触发器,不接上、下拉电阻
输出模式名称 作用 原理
普通推挽输出 推挽输出的供电平为 0 伏,高电平为 3.3 pp*伏 在输出高电平时,P-MOS 导通,低电平时,N-MOS 管导通。两个管子轮流导通,一个负责灌电流,一个负责拉电流,使其负载能力和开关速度都比普通的方式有很大的提高
普通开漏输出 控制输出为 0,低电平;若控制输出为 1,为高阻态 如果我们控制输出为 0,低电平,则使 N-MOS 管导通,使输出接地,若控制输出为1 (无法直接输出高电平),则既不输出高电平,也不输出低电平,为高阻态
复用推挽输出 GPIO 的引脚用作串口的输出
复用开漏输出 用在 IC、SMBUS 这些需要线与功能的复用场合

注意:在使用任何一种开漏模式,都需要接上拉电阻

最大速率设置

GPIO 的输出速率:GPIO 电平每秒切换的最大次数

这个输出速率主要体现 I/O 驱动电路的输出反应能力,通过选择不同的输出驱动速率,实现最佳的噪声与和功耗控制。不难理解,选择输出驱动速率越高,噪声也越大,相应的芯片功耗也会越大

当 STM32 的 GPIO 端口设置为输出模式时,有三种速度可以选择:2MHz、10MHz 和 50MHz,这个速度是指 I/O 口驱动电路的速度,是用来选择不同的输出驱动模块,达到最佳的噪声控制和降低功耗的目的。

对于 STM32 GPIO 输出速率的选择问题,我们在开发应用中应多加注意。如果因为这个输出速率选择导致麻烦,原因往往比较隐晦,很难直接从代码语句或程序逻辑上找到突破。

高频的驱动电路,噪声也高,当你不需要高的输出频率时,请选用低频驱动电路,这样非常有利于提高系统的EMI性能。

当然如果你要输出较高频率的信号,但却选用了较低频率的驱动模块,你很可能会得到失真的输出信号。实际上芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路,用户可以根据自己的需要选择合适的驱动电路。在满足实际应用需求的前提下,速率就低不就高,这对降低功耗、减少噪声、改善 EMI 都有好处

gpio 不同速率设置对实际开发的影响:
1、LED 闪烁快慢不一致
2、Audio 噪声
其中有 I2S 的音频播放功能。在调试时用到 Printf 串口打印,发现使用 printf 输出时会出现噪音,如果关闭 printf 则正常。直到将 UART 的 TX 输出端口的管脚输出速率由 very high 改为 Low 后噪声消失。
3、SPI 通信异常
用到 SPI 通信。STM32 做主,其它外围器件做从,有时发现 SPI 读取数据总是出错。对于这里的通信出错,如果 SPI 通信端口脚的输出速率选择跟实际通信速率不合适的话也会出现。相比实际速率需求,过高或过低了都会导致通信出错。

注意:GPIO 的引脚速度是指 I/O 口驱动电路的响应速度而不是输出信号的速度,输出信号的速度与你的程序有关

代码示例
/* GPIO_InitTypeDef结构体 */
typedef enum
{
  GPIO_Speed_10MHz = 1,  //枚举常量,值为 1,代表输出速率最高为 10MHz
  GPIO_Speed_2MHz,       //对不赋值的枚举变量,自动加 1,此常量值为 2
  GPIO_Speed_50MHz       //常量值为 3
} GPIOSpeed_TypeDef;
 
typedef enum
{
  GPIO_Mode_AIN = 0x0, //模拟输入模式
  GPIO_Mode_IN_FLOATING = 0x04, //浮空输入模式
  GPIO_Mode_IPD = 0x28, //下拉输入模式
  GPIO_Mode_IPU = 0x48, //上拉输入模式
  GPIO_Mode_Out_OD = 0x14, //开漏输出模式
  GPIO_Mode_Out_PP = 0x10, //通用推挽输出模式
  GPIO_Mode_AF_OD = 0x1C,  //复用功能开漏输出
  GPIO_Mode_AF_PP = 0x18   //复用功能推挽输出
} GPIOMode_TypeDef;
 
typedef struct
{
  uint16_t GPIO_Pin;              /* 指定要配置的引脚 */
  GPIOSpeed_TypeDef GPIO_Speed;   /* 指定GPIO引脚输出的最高频率 */
  GPIOMode_TypeDef GPIO_Mode;     /* 指定GPIO引脚工作状态 */
} GPIO_InitTypeDef;
/* 初始化GPIO -- GPIO_Init() */
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  /* 断言,用于检查输入的参数是否正确 */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
  /*---------------------------- GPIO 的模式配置 -----------------------*/
  /*把输入参数 GPIO_Mode 的低四位暂存在 currentmode*/
  currentmode = ((uint32_t)GPIO_InitStruct -
                 > GPIO_Mode) & ((uint32_t)0x0F);
  /*判断是否为输出模式,输出模式,可输入参数中输出模式的 bit4 位都是 1*/
  if ((((uint32_t)GPIO_InitStruct -
        > GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  {
    /* 检查输入参数 */
    assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
    /* 输出模式,所以要配置 GPIO 的速率:00(输入模式) 01(10MHz) 10(2MHz) 11 */
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
  /*----------------------------配置 GPIO 的 CRL 寄存器 -----------------------
  -*/
  /* 判断要配置的是否为 pin0 ~~ pin7 */
  if (((uint32_t)GPIO_InitStruct -
       > GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    /*备份原 CRL 寄存器的值*/
    tmpreg = GPIOx->CRL;
    /*循环,一个循环设置一个寄存器位*/
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      /*pos 的值为 1 左移 pinpos 位*/
      pos = ((uint32_t)0x01) << pinpos;
      /* 令 pos 与输入参数 GPIO_PIN 作位与运算,为下面的判断作准备 */
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
      /*判断,若 currentpin=pos,说明 GPIO_PIN 参数中含的第 pos 个引脚需要配置*/
      if (currentpin == pos)
      {
        /*pos 的值左移两位(乘以 4),因为寄存器中 4 个寄存器位配置一个引脚*/
        pos = pinpos << 2;
        /*以下两个句子,把控制这个引脚的 4 个寄存器位清零,其它寄存器位不变*/
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* 向寄存器写入将要配置的引脚的模式 */
        tmpreg |= (currentmode << pos);
        /* 复位 GPIO 引脚的输入输出默认值*/
        /*判断是否为下拉输入模式*/
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          /*下拉输入模式,引脚默认置 0,对 BRR 寄存器写 1 可对引脚置 0*/
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }
        else
        {
          /*判断是否为上拉输入模式*/
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
            /*上拉输入模式,引脚默认值为 1,对 BSRR 寄存器写 1 可对引脚置 1*/
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
    /*把前面处理后的暂存值写入到 CRL 寄存器之中*/
    GPIOx->CRL = tmpreg;
  }
  /*---------------------------- 以下部分是对 CRH 寄存器配置的 -----------------
  --------当要配置的引脚为 pin8 ~~ pin15 的时候,配置 CRH 寄存器, -----
  ------------- -----这过程和配置 CRL 寄存器类似------------------------------
  ------
  -------读者可自行分析,看看自己是否了解了上述过程--^_^-----------*/
  /* Configure the eight high port pins */
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
    tmpreg = GPIOx->CRH;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
      /* Get the port pins position */
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding high control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
        /* Set the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
    GPIOx->CRH = tmpreg;
  }
}

参考资料:

1.【嵌入式系统】存储器映射与寄存器映射原理

2.STM32寄存器的简介、地址查找,与直接操作寄存器

3.简谈stm32的地址映射

4.STM32入门之GPIO详解

5.STM32入门-GPIO初始化步骤

6.GPIO代码详解文章来源地址https://www.toymoban.com/news/detail-766391.html

到了这里,关于STM32 入门 —— 寄存器与 GPIO的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32 | STM32时钟分析、GPIO分析、寄存器地址查找、LED灯开发(第二天)

    寄存器 :寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成 在计算机领域,寄存器是CPU内部的元件,包括通用寄存器、专用寄存器和 控制寄存器 。寄存

    2024年03月08日
    浏览(23)
  • 嵌入式学习笔记——使用寄存器编程操作GPIO

    上一篇重点介绍了STM32 GPIO的输入输出模式,在整个框图中我们发现需要我们使用代码来控制GPIO的模式,本文的重点就是使用寄存器的编程方式,实现对于GPIO口的操作。 在这里首先需要做一个区分,我们常见的STM32的开发方式有两种,也就是寄存器开发与库函数开发。寄存器

    2023年04月09日
    浏览(20)
  • STM32F103ZET6 GPIO工作模式介绍+使用寄存器点亮第一个LED灯

    目录  GPIO的工作模式介绍 1.输入模式(模拟、上拉、下拉、浮空) 2.输出模式(推挽/开漏) 3.复用功能(推挽/开漏) 4.模拟输入输出(上下拉无影响) 如何使用寄存器点亮第一个LED灯         在输入模式时,施密特触发器打开,输出被禁止。可通过输入数据寄存器 GPIOx_

    2024年02月06日
    浏览(23)
  • STM32萌新学习日志——用GPIO外设寄存器输出点亮LED对比库函数点亮LED——谈学习感悟

            本周学习STM32单片机,由于之前学过51单片机,为了便于切入,先学习了寄存器点亮LED灯的方法,整体思路与51单片机相似。在基本掌握后,尝试自己构建库函数雏形,过程比较困难,后面开始借鉴野火官方库函数。         这里其实建议大家在学会构建库函数后

    2024年03月17日
    浏览(61)
  • STM32 | GPIO口的普通与复用如何配置与用法,本文降从最底层教你如何查看手册运用寄存器来实现GPIO口的配置

    🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -  蓝桥杯嵌入式_勾栏听曲_0的博客 🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️ 📌个人主

    2024年02月14日
    浏览(20)
  • 【六一】【海思SS528】GPIO寄存器操作 - 使能GPIO管脚输出高、低电平

    这篇文章根据海思SS528芯片提供的 《22AP30 H.265编解码处理器用户指南.pdf》 文档(文档路径: SS528ReleaseDochardwarechip ),演示怎样操作GPIO寄存器来控制某个IO口输出高电平,本文控制的是 GPIO20_6 。 关于如何操作寄存器的步骤,在文档的 13.6.3 有提供说明,结合本文目的分为三个

    2024年02月08日
    浏览(25)
  • 荔枝派zero驱动开发04:GPIO操作(寄存器方式)

    参考:https://wiki.sipeed.com/soft/Lichee/zh/Zero-Doc/Drive/GPIO_mmap.html 上一篇:荔枝派zero驱动开发03:设备树基础 下一篇:荔枝派zero驱动开发05:GPIO操作(使用GPIO子系统) :ioremap/iounmap,copy_from_user/copy_to_user,readl/writel 设备树修改: 本文不涉及设备树操作,但由于默认设备树

    2024年01月20日
    浏览(24)
  • 嵌入式学习笔记——STM32的USART相关寄存器介绍及其配置

    上一篇中,对串口做了个概述,主要是介绍了串口通信的特征,异步串行全双工通信,然后就是结合串口的框图梳理了一下STM32中USART的配置流程以及发送接收数据的流程,本文将接着上篇的内容,对串口的寄存器做个介绍,然后实现一个简单的收发实验。 根据之前GPIO的经验

    2024年02月05日
    浏览(21)
  • 【ARM裸机编程 | 海思SS528】- 操作 GPIO 寄存器输出低电平点亮 LED 灯

    这篇文章主要介绍在 海思SS528 开发板,去操作某个 GPIO 寄存器输出高、低电平,来熄灭或点亮 LED 灯。 首先,了解一下 ARM裸机编程 ,也就是在一块没有移植操作系统的ARM开发板去编程,相当于把它当成单片机去使用,很多与硬件相关的操作都需要直接读写该硬件的寄存器。

    2024年02月10日
    浏览(20)
  • stm32的BRR寄存器和BSRR寄存器

    1、BRR---   bit   RESET(置0)  register   //高16位无,低16位置1为0,不能写1 2 、BSRR---   bit   SET(设置1或0)       register   //低16位设置1为0 BSRR:用于低16位的作用是让指定的IO口置1;而高16位的作用是让指定的IO口置0。  

    2024年02月11日
    浏览(22)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包