3

3191751271

V1

2022/10/19阅读:28主题:默认主题

[STM32] 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总线偏移地址
GPIOA 0x4001 0800 0x0000 0800
GPIOB 0x4001 0C00 0x0000 0C00
GPIOC 0x4001 1000 0x0000 1000
GPIOD 0x4001 1400 0x0000 1400
GPIOE 0x4001 1800 0x0000 1800
GPIOF 0x4001 1C00 0x0000 1C00
GPIOG 0x4001 2000 0x0000 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 */

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566

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);
 }
}
1234567891011121314151617181920212223

END

分类:

后端

标签:

后端

作者介绍

3
3191751271
V1