【STM32F4】GPIO详解与配置教程
-
GPIO简介
GPIO是通用输入输出端口的简称,为STM32可控制的引脚,STM32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32芯片的GPIO被分成很多组,每组有16个引脚,所有的GPIO引脚都有基本的输入输出功能。
-
GPIO内部构图
GPIO口可容忍电压
其中保护二极管使GPIO能够5V电压容忍。在手册中可以找到GPIO口图中标注FT的GPIO口为可容忍5V电压输入GPIO口。
引脚的两个保护二极管可以防止引脚外部过高或过低的电压输入,当引脚电压高VDD_FT(5V)于时,上方的二极管导通,当引脚电压低于Vss时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。
上下拉电阻
概述
上拉电阻:将一个不确定的信号(高或低电平),通过一个电阻与电源VCC相连,固定在高电平。
下拉电阻:将一个不确定的信号(高或低电平),通过一个电阻与地GND相连,固定在低电平。
在内部构图中可以看出通过上、下拉电阻的开关配置,我们可以控制引脚默认状态的电压,开启上拉的时候引脚电压为高电平,开启下拉的时候引脚电压为低电平。
GPIO口输入工作模式一.浮空输入模式
VDD和VSS所在路径的两个开关同时断开。此时没有上拉和下拉的情况,所以当IO口没有接输入的时候,此时的电平会是一个不确定的值,也就是我们所说的浮空。电平会处于一个跳变的状态,一会高,一会低。只有输入了一个高/低电平才会确定下来。设置‘无上拉或下拉’(浮空模式)模式时,直接用电压表测量其引脚电压跳变,这是个不确定的值。所以一般来说我们都会选择给引脚设置“上拉模式”或“下拉模式”使它有默认状态。STM32的内部上拉时“弱上拉”,即通过上拉输出的电流时很弱的,如要求大电流还是需要外部上拉,通过“上拉/下拉寄存器GPIOx_PUPDR”控制引脚的上、下拉及浮空模式。
优势:这一种输入模式的电平会完全取决于外部电路而与内部电路无关。有时候会用作对开关按键的读取。
缺点:但是在没有外部电路接入的时候,IO脚浮空会使得电平不确定,会使MCU进行误操作。
GPIO口输入工作模式二.上拉输入模式
VDD所在上拉电阻开关闭合,下拉电阻的开关断开。根据前面浮空输入里面所提的,在没有信号输入的时候,此时的电平就是VDD的电平,此时读取到的电平就是高电平。
当输入信号是一个低电平(<1.166v)的时候,电压就会被拉低变成低电平,那么VDD和输入电压之间形成了电势差,但是因为上拉电阻的存在,所以不会出现一个大电流。此时单片机读取到的一个电平就是一个低电平。在上拉输入的情况下,低电平的是能够非常明显的读取到的。
优势:输入的电平不会上下浮动而导致输入信号不稳定,在没有信号输入的情况下可以稳定在高电平。
缺点:如果输入了一个高电平,VDD和输入电压就没有电势差,此时读取到的电平就仍然是高电平。这一种输入情况下是没有办法确定信号是否输入了。
GPIO口输入工作模式三.下拉输入模式
VDD所在上拉电阻开关断开,下拉电阻的开关闭合。电平就是VSS的电平,此时读取到的电平就是低电平。如果输入的是一个高电平,输入电压和VSS之间同样形成了电势差,输入的电平会变成外部的高电平,那么单片机得到的就是一个高电平信号。
优势:下拉输入的好处就是输入的电平不会上下浮动而导致输入信号不稳定,在没有信号输入的情况下可以稳定在低电平。
缺点:此时输入的电平如果是一个低电平,就没有办法和之前的情况进行区分。
GPIO口输入工作模式四.模拟输入模式
概述:模拟量输入(Analog Input,简称AI)是指输入为连续变化的物理量(电压)
在STM32F4中,我们有时候需要用AD采集到IO口上面的真实电压。需要用到模拟输入。为了让MCU读取到真实的外部的电压,我们既不能闭合上拉和下拉的开关,也不能让电压信号经过施密特触发器。
GPIO口输出工作模式一.开漏输出模式
概述:开漏输出就是不输出电压,控制输出低电平时引脚接地,控制输出高电平时引脚既不输出高电平,也不输出低电平,为高阻态。如果外接上拉电阻,则在输出高电平时电压会拉到上拉电阻的电源电压。
优势:开漏输出的实质其实就是一个OD门(OD:漏极输出(Open Drain))。而在数电中,OD门有一个非常重要的特性就是可以实现线与的功能,简单来说,就是在像IIC这样的总线协议中,只要有一个给低电平,那么总线都会被拉低。
GPIO口输出工作模式二.开漏复用输出模式
GPIO口输出工作模式三.推挽输出模式
推挽输出就是可以需要利用两个不同的MOS管来实现输出。P-MOS和N-MOS是不同的控制方式,当给一个高电平的时候,N-MOS导通,P-MOS不导通,此时IO口接通在VSS,此时输出的是低电平。当给一个低电平的时候,P-MOS导通,N-MOS导通,此时IO口接通在VDD电源上面,此时输出的是高电平。
GPIO口输出工作模式四.复用推挽输出模式
上电复位
上电复位后,GPIO默认为浮空状态,部分特殊功能引脚为特定状态。
GPIO口寄存器
每组GPIO端口的寄存器包括:
一个端口模式寄存器(GPIOx_MODER)
一个端口输出类型寄存器(GPIOx_OTYPER)
一个端口输出速度寄存器(GPIOx_OSPEEDR)
一个端口上拉下拉寄存器(GPIOx_PUPDR)
一个端口输入数据寄存器(GPIOx_IDR)
一个端口输出数据寄存器(GPIOx_ODR)
一个端口置位/复位寄存器(GPIOx_BSRR)
一个端口配置锁存寄存器(GPIOx_LCKR)
两个复位功能寄存器(低位GPIOx_AFRL & GPIOx_AFRH)
端口模式寄存器(GPIOx_MODER)
端口输出类型寄存器(GPIOx_OTYPER)
端口输出速度寄存器(GPIOx_OSPEEDR)
端口上拉/下拉寄存器(GPIOx_PUPDR)
端口输入数据寄存器(GPIOx_IDR)
端口置位/复位寄存器(GPIOx_BSRR)
基础知识
4种输入模式:
输入浮空
输入上拉
输入下拉
模拟输入
4种输出模式:
开漏输出(上拉或者下拉)
开漏复用功能(上拉或者下拉)
推挽式输出(上拉或者下拉)
推挽式复用功能(上拉或者下拉)
4种最大输出速度:
-2MHZ
-25MHz
-50MHz
-100MHz
库函数介绍
1个初始化函数:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
2个读取输入电平函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //读取当前GPIO口的电平状态
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx); //读取当前GPIO口组的所有IO口电平状态
2个读取输出电平函数:
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //读取当前设置GPIO口输出电平为何状态
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx); //读取当前GPIO口组所有IO口输出状态
4个设置输出电平函数:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //设置单个GPIO口输出高电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //设置单个GPIO口输出低电平
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal); //不常用
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); //不常用
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);//作用:初始化一个或者多个IO口(同一组)的工作模式,输出类型,速度以及上下拉方式。也就是一组IO口的4个配置寄存器。
//
//typedef struct
//{
// uint32_t GPIO_Pin//指定要初始化的端口
// GPIOMode_TypeDef GPIO_Mode;//端口模式
// GPIOSpeed_TypeDef GPIO_Speed;//速度
// GPIOOType_TypeDef GPIO_OType; //输出类型
// GPIOPuPd_TypeDef GPIO_PuPd;//上拉或者下拉
//}GPIO_InitTypeDef;
函数编写示例
#include "stm32f4xx.h"
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOF,&GPIO_InitStructure);
GPIO_SetBits(GPIOF,GPIO_Pin_9);
GPIO_SetBits(GPIOF,GPIO_Pin_10);
}