STM32 GPIO(IO口)通用输入输出端口详解

1.GPIO口的概念

1.1概念

IO口:通用输入输出端口,通过软件控制其输入输出,STM32芯片的GPIO引脚与外部设备连接起来,从而可以实现与外部的通信,控制以及数据采集的功能;

输出模式下,可控制端口输出高低电平,用于驱动LED,蜂鸣器,模拟通信协议输出时序等;

当控制功率较大的设备可以加入驱动电路;

输入模式下,可读取端口的高低电平或电压,用于读取按键输入,外接模块电平信号输入,ADC电压采集,模块通信协议接收数据等;

每个IO口有俩个32位的配置寄存器(CPIO_CRL低位,GPIO_CRH高位),俩个数据寄存器,一个位置位/复位寄存器,一个16位的复位寄存器,一个32位的锁定寄存器;

*GPIO每一个端口模式由两位进行配置,16个端口就是64位,所以有俩个32位的;

分为低位和高位,

*数据寄存器32位,低位(前16位)配置,高位(后16位无用);

*位置位/复位寄存器,低位配置,高位清除,通过配置1/0来操作;

*32位的锁定寄存器,对端口配置进行锁定,防止误操作;

二、I/O用法

I/O端口特定的硬件特征,GPIO每个位可以由软件分别配置成8种模式;

模式分别为:浮空输入,上拉输入,下拉输入,模拟输入,开漏输出,推挽输出,开漏输出复用功能,推挽输出复用功能;

输入输出模式输出只能是3.3V,引脚电平:0~3.3V,部分引脚可以容忍5V(只能是输入,输出最大是3.3V),容忍5V的使用FT标识;

三、I/O口的基本结构

所由GPIO都是挂接在APB2总线上的;

寄存器就是一段特殊的存储器,内核通过APB2总线对寄存器进行读写,可以完成输出电平和读取电平的功能;

输入寄存器读取为1,就证明是高电平,读取为0则是低电平;

寄存器每一位对于一个引脚,输出寄存器写1,对于引脚是高电平,0为低电平;

GPIO分为A-G多种类型,例如GPIOA,每一组含有PX0-PX15,16个引脚,每个引脚占4位,所以总共是64位;

I/O分为寄存器,驱动器,某一I/O引脚三部分,如图所示;

*最右端的I/O是STM32芯片的引脚,其他部分都在STM32芯片内部;

I/O分为输入,输出两部分,如下图;

3.1输入部分

3.1.1保护二极管

组成:保护二极管+VSS+VDD;

功能:保护电路,对输入的电压进行限幅,控制输入内部电路的电压在0~3.3V;

原理:如果输入电压大于3.3V,VDD连接的保护二极管将会被导通,此时输入电压产生的电流就会流入VDD,而不会流入内部电路,防止内部电路被破坏;如果输入电压小于0V,这里指的是相对于VSS的电压,所以可以有负电压,下面的二极管就会被导通,此时输入电压产生的电流就会流入VSS,防止从内部电路汲取电流,也可以保护内部电路;

3.1.2浮空输入,上拉输入,下拉输入

组成:上拉电阻VDD,下拉电阻VSS,开关;

功能:作为数字信号输入,可读取引脚电平,为给输入提供一个默认的引脚输入电平

原理:对于一个数字的端口,输入不是高电平就是低电平,如果输入引脚什么都不接,输入处于一种浮空状态,引脚的输入电平极易收到外界的干扰而改变;如果悬空,上拉为高电平,下拉为低电平,浮空不确定(波动会带来很大的影响所以不悬空);

*STM32内部的上拉其实上一个弱上拉,也就是说通过此上拉电阻输出的电流很小,如果想要输出一个大电流,那么就需要外接上拉电阻;

使用方法:通过程序进行配置;

如果VDD连接的开关闭合,此时上面接通下面断开,位上拉输入模式;

如果VSS连接的开关闭合,此时下面接通上面断开,为下拉输入模式;

如果俩个开关都不闭合,上下均断开,此时为浮空输入模式;

1.3.1.3TTL触发器

组成:TTL触发器

作用:对输入的电压进行整型;

原理:如果输入的电压大于某一阈值,输出就会瞬间升为高电平,如果输入的电压小于某一阈值则会瞬间将为低电平;

输入的是数字信号,但是实际情况可能出现各种失真,如果没有整型就会导致误判;

红色的为输入的一段数字信号波形;

绿色为阈值;

紫色为整形后的波形;

使用俩个阈值来进行判断,中间留有一定的变化范围,可以有效的避免因为波动而导致的输出抖动现象;

经过触发器整形后的波形,就可以写入输入数据寄存器了,再用程序读取输入数据寄存器对应某一位的数据就可以知道端口的电平了;

3.1.3模拟输入

原理:模拟输入,GPIO特征无效,引脚直接接入内部ADC,可以说是模数转换器ADC的专属配置了;

使用:由引脚接入ADC,使用ADC时选择模拟输入;

此时触发器和输出是关闭的;

*因为ADC需要接收模拟量所以接在触发器前;

3.1.4复用功能

功能:连接其他需要读取的外设端口上,例如串口的输入引脚,接收的数字,所以在触发器之后;

3.1.5输入总详情

在输入的时候,输出端口是断开的;

3.2输出部分

3.2.1输出数据寄存器

数字部分可以由输出数据寄存器或者片上外设控制

两种控制方式通过数据选择器接到了输出控制部分;

如果通过输出数据寄存器,就是普通的I/O口控制,写这个数据寄存器的某一位就可以操作对应的某个端口了;

3.2.2位设置/清除寄存器

可以单独操作寄存器的某一位,而不影响其他位,因为输出数据寄存器同时控制16个端口,并且寄存器只能整体读写,所以如果想要单独控制某一个端口而不影响其他端口,需要特殊的操作方式;第一种方法就是先读取这个寄存器的,然后用按位与或者按位或的方式更改某一位,然后再把数据写回去,在C语言中就是&=和|=的操作;第二种方法就是通过位设置/清除寄存器单独操作寄存器的某一位,如果要对某一位置1,在设置寄存器的对应位写1即可;第三种方法读写 STM32的位带区域;

3.2.3MOS管

是一个电子开关,通过信号来控制开关的导通和关闭;

线路经过P-MOS,N-MOS管组成的单元电路,让GPIO引脚具有推挽和开漏两种输出模式;

*推挽输出模式也根据P-MOS,N-MOS的工作方式来命名的;

当单位电路,输入一个高电平的时候,P-MOS管导通,N-MOS管截至,对外输出高电平(3.3V);

当输入一个低电平的时候,P-MOS管截至,N-MOS管导通,对外输出低电平(0V);

如果当切换输入高低电平时,俩个MOS管轮流导通,一个负责灌电流(电流输出到负载),一个负责拉电流(负责电流流向芯片),使其负载能力和开关速度都比普通方式有很大的提高;

3.2.4开漏输出

开漏模式下,P-MOS管总是处于关闭状态;

当给这个单元电路输入低电平时(数据寄存器位0),N-MOS管导通,输出为低电平(具有低电平驱动能力);

当给这个单元电路输入高电平时(数据寄存器位1),P-MOS,N-MOS管都截至,为高阻状态,此时输出相当于断开;

这种开漏模式通常作为通信协议的驱动方式;

多机通信模式下,开漏模式,可以避免各个设备的相互干扰,开漏模式还可以用于输出5V的电平信号,用于兼容5V的外设;

当输入高电平时(数据寄存器位1),为高阻状态,输出视为断开,输入关闭,端口的电平由外部信号来控制,此时保护二极管,外接上拉电阻到5V的电源即可;

3.2.5推挽输出

推挽输出下,P-MOS,N-MOS均有效;

当给这个单元电路输入低电平时(数据寄存器位0),P-MOS管截止,N-MOS管导通,输出为低电平(3.3V);

当给这个单元电路输入高电平时(数据寄存器位1),P-MOS管导通,N-MOS管截止,对外输出高电平(0V);

这种模式下,高低电平均有驱动能力,所以推挽模式也叫强推输出模式;在推挽模式下,STM32对I/O有绝对控制权;

3.2.6功能复用

在功能复用时,通用的输出是没有连接的,其他基本与开漏和推挽模式相同;

引脚的控制权转移到了片上外设,由片上外设来控制;

3.2.7输出总详情

四、GPIO相关的API

首先想要控制某个外设就要先开启对应的时钟,在完成复位程序后;

4.1开启对应的时钟

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

第一个参数是选择外设为APB2,第二个是开启时钟ENABLE,或者DISANBLE关闭

下面打开gpio.h控制GPIO的所有函数如下;

void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

主要介绍以下部分;

void GPIO_DeInit(GPIO_TypeDef* GPIOx);

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

void GPIO_DeInit(GPIO_TypeDef* GPIOx);

4.2复位所指定的GPIOX端口

4.3初始化GPIO

参数:第一个参数为选择哪一个GPIO组,第二个为定义结构体变量配置参数;

用结构体变量来初始化GPIO,使用时需要先定义一个结构体变量,然后赋值,最后调用;

函数调用结构体变量,会根据结构体变量的成员自动配置参数;

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

4.4GPIO结构体变量赋一个默认的值,设置结构体变量的成员;

GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);

void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct)
{
  /* Reset GPIO init structure parameters values */
  GPIO_InitStruct->GPIO_Pin  = GPIO_Pin_All;
  GPIO_InitStruct->GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStruct->GPIO_Mode = GPIO_Mode_IN_FLOATING;
}

成员分别为:选择具体哪一个引脚

  GPIO_InitStruct->GPIO_Pin  = GPIO_Pin_All;

#define GPIO_Pin_0                 ((uint16_t)0x0001)  /*!< Pin 0 selected */
#define GPIO_Pin_1                 ((uint16_t)0x0002)  /*!< Pin 1 selected */
#define GPIO_Pin_2                 ((uint16_t)0x0004)  /*!< Pin 2 selected */
#define GPIO_Pin_3                 ((uint16_t)0x0008)  /*!< Pin 3 selected */
#define GPIO_Pin_4                 ((uint16_t)0x0010)  /*!< Pin 4 selected */
#define GPIO_Pin_5                 ((uint16_t)0x0020)  /*!< Pin 5 selected */
#define GPIO_Pin_6                 ((uint16_t)0x0040)  /*!< Pin 6 selected */
#define GPIO_Pin_7                 ((uint16_t)0x0080)  /*!< Pin 7 selected */
#define GPIO_Pin_8                 ((uint16_t)0x0100)  /*!< Pin 8 selected */
#define GPIO_Pin_9                 ((uint16_t)0x0200)  /*!< Pin 9 selected */
#define GPIO_Pin_10                ((uint16_t)0x0400)  /*!< Pin 10 selected */
#define GPIO_Pin_11                ((uint16_t)0x0800)  /*!< Pin 11 selected */
#define GPIO_Pin_12                ((uint16_t)0x1000)  /*!< Pin 12 selected */
#define GPIO_Pin_13                ((uint16_t)0x2000)  /*!< Pin 13 selected */
#define GPIO_Pin_14                ((uint16_t)0x4000)  /*!< Pin 14 selected */
#define GPIO_Pin_15                ((uint16_t)0x8000)  /*!< Pin 15 selected */
#define GPIO_Pin_All               ((uint16_t)0xFFFF)  /*!< All pins selected */

选择频率(主要影响功耗等)定义了一个枚举形式

 GPIO_InitStruct->GPIO_Speed = GPIO_Speed_2MHz;
typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
#define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || ((SPEED) == GPIO_Speed_2MHz) || \
                              ((SPEED) == GPIO_Speed_50MHz))

选择模式定义了一个枚举形式

 GPIO_InitStruct->GPIO_Mode = GPIO_Mode_IN_FLOATING;

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;

4.5GPIO读取函数

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

4.6GPIO自卫函数,把端口电平设置成高电平或者低电平

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

4.7写入函数

void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);

第一个参数是选择哪一个类型的GPIO口,第二个参数是选择哪一个引脚,第三个参数  指定要写入选定位的值。该参数可以是 BitAction 枚举值之一:
  * @arg Bit_RESET:清除端口引脚
  * @arg Bit_SET:设置端口引脚

也可以使用强制转换(BitAction )1,(BitAction)0.

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

第一个参数是选择哪一个类型的GPIO口,第二个参数指定要写入端口输出数据寄存器的值。

五、实例

加入断点进行调试

编译无问题后;

六、软件调试

进行复位

打开外设列表选择GPIOC

运行

刚好对应的是13位

后续会添加实例操作;

作者:开心的龙

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 GPIO(IO口)通用输入输出端口详解

发表评论