[005] [STM32] GPIO工作模式与寄存器详解

本文详细介绍了STM32的GPIO模块,包括电源符号、TTL肖特基触发器、端口引脚保护二极管、推挽输出和开漏输出的工作原理。还解析了GPIO的输入模式、输出模式、模拟输入模式和复用模式,以及GPIO模块的寄存器配置。最后,给出了GPIO直接使用寄存器点灯的应用示例,强调了必须以字的方式操作GPIO外设寄存器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

STM32
Contents
模电知识
GPIO模块电路结构
输入模式
输出模式
模拟输入模式
复用模式
GPIO模块寄存器
应用示例

本文为百问网&韦东山【物联网智能家居实战训练营】课程笔记

1 模电知识

在这里插入图片描述

  • 电源符号含义

VDD:D=device 表示芯片内部工作电源(一般VDD<VCC)。端口引脚电路中的VDD表示能够容忍3.3V电压(最大3.6V),如果是VDD_FT则表示能够忍5V。
VSS:S=series 表示公共连接,通常指电路公共接地端电压。

  • TTL肖特基触发器

TTL肖特基触发器即为用肖特基管构成的施密特触发器,施密特触发器利用门阀电压将引脚模拟信号变成矩形信号,进行转化为0/1数字信号存入输入数据寄存器。

  • 端口引脚保护二极管

当引脚电压高于VDD 时,上方的二极管导通,当引脚电压低于VSS 时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。(虽有这样的保护,但不能驱动大功率器件,如直接驱动电机,电机堵转的反向电流会烧毁芯片)

  • 推挽输出
    在这里插入图片描述

PMOS栅极低电平导通,NMOS栅极高电平导通。一般PMOS源极接VDD,NMOS源极接GND。
MOS管中寄生二极管作用是防止VDD过压的情况下,烧坏mos管。

推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载拉取电流。下面分析电路:

当输入为高电平时,经过反向后输出到MOS管栅极为低电平,PMOS的SD导通,OUT输出为高电平VDD;当输如为低时,NMOS导通,OUT输出为低电平。当引脚高低电平切换时,两个管子轮流导通,P 管负责灌电流,N 管负责拉电流,使其负载能力和开关速度都比普通的方式有很大的提高。

  • 开漏输出
    在这里插入图片描述

  • 开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。

  • 上拉电阻的阻值决定了逻辑电平转换的沿的速度 :阻值越大,速度越低,功耗越小;所以负载电阻的选择要兼顾功耗和速度。一般会带来上升沿的延时,因为上升沿是通过外接上拉电路对负载供电,所以若对延时有要求,建议用下降沿输出。

  • 开漏输出可以实现线与功能,可以将多个开漏输出的Pin,经上拉电阻连接到一条总线上,实现与逻辑,主要用于IIC、SMBus总线。

线与:当在很多个开漏引脚连在一起时,外接一上拉电阻,如果有一个引脚输出为逻辑0,相当于短路接地,所以外电路逻辑电平便为0;只有当所有引脚均输出高阻态时,才由上拉电阻提供高电平,即为逻辑1。

2 GPIO模块电路结构

在这里插入图片描述

2.1 输入模式

在这里插入图片描述

  1. 输出驱动器关闭(N/PMOS关闭)
  2. 施密特触发器打开,可以获取引脚状态
  3. 通过寄存器使能上/下拉电阻配置输入模式下的三种状态
  4. 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器
  5. 引脚电平状态将存入输入数据寄存器
  • 浮空输入:上下拉电阻全断开
    在这里插入图片描述

  • 上拉输入:上拉电阻打开,下拉电阻关闭
    在这里插入图片描述

  • 下拉输入:下拉电阻打开,上拉电阻关闭
    在这里插入图片描述

注意

  • 设计按键电路时可以利用芯片内部弱上拉和下拉电阻,这样省去了外接的电阻。
  • 浮空输入状态下,IO的电平状态是不确定的,完全由外部输入决定。

2.2 输出模式

在这里插入图片描述

  1. 输出驱动器打开
  2. 施密特触发器打开
  3. 弱上拉和下拉电阻被禁止
  4. 在每个APB2时钟周期,出现在I/O脚上的数据被采样到输入数据寄存器
  5. 在开漏模式时,对输入数据寄存器的读访问可得到I/O状态;在推挽式模式时,对输出数据寄存器的读访问得到最后一次写的值。
  • 通用推挽输出
    在这里插入图片描述

  • 当输出数据寄存器相应位为1时,同相端输出为1,但经过反相器(小圆圈)后,到PMOS栅极输出为0,反相端反相后输出为1,此时PMOS导通;NMOS截止,引脚输出高电平。(推-灌电流

  • 当输出数据寄存器相应位为0时,PMOS截止,NMOS导通,引脚输出低电平。(挽-拉电流


  • 通用开漏输出
    在这里插入图片描述
    开漏输出时,PMOS关闭(输出数据寄存器的1将端口置于高阻态),只有NMOS工作,但是此时只能输出低电平,要输出高电平必须外加上拉电阻。

上图为加了上拉电阻的开漏输出(需用户外接):当输出数据寄存器相应位为1时,反相端输出0,此时NMOS截止,由外加的上拉电阻提供高电平。当输出数据寄存器相应位为0时,NMOS导通,引脚输出低电平

2.3 模拟输入模式

在这里插入图片描述

  1. 输出驱动器关闭
  2. 施密特触发器关闭
  3. 弱上拉和下拉电阻被禁止
  4. 输入数据寄存器的值为0(处于高阻抗)
  5. 功耗最小

因为模拟信号经过施密特触发器后只有0/1两种状态,因此信号源输入在施密特触发器前。类似地,当GPIO 引脚用于DAC 作为模拟电压输出通道时,DAC 的模拟信号输出就不经过双MOS 管结构,模拟信号直接输出到引脚。

模拟状态与模拟外设复用引脚区别
在这里插入图片描述

  • 模拟状态:表示引脚功能选择为模拟模式,但不作为任何片内模拟外设(ADC)的复用引脚,只是为了减少系统功耗。
  • 模拟外设复用引脚:表示引脚作为片内模拟外设的复用引脚,用于完成相应功能操作,如ADC信号采集。

2.4 复用模式

在这里插入图片描述

  1. 输出可配置为推挽或者开漏模式,内置外设的信号驱动输出驱动器
  2. 施密特触发器打开
  3. 弱上拉和下拉电阻被禁止
  4. 在每个APB2时钟周期,出现在I/O脚上的数据被采样到输入数据寄存器
  5. 在开漏模式时,对输入数据寄存器的读访问可得到I/O状态;在推挽式模式时,对输出数据寄存器的读访问得到最后一次写的
  • 复用推挽输出
    在这里插入图片描述
  • 复用开漏输出
    在这里插入图片描述

  • 对于复用的输入功能,端口必须配置成输入模式(浮空、上拉或下拉)且输入引脚必须由外部驱动。
  • 对于复用输出功能,端口必须配置成复用功能输出模式(推挽或开漏)。
  • 对于双向复用功能,端口位必须配置复用功能输出模式(推挽或开漏)。

如果把端口配置成复用输出功能,则引脚和输出寄存器断开,并和片上外设的输出信号连接。
如果软件把一个GPIO脚配置成复用输出功能,但是外设没有被激活,它的输出将不确定。


注意

  • STM32复位后,IO端口处于浮空输入状态(CNFx[1:0]=01b,MODEx[1:0]=00b);JTAG引脚复位以后,处于上拉或者下拉状态。
  • stm32具有GPIO锁定机制,即锁定GPIO配置,下次复位前不能再修改端口位的配置。
  • 所有IO端口都具有外部中断能力,端口必须配置成输入模式,才能使用外部中断功能。
  • 当LSE振荡器关闭时,OSC32_IN/OSC32_OUT可以用作通用GPIO PC14/PC15。当进入待机模式或者备份域由Vbat供电时,不能使用PC14/PC15的GPIO口功能。
  • PC13/PC14/PC15只能用于2MHz的输出模式(LSE关闭,PC13关闭入侵检测),最多只能带30pF的负载,而且这些I/O口绝对不能当作电流源(如驱动LED)。(参考STM32中文手册4.1.2)
  • 一般上下拉电阻的阻值都在30-50K之间。这样可以增强MCU的抗干扰能力。
  • 芯片内部上/下拉电阻不影响GPIO输出模式。

3 GPIO模块寄存器

注意必须以字(32位)的方式操作GPIO外设寄存器!

端口模式与输出速度配置:
在这里插入图片描述
GPIO所有寄存器思维导图一览:
在这里插入图片描述
GPIO寄存器地址映像和复位值:
在这里插入图片描述
GPIO外设基地址与相对于APB2总线(0x4001 0000)的偏移地址:

GPIO外设基地址相对APB2总线偏移地址
GPIOA0x4001 08000x0000 0800
GPIOB0x4001 0C000x0000 0C00
GPIOC0x4001 10000x0000 1000
GPIOD0x4001 14000x0000 1400
GPIOE0x4001 18000x0000 1800
GPIOF0x4001 1C000x0000 1C00
GPIOG0x4001 20000x0000 2000

4 应用示例

直接使用寄存器点灯(PA8-红灯 PD2-黄灯),系统时钟启动文件跳转自动配置。
led.h

#ifndef __LED_H
#define	__LED_H
#include <stdio.h>

typedef unsigned int uint32_t;

#define _IO 	volatile
#define _I  	volatile const	
#define _O  	volatile 
	
#define PERIPH_BASE         0x40000000UL 
#define APB1_BASE           PERIPH_BASE
#define APB2_BASE           (PERIPH_BASE + 0x10000)
#define AHB_BASE            (PERIPH_BASE + 0x20000)

#define GPIOA_BASE       	(APB2_BASE + 0x0800)
#define GPIOD_BASE       	(APB2_BASE + 0x1400)
#define RCC_BASE            (AHB_BASE + 0x1000)

typedef struct
{
  _IO uint32_t CRL;
  _IO uint32_t CRH;
  _I uint32_t IDR;
  _IO uint32_t ODR;
  _IO uint32_t BSRR;
  _IO uint32_t BRR;
  _IO uint32_t LCKR;
} GPIO_Init_t;

typedef struct
{
  _IO uint32_t CR;
  _IO uint32_t CFGR;
  _IO uint32_t CIR;
  _IO uint32_t APB2RSTR;
  _IO uint32_t APB1RSTR;
  _IO uint32_t AHBENR;
  _IO uint32_t APB2ENR;
  _IO uint32_t APB1ENR;
  _IO uint32_t BDCR;
  _IO uint32_t CSR;
} RCC_t;

#define GPIOA       ((GPIO_Init_t*)GPIOA_BASE)
#define GPIOD       ((GPIO_Init_t*)GPIOD_BASE)
#define RCC         ((RCC_t *) RCC_BASE)

#define RED_LED_GPIO_PORT       GPIOA
#define RED_LED_GPIO_PIN        (0x0100)    // PIN8

#define YELLOW_LED_GPIO_PORT    GPIOD
#define YELLOW_LED_GPIO_PIN     (0x0004)    // PIN2

#define RED_LED_ON          (RED_LED_GPIO_PORT->BRR |= RED_LED_GPIO_PIN)
#define RED_LED_OFF         (RED_LED_GPIO_PORT->BSRR |= RED_LED_GPIO_PIN)
#define RED_LED_TOGGLE      (RED_LED_GPIO_PORT->ODR ^= RED_LED_GPIO_PIN)

#define YELLOW_LED_ON       (YELLOW_LED_GPIO_PORT->BRR |= YELLOW_LED_GPIO_PIN)
#define YELLOW_LED_OFF      (YELLOW_LED_GPIO_PORT->BSRR |= YELLOW_LED_GPIO_PIN)
#define YELLOW_LED_TOGGLE   (YELLOW_LED_GPIO_PORT->ODR ^= YELLOW_LED_GPIO_PIN)

void LED_Init(void); 	

#endif /* __LED_H */

led.c & main.c

void LED_Init(void)
{
    RCC->APB2ENR |= 1 << 2; // PortA
    RCC->APB2ENR |= 1 << 5; // PortD

	RED_LED_GPIO_PORT->CRH &= ~(0x0f << (0 * 4));
    RED_LED_GPIO_PORT->CRH |= 0x03 << (0 * 4);
    RED_LED_GPIO_PORT->BSRR |= 0x01 << 8;

	YELLOW_LED_GPIO_PORT->CRL &= ~(0x0f << (2 * 4));
    YELLOW_LED_GPIO_PORT->CRL |= 0x03 << (2 * 4);
    YELLOW_LED_GPIO_PORT->BSRR |= 0x01 << 2;
}

int main()
{
	LED_Init();
	while(1)
	{
		YELLOW_LED_TOGGLE;
		HAL_Delay(500);
	}
}

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

柯西的彷徨

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

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

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

打赏作者

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

抵扣说明:

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

余额充值