2、GPIO通用输入输出口

一、GPIO简介

  • GPIO ( General Purpose Input Output )通用输入输出口
  • 可配置为 8 种输入输出模式
  • 引脚电平: 0V~3.3V ,部分引脚可容忍 5V
  • 输出模式下可控制端口输出高低电平,用以驱动 LED 、控制蜂鸣器、模拟通信协议输出时序等
  • 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、 ADC 电压采集、模拟通信协议接收数据等

img

二、STM32 GPIO模式:

当使用STM32系列微控制器的GPIO引脚时,您可以根据您的应用需求选择不同的GPIO模式。以下是对每个模式的更详细介绍:

img

  1. 输入模式

    • 浮空输入(Floating Input):引脚未连接到任何电路。可以用于读取外部电平,但应注意,由于没有上拉或下拉电阻,引脚可能会受到噪声干扰。
    • 上拉输入(Input Pull-Up):引脚通过内部上拉电阻连接到高电平。在没有外部连接时,引脚会保持高电平。通常用于按钮和开关的输入,以防止悬空电平。
    • 下拉输入(Input Pull-Down):引脚通过内部下拉电阻连接到低电平。在没有外部连接时,引脚会保持低电平。与上拉输入类似,通常用于按钮和开关的输入。
  2. 输出模式

    • 通用推挽输出(General-Purpose Push-Pull Output):引脚可以输出高电平或低电平。用于驱动外部数字电路。
    • 开漏输出(Open-Drain Output):引脚只能拉低,而不能拉高。通常需要外部上拉电阻来拉高电平。用于实现开漏输出,适用于一些总线通信协议如I2C和SPI。
    • 复用开漏输出(Open-Drain Output with Alternate Function):类似于开漏输出,但引脚还可以配置为执行特定的备用功能,如UART通信、PWM输出等。
  3. 复用模式
    • 引脚可以配置为具有不同备用功能的引脚。这些备用功能可以通过相应的寄存器设置来选择,以满足特定的通信或控制需求。
  4. 模拟模式
    • GPIO引脚可以配置为模拟输入或模拟输出。模拟输入用于连接到模拟传感器或其他模拟电路,而模拟输出用于输出模拟信号。
  5. 中断模式
    • GPIO引脚可以配置为触发外部中断。当引脚状态变化时,可以触发中断服务程序,允许您在特定事件发生时立即响应。
  6. 高速模式
    • 高速模式允许GPIO引脚以更高的时钟频率工作,以适应特定的应用需求。这对于需要快速切换引脚状态的应用非常有用。
  7. 低功耗模式
    • 某些STM32系列支持低功耗模式,可以降低GPIO引脚的功耗,适用于需要长时间运行的电池供电应用。

引脚电平:0V~3.3V,部分引脚可容忍5V

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Analog IN 模拟输入,这些引脚通常用于连接到模拟传感器或其他模拟电路。
GPIO_Mode_AIN = 0x0,
//In_Floating 浮空输入,这意味着引脚未连接到任何电路,可以读取外部电平,但可能受到噪声干扰。
GPIO_Mode_IN_FLOATING = 0x04,
//In Pull Down 下拉输入,引脚通过内部下拉电阻连接到低电平,在没有外部连接时,引脚会保持低电平。
GPIO_Mode_IPD = 0x28,
//In Pull Up 上拉输入,引脚通过内部上拉电阻连接到高电平,在没有外部连接时,引脚会保持高电平。
GPIO_Mode_IPU = 0x48,
//Out Open Drain 开漏输出,高电平没有驱动能力,在这种模式下,引脚可以拉低,但不能拉高,通常需要外部上拉电阻来拉高电平。
GPIO_Mode_Out_OD = 0x14,
//Out Push Pull 推挽输出,高低电平都有驱动能力,在这种模式下,引脚可以输出高电平或低电平,具有驱动能力,用于驱动外部数字电路。
GPIO_Mode_Out_PP = 0x10,
//Atl Open Drain 复用开漏,类似于开漏输出,但引脚还可以配置为执行特定的备用功能,如UART通信、I2C通信等。
GPIO_Mode_AF_OD = 0x1C,
//Atl Push Pull 复用推挽,类似于推挽输出,但引脚也可以配置为执行备用功能。
GPIO_Mode_AF_PP = 0x18

三、GPIO常用寄存器

(1)端口配置低\高寄存器(GPIOx_CRL/GPIOx_CRH)(x=A…E)

  • 端口配置寄存器共16位,但每4位数据表示1位,共需要64位,而STM32中每个寄存器都为32位,因此分为端口配置低寄存器和端口配置高寄存器。通过端口配置寄存器可以配置GPIO工作模式与端口输出速度。

  • 注意:输出速度可以限制输出引脚的最大翻转速度,作用是降低功耗、提高稳定性,一般情况下配置为50MHz。

(2)端口输入数据寄存器(GPIOx_IDR)(x=A…E)

  • 输入数据共16位,但寄存器共32位,因此寄存器高16位为空。

(3)端口输出数据寄存器(GPIOx_ODR)(x=A…E)

  • 输出数据共16位,但寄存器共32位,因此寄存器高16位为空。

(4)端口位设置/清除寄存器(GPIOx_BERR)(x=A…E)

  • 高16位用于位清除,低16位用于位设置。
  • 高16位:为0不影响;为1清0;
  • 低16为:为0不影响;为1置1。

(5)端口位清除寄存器(GPIOx_BER)(x=A…E)

  • 高16位为空,低16位用于清除,方法同上。

(6)端口位配置锁定寄存器(GPIOx_LCKR)(x=A…E)

  • 高15位为空,低17位用于锁定,较少使用。

四、操作STM32的GPIO步骤

1、使用RCC开启GPIO的时钟

1
2
3
4
//第一个参数选择外设,第二个参数使能或失能
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);

AHB外设时钟控制函数就是使能或者失能AHB外设时钟的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* @brief Enables or disables the AHB peripheral clock.
使能或失能AHB外设时钟
* @param RCC_AHBPeriph: specifies the AHB peripheral to gates its clock.
* 第一个参数选择哪一个外设
* For @b STM32_Connectivity_line_devices, this parameter can be any combination
* of the following values:
stm32互联型的设备可以在这个列表选择
* @arg RCC_AHBPeriph_DMA1
* @arg RCC_AHBPeriph_DMA2
* @arg RCC_AHBPeriph_SRAM
* @arg RCC_AHBPeriph_FLITF
* @arg RCC_AHBPeriph_CRC
* @arg RCC_AHBPeriph_OTG_FS
* @arg RCC_AHBPeriph_ETH_MAC
* @arg RCC_AHBPeriph_ETH_MAC_Tx
* @arg RCC_AHBPeriph_ETH_MAC_Rx
*
* For @b other_STM32_devices, this parameter can be any combination of the
* following values:

* @arg RCC_AHBPeriph_DMA1
* @arg RCC_AHBPeriph_DMA2
* @arg RCC_AHBPeriph_SRAM
* @arg RCC_AHBPeriph_FLITF
* @arg RCC_AHBPeriph_CRC
* @arg RCC_AHBPeriph_FSMC
* @arg RCC_AHBPeriph_SDIO
*
* @note SRAM and FLITF clock can be disabled only during sleep make
注意SRAM和FLITF时钟只能在睡眠时禁用
* @param NewState: new state of the specified peripheral clock.
指定外设时钟的新状态。
* This parameter can be: ENABLE or DISABLE.
取值为:ENABLE或DISABLE。
* @retval None
*/
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_RCC_AHB_PERIPH(RCC_AHBPeriph));
assert_param(IS_FUNCTIONAL_STATE(NewState));

if (NewState != DISABLE)
{
RCC->AHBENR |= RCC_AHBPeriph;
}
else
{
RCC->AHBENR &= ~RCC_AHBPeriph;
}
}

2、使用GPIO_Init函数初始化GPIO

1
2
3
4
5
6
7
8
9
10
11
//该函数用于将指定的GPIO外设复位为其默认状态。
//参数 GPIOx 是一个指向 GPIO_TypeDef 结构的指针,用于指定要复位的GPIO外设,例如 GPIOA、GPIOB等。
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
//该函数用于将AFIO (Alternate Function I/0) 外设复位为其认状态。AFIO设用于配器GPIO引脚的复用功能。
void GPIO_AFIODeInit(void);
//该函数用于通过结构体参数初始化指定GPIO外设的GPIO引脚。
//参数 GPIOx 是一个指向 GPIO_TypeDef 结构的指针,用于指定要配置的GPIO外设
//参数 GPIO Initstruct 是一个结构体指针,包含有关要配置的GPIO引脚的详细信息,例如引脚的模式速度、上下拉等。
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//该函数用于将传递的结构体初始化为默认值,以便稍后通过 GPIO_Init 函数进行配置//参数 GPIO_Initstruct 是一个结构体指针,需要在调用该函数前进行定义,并将其传递给 GPIO_Init函数。
void pIO structInit(GPIO InitTypeDef* GPIO Initstruct);//该函数用于读取指

3、使用输出或输入的函数控制GPIO

3.1、GPIO输出函数
1
2
3
4
5
6
7
8
9
//GPIO的写入输出函数
//该函数用于将指定GPIO外设的一个或多个引脚设置为高电平。
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);
//该函数用于根据第二个参数的值来设置指定GPIO外设的所有引脚的状态(高或低)。
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
3.2 输出函数控制GPIO示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//这行代码启用了GPIOA外设的时钟。在STM32中,要使用GPIO端口,首先需要启用相应的外设时钟。
GPIO_InitTypeDef GPIO_InitStructure;
//这行代码定义了一个名为`GPIO_InitStructure`的结构体变量,用于配置GPIO端口的初始化参数
/*!typedef struct{ //typedef更改结构体的名字为GPIO_InitStructure
uint16_t GPIO_Pin;
//指定要配置的GPIO引脚。
该参数可以是@ref GPIO_pins_define的任意值
GPIOSpeed_TypeDef GPIO_Speed;
//指定所选引脚的速度。
该参数可以是@ref GPIOSpeed_TypeDef的值
GPIOMode_TypeDef GPIO_Mode;
//指定所选引脚的工作模式。
该参数可以是@ref GPIOMode_TypeDef的值
}GPIO_InitTypeDef; */
//.引出结构体成员
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//这行代码配置了GPIO的工作模式。`GPIO_Mode_Out_PP` 表示GPIO端口将被配置为推挽输出模式,即可以输出高电平和低
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
//这行代码指定了要初始化的GPIO引脚。在这里,GPIOA的引脚1和2被初始化。
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//这行代码配置了GPIO的输出速度。在这里,GPIO的输出速度被设置为50MHz。
GPIO_Init(GPIOA, &GPIO_InitStructure);
//这行代码使用前面配置好的参数初始化GPIOA端口。它告诉STM32使用`GPIO_InitStructure`中定义的参数来配置GPIOA端口。
GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);
//这行代码将GPIOA的引脚1和2设置为高电平,从而点亮LED。`GPIO_SetBits` 函数用于设置指定引脚的电平。
}
3.3、GPIO读取函数
1
2
3
4
5
6
7
8
9
//GPIO的读取输入函数
//该函数用于读取指定GPIO外设的单个引脚的输入状态,返回引脚的状态(高电平或低电平)
uint8_t GPIO_ReadInputDataBit(GpIO_TypeDef* GPIOx, uint16 t GpIO Pin);
//该函数用于读取指定GPIO外设的所有引脚的输入状态,返回整个GPIO端口的状态值。
uint16 t GPIO ReadInputData(GPIO TypeDef* GPIOx);
//该函数用于读取指定GPIO外设的单个引脚的输出状态,返回引脚的状态(高电平或低电平)
uint8 t GPIO ReadOutputDataBit(GpIO TypeDef* GPIOx, uint16 t GpIO pin);
//该函数用于读取指定GPIO外设的所有引脚的输出状态,返回整个GPIO端口的状态值。
uint16 t GPIO ReadoutputData(GpIO TypeDef* GPIOx)
3.4 输入函数控制GPIO示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void Key_Init(void)
{
// 启用GPIOB外设的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 定义GPIO初始化参数结构体
GPIO_InitTypeDef GPIO_InitStructure;
// 配置GPIO工作模式为上拉输入模式(Input Pull-Up)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
// 配置GPIO引脚,这里配置引脚1和引脚11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
// 配置GPIO输出速度为50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 使用上述配置初始化GPIOB端口
GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
// 检测引脚1是否低电平(按钮按下)
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
// 等待20毫秒(消除按键抖动)
Delay_ms(20);
// 检测引脚1是否仍然低电平(按钮保持按下)
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
// 再次等待20毫秒(避免多次检测到按钮按下)
Delay_ms(20);
// 设置按键号为1
KeyNum = 1;
}

// 检测引脚11是否低电平(按钮按下)
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
{
// 等待20毫秒(消除按键抖动)
Delay_ms(20);
// 检测引脚11是否仍然低电平(按钮保持按下)
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
// 再次等待20毫秒(避免多次检测到按钮按下)
Delay_ms(20);
// 设置按键号为2
KeyNum = 2;
}
// 返回检测到的按键号(0表示没有按键按下)
return KeyNum;
}

1、A15、B3、B4三个端口默认是JTAG调试端口,如果要当作普通端口,要进行一些相关的配置

2、有源蜂鸣器:内部自带振荡器,频率固定

无源蜂鸣器:不带振荡器,要提供震荡脉冲才能发生,可以发出不同频率的声音

3、推挽输出高低电平都有驱动能力

开漏输出高电平相当于高阻态,没有驱动能力,低电平有驱动能力