GPIO详解
一、GPIO基本概念
GPIO(通用输入输出)是微控制器与外部设备交互的核心接口,具有以下特性:
- 可编程控制输入/输出模式
- 支持数字信号的读取与输出
- 集成多种保护机制
- 复用功能支持片上外设连接
二、GPIO位结构解析

2.1 保护二极管机制

-
功能:钳制输入电压在安全范围(0-3.3V)
-
工作状态 :
输入电压范围 导通情况 保护机制 V > 3.3V 上管导通 大部分电流导向VDD V < 0V(大概率是电源反接) 下管导通 电流导向VSS 0-3.3V 均不导通 正常模式
如果对保护二极管的作用不是很清楚,可以看这篇文章:理解保护二极管在GPIO过电压保护中的作用
2.2 上拉/下拉电阻
开/关:如果上面导通、下面断开,就是上拉输入模式 ,如果下面导通、上面断开,就是下拉输入模式 ,如果两个都断开,就是浮空输入模式 。
上拉和下拉的作用------>为了给输入提供一个默认的输入电平
因为对应一个数字的端口,输入不是高电平就是低电平,那如果输入引脚什么都不接,那就不确定算高电平还是低电平。而实际情况是,如果啥也不接,这时输入就会处于一种浮空的状态,引脚的输入电平极易受外界干扰而改变。为了避免引脚悬空导致的输入数据不确定,我们就需要在这里加上拉或者下拉电阻了,如果接入上拉电阻,当引脚悬空时,还有上拉电阻来保证引脚的高电平,所以上拉输入又可以称作是默认为高电平的输入模式。下拉也是同理,就是默认为低电平的输入方式。
- 作用:避免浮空输入带来的电平不确定
- 配置模式 :
- 上拉输入(默认高电平)
- 下拉输入(默认低电平)
- 浮空输入(需外接确定电平)
- 特性:弱上拉/弱下拉(约30-50KΩ),降低对信号的影响
2.3 施密特触发器

英文原文档是施密特触发器,(模电里这叫迟滞/滞回比较器,也就是施密特触发器的电路)
- 功能:信号整形(消除抖动)
- 工作原理 :
- 输入>正向阈值 ⇒ 输出高电平
- 输入<负向阈值 ⇒ 输出低电平
- 特征 :提供约200mV的迟滞电压范围
接下来经过施密特触发器整形的波形就可以直接写入输入数据寄存器了,我们再用程序读取数据输存器对应某一位的数据,就可以知道端口的输入电平了。最后上面这还有两路线路,这些就是连接到片上外设的一些端口,其中有模拟输入 ,这个是连接到ADC上的,因为ADC需要接收模拟量,所以这根线是接到施密特触发器前面 的;另一个是复用功能输入 ,这个是连接到其他需要读取端口的外设上的,比如串口的输入引脚等,这根线接收的是数字量,所以在施密特触发器后面。
2.4 双MOS管输出结构

输出部分可以由 输出数据寄存器 或片上外设 控制,两种控制方式通过这个数据选择器接到了输出控制部分。
如果选择通过输出数据寄存器进行控制,就是普通的IO口输出,写这个数据寄存器的某一位就可以操作对应的某个端口了。
位设置/清除寄存器:这个可以用来单独操作输出数据寄存器的某一位,而不影响其它位。因为这个输出数据寄存器同时控制16个端口,并且这个寄存器只能整体读写,所以如果想单独控制其中某一个端口而不影响其他端口的话,就需要一些特殊的操作方式。
- 第一种方式是先读出这个寄存器,然后用 按位与 和 按位或 的方式更改某一位,最后再将更改后的数据写回去,在C语言中就是&=和 |=的操作,这种方法比较麻烦,效率不高,对于IO口的操作而言不太合适;
- 第二种方式是通过设置这个位设置和位清除寄存器,如果我们要对某一位进行置1的操作,在位设置寄存器 的对应位写1便可,剩下不需要操作的位写0,这样它内部就会有电路,自动将输出数据寄存器中对应位置为1,而剩下写0的位则保持不变,这样就保证了只操作其中某一位而不影响其它位,并且这是一步到位的操作。如果想对某一位进行清0的操作,就在位清除寄存器 的对应位写1即可,这样内部电路就会把这一位清0了,这就是第二种方式也就是这个位设置和位清除寄存器的作用。【作用:将设置/清除寄存器的某一位写1/0就能达到单独影响输出寄存器的某一位,从而单独影响某个端口】
- 第三种操作方式【了解即可】 ,就是读写STM32中的"位带"区域,这个位带的作用就跟51单片机的位寻址作用差不多,在STM32中,专门分配的有一段地址区域,这段地址映射了RAM和外设寄存器所有的位,读写这段地址中的数据,就相当于读写所映射位置的某一位,这就是位带的操作方式,这个方式我们本课程暂时不会用到。我们的教程主要使用的是库函数来操作的,库函数使用的是读写位设置和位清除寄存器的方法

上面是P-MOS,下面是N-MOS,这个MOS管就是一种电子开关,我们的信号来控制开关的导通和关闭,开关负责将IO口接到VDD或者VSS,
在这里可以选择推挽、开漏或关闭三种输出方式。
- 推挽输出模式
在推挽输出模式下,P-MOS和N-MOS均有效,数据寄存器为1时,上管导通,下管断开,输出直接接到VDD,就是输出高电平,数据寄存器为0时,上管断开,下管导通,输出直接接到VSS,就是输出低电平,这种模式下,高低电平均有较强的驱动能力,所以推挽输出模式也可以叫强推输出模式。在推挽输出模式下,STM32对IO口具有绝对的控制权,高低电平都由STM32说的算。 - 开漏输出模式
在开漏输出模式下,这个P-MOS是无效的,只有N-MOS在工作,数据寄存器为1时,下管断开,这时输出相当于断开,也就是高阻模式;数据寄存器为0时,下管导通,输出直接接到VSS,也就是输出低电平;这种模式下,只有低电平有驱动能力,高电平是没有驱动能力的。那这个模式有什么用呢,这个开漏模式 可以作为通信协议的驱动方式,比如I2C通信的引脚,就是使用的开漏模式,在多机通信的情况下,这个模式可以避免各个设备的相互干扰,另外开漏模式还可以用于输出5V的电平信号。
比如在I0口外接一个上拉电阻到5V的电源,当输出低电平时,由内部的N-MOS直接接VSS,当输出高电平时,由外部的上拉电阻拉高至5V,这样就可以输出5V的电平信号,用于兼容一些5V电平的设备,这就是开漏输出的主要用途。
开漏模式下,输出1时,两个mos管都相当于关断,左侧相当于断路。外接5V的电能只能流向右侧,故输出5V。反之,输出0时,左下方mos管导通,外接5V的电能流到左下方Vss,且两者之间几乎没有电压降,可看做5V电压降在了上拉电阻上,故引脚输出0V
- 关闭
剩下的一种状态就是关闭,这个是当引脚配置为输入模式的时候,这两个MOS管都无效,也就是输出关闭,端口的电平由外部信号来控制。
三、GPIO工作模式详解

3.1 输入模式

模式 | 特征 |
---|---|
浮空输入 | 电压完全由外部决定 |
上拉输入 | 悬空时默认高电平 |
下拉输入 | 悬空时默认低电平 |
模拟输入 | 直连ADC(关闭数字部件) |
首先是前三个,浮空输入、上拉输入和下拉输入 。这三个模式的电路结构基本是一样的,区别就是上拉电阻和下拉电阻的连接,它们都属于数字的输入口,那特征就是,都可以读取端口的高低电平,当引脚悬空时,上拉输入默认是高电平,下拉输入默认是低电平,而浮空输入的电平是不确定的,所以在使用浮空输入时,端口---定要接上一个连续的驱动源,不能出现悬空的状态。
那我们来看一下这三种模式的电路结构,这里可以看到,在输入模式下,输出驱动器是断开的,端口只能输入而不能输出,上面这两个电阻可以选择为上拉工作、下拉工作或者都不工作,对应的就是上拉输入、下拉输入和浮空输入,然后输入通过施密特触发器进行波形整形后,连接到输入数据寄存器。
另外右边这个输入保护这里,上面写的是VDD或者VDD_FT,这就是3.3V端口和容忍5V端口的区别。这个容忍5V的引脚,它的上边保护二极管要做一下处理,要不然这里直接接VDD 3.3V的话,外部再接入5V电压就会导致上边二极管开启,并且产生比较大的电流,这个是不太妥当的。
接着我们再来看一下下面这一个模拟输入,特征是GPIO无效,引脚直接接入内部ADC,这个模拟输入可以说是ADC模数转换器的专属配置了。

这里输出是断开的,输入的施密特触发器也是关闭的无效状态,所以整个GPIO的这些都是没用的,那么只剩下从引脚直接接入片上外设,也就是ADC,所以,当我们使用ADC的时候,将引脚配置为模拟输入就行了,其他时候,一般用不到模拟输入。
3.2 输出模式

模式 | 控制源 | 特点 |
---|---|---|
推挽输出 | 数据寄存器 | 双向强驱动 |
开漏输出 | 数据寄存器 | 需外接上拉 |
复用推挽/开漏 | 片上外设 | 配合UART/SPI等外设使用 |

开漏输出 和推挽输出 ,这两个电路结构也基本一样,都是数字输出端口,可以用于输出高低电平,区别就是开漏输出的高电平呈现的是高阻态,没有驱动能力,而推挽输出的高低电平都是具有驱动能力的。 这时候,输出是由输出数据寄存器控制的,如果P-MOS无效,就是开漏输出;如果P-MOS和N-MOS都有效,就是推挽输出。另外我们还可以看到,在输出模式下,输入模式也是有效的,但是在我们刚才的电路图,在所有输入模式下,输出都是无效的,这是因为,一个端口只能有一个输出,但可以有多个输入,所以当配置成输出模式的时候,内部也可以顺便输入一下,这个也是没啥影响的。
最后我们再来看一下复用开漏输出 和复用推挽输出 ,这俩模式跟普通的开漏输出和推挽输出也差不多。
可以看到通用的输出/数据寄存器没有连接的,引脚的控制权转移到了片上外设,由片上外设来控制,在输入部分,片上外设也可以读取引脚的电平,同时普通的输入也是有效的,顺便接收一下电平信号其实在GPIO的这8种模式中,除了模拟输入这个模式会关闭数字的输入功能,在其他的7个模式中,所有的输入都是有效的。
四、寄存器配置精要
4.1 核心寄存器列表
GPIO寄存器讲解23:45~26:32
4.2 配置技巧


那这高电平驱动和低电平驱动两种驱动方式应该如何选择呢?
这就得看这个I0口高低电平的驱动能力如何了,我们刚才介绍,这个GPIO在推挽输出模式下,高低电平均有比较强的驱动能力,所以在这里,这两种接法均可。但是在单片机的电路里,一般倾向使用第一种接法(低电平驱动),因为很多单片机或者芯片,都使用了高电平弱驱动,低电平的强驱动的规则,这样可以一定程度上避免高低电平打架。所以如果高电平驱动能力弱,那就不能使用第一种连接方法了
本节内容跟手册第8章的GPIO相关,AFIO暂时不用管。
五、实战应用案例
操作STM32的GPIO总共需要3个步骤:
- 第一步,使用RCC开启GPIO的时钟
涉及的函数如下:
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
作用:使能(开启)或失能(关闭)APB2外设时钟
参数说明:
参数 | 说明 |
---|---|
RCC_APB2Periph | 门控 APB2 外设时钟:指明需要开启的是哪一个APB2外设,取值范围在下图表明 |
NewState | NewState:指定外设时钟的新状态 ,这个参数可以取:ENABLE(打开) 或者 DISABLE(关闭) |

其它两个外设时钟函数也是大差不差的,根据不同外设选择相应的函数开启就行。
- 第二步,使用GPIO_Init函数初始化GPIO
涉及的函数如下:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
作用:根据GPIO_InitStruct中的指定参数初始化GPIOx外设。
参数说明:
参数 | 说明 |
---|---|
GPIOx | 其中x可以为(A...G)选择GPIO外设。 |
GPIO_InitStruct | 指向GPIO InitTypeDef结构的指针,该结构包含指定GPIO外设的配置信息。 |
指定要配置的GPIO引脚。
其中 GPIO InitTypeDef结构体配置信息如下:
c
typedef struct
{
uint16_t GPIO_Pin;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
参数说明:
参数 | 说明 |
---|---|
GPIO_Pin | 指定要配置的GPIO引脚。例如:GPIO_Pin_14 |
GPIO_Speed | 指定所选引脚的速率。在GPIO_Speed_10MHz,GPIO_Speed_2MHz,GPIO_Speed_50MHz中选择,库里已经定义好了 |
GPIO_Mode | 指定所选引脚的工作模式。 |
引脚的工作模式如下:
举例:根据LED闪烁接线图设置
c
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
第三步,使用输出或者输入的函数控制GPIO口
涉及的函数如下:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
作用:设置所选数据端口位。对某个端口写1,也就是高电平
参数说明:
参数 | 说明 |
---|---|
GPIOx | 其中x可以为(A...G)选择GPIO外设。 |
GPIO_Pin | 指定要写入的端口位,该参数可以是GEIo_Pin_x的任意组合,其中x可以是(0...15)。 |
类似的还有:GPIO_ResetBits 函数,同样的用法,只不过这个函数是写0
5.1 LED控制
接线图:
5.2 LED流水灯


5.3 蜂鸣器驱动