函数解释
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
- 作用:将指定GPIO端口的所有寄存器恢复为默认值。这会清除之前对该端口的所有配置,使其回到初始状态。
- 使用方法:传入要复位的GPIO端口指针,例如
GPIOA
、GPIOB
等。
void GPIO_AFIODeInit(void);
- 作用:将复用功能I/O(AFIO)的寄存器恢复为默认值。复用功能I/O用于将一些外设功能映射到特定的GPIO引脚上,该函数可清除这些映射配置。
- 使用方法:无需传入参数,直接调用即可。
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
- 作用:根据
GPIO_InitStruct
结构体中的参数初始化指定的GPIO端口。该结构体包含了GPIO引脚的模式、速度等配置信息。 - 使用方法:传入要初始化的GPIO端口指针和一个
GPIO_InitTypeDef
结构体指针。
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
- 作用:将
GPIO_InitTypeDef
结构体的成员初始化为默认值。通常在使用GPIO_Init
函数之前调用该函数,以确保结构体中的成员有合理的初始值。 - 使用方法:传入一个
GPIO_InitTypeDef
结构体指针。
示例代码
以下是一个简单的示例,展示了如何使用这些函数来初始化GPIO端口:
#include "stm32f10x.h"int main(void)
{GPIO_InitTypeDef GPIO_InitStructure;// 使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 将GPIOA的所有寄存器恢复为默认值GPIO_DeInit(GPIOA);// 将复用功能I/O的寄存器恢复为默认值GPIO_AFIODeInit();// 将GPIO_InitStructure结构体的成员初始化为默认值GPIO_StructInit(&GPIO_InitStructure);// 配置GPIOA的引脚0为推挽输出模式,最大速度为50MHzGPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;// 根据GPIO_InitStructure结构体的参数初始化GPIOAGPIO_Init(GPIOA, &GPIO_InitStructure);while (1){// 点亮LEDGPIO_SetBits(GPIOA, GPIO_Pin_0);// 延时一段时间for (int i = 0; i < 1000000; i++);// 熄灭LEDGPIO_ResetBits(GPIOA, GPIO_Pin_0);// 延时一段时间for (int i = 0; i < 1000000; i++);}
}
代码解释
- 使能时钟:在使用GPIO之前,需要先使能相应的时钟。这里使用
RCC_APB2PeriphClockCmd
函数使能GPIOA的时钟。 - 复位寄存器:调用
GPIO_DeInit
和GPIO_AFIODeInit
函数将GPIOA和复用功能I/O的寄存器恢复为默认值。 - 初始化结构体:调用
GPIO_StructInit
函数将GPIO_InitStructure
结构体的成员初始化为默认值。 - 配置GPIO:设置
GPIO_InitStructure
结构体的成员,指定要配置的引脚、模式和速度。 - 初始化GPIO端口:调用
GPIO_Init
函数根据GPIO_InitStructure
结构体的参数初始化GPIOA。 - 循环控制LED:在主循环中,使用
GPIO_SetBits
和GPIO_ResetBits
函数点亮和熄灭连接到GPIOA引脚0的LED。
通过以上步骤,你可以使用这些函数来配置和控制STM32的GPIO端口。
函数名 | 功能 | 参数说明 | 适用场景 |
---|---|---|---|
GPIO_SetBits | 将指定GPIO端口的指定引脚置为高电平(逻辑1) | - GPIOx :指定的GPIO端口,如GPIOA 、GPIOB 等- GPIO_Pin :指定的引脚,如GPIO_Pin_0 、GPIO_Pin_1 等 | 仅需将引脚拉高的场景,操作简单直接 |
GPIO_ResetBits | 将指定GPIO端口的指定引脚置为低电平(逻辑0) | - GPIOx :指定的GPIO端口,如GPIOA 、GPIOB 等- GPIO_Pin :指定的引脚,如GPIO_Pin_0 、GPIO_Pin_1 等 | 仅需将引脚拉低的场景,操作简单直接 |
GPIO_WriteBit | 将指定GPIO端口的指定引脚设置为指定的电平值 | - GPIOx :指定的GPIO端口,如GPIOA 、GPIOB 等- GPIO_Pin :指定的引脚,如GPIO_Pin_0 、GPIO_Pin_1 等- BitVal :电平值,Bit_SET 表示高电平,Bit_RESET 表示低电平 | 需要灵活设置引脚高低电平的场景 |
GPIO_Write | 将一个16位的值写入指定的GPIO端口,一次性设置该端口所有引脚的电平状态 | - GPIOx :指定的GPIO端口,如GPIOA 、GPIOB 等- PortVal :一个16位的值,用于设置端口所有引脚的电平 | 需要对一个端口的多个引脚进行批量操作的场景 |
这四个函数均为STM32标准库中用于操作GPIO引脚输出电平的函数,下面为你对比分析它们的区别,并给出使用示例。
函数区别分析
1. void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
- 功能:将指定GPIO端口的指定引脚置为高电平(逻辑1)。
- 特点:只能用于将引脚置高,操作简单直接,适用于只需要将引脚拉高的场景。
2. void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
- 功能:将指定GPIO端口的指定引脚置为低电平(逻辑0)。
- 特点:只能用于将引脚置低,操作简单直接,适用于只需要将引脚拉低的场景。
3. void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
- 功能:将指定GPIO端口的指定引脚设置为指定的电平值,电平值可以是高电平(
Bit_SET
)或低电平(Bit_RESET
)。 - 特点:可以灵活设置引脚的高低电平,通过传入不同的
BitAction
参数来控制。
4. void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
- 功能:将一个16位的值写入指定的GPIO端口,一次性设置该端口所有引脚的电平状态。
- 特点:可以同时控制一个端口的所有16个引脚,适用于需要对多个引脚进行批量操作的场景。
示例代码
以下是使用这四个函数的示例代码:
#include "stm32f10x.h"int main(void)
{GPIO_InitTypeDef GPIO_InitStructure;// 使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 配置GPIOA的引脚0和引脚1为推挽输出模式,最大速度为50MHzGPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);while (1){// 使用GPIO_SetBits将引脚0置高GPIO_SetBits(GPIOA, GPIO_Pin_0);// 延时一段时间for (int i = 0; i < 1000000; i++);// 使用GPIO_ResetBits将引脚0置低GPIO_ResetBits(GPIOA, GPIO_Pin_0);// 延时一段时间for (int i = 0; i < 1000000; i++);// 使用GPIO_WriteBit将引脚1置高GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);// 延时一段时间for (int i = 0; i < 1000000; i++);// 使用GPIO_WriteBit将引脚1置低GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);// 延时一段时间for (int i = 0; i < 1000000; i++);// 使用GPIO_Write同时设置引脚0和引脚1的电平// 0x03 表示二进制 0000 0000 0000 0011,即引脚0和引脚1都置高GPIO_Write(GPIOA, 0x03);// 延时一段时间for (int i = 0; i < 1000000; i++);// 使用GPIO_Write同时设置引脚0和引脚1的电平// 0x00 表示二进制 0000 0000 0000 0000,即引脚0和引脚1都置低GPIO_Write(GPIOA, 0x00);// 延时一段时间for (int i = 0; i < 1000000; i++);}
}
代码解释
- 使能时钟和配置GPIO:使能GPIOA的时钟,并将GPIOA的引脚0和引脚1配置为推挽输出模式。
- 使用
GPIO_SetBits
和GPIO_ResetBits
:分别将引脚0置高和置低,实现引脚0的电平翻转。 - 使用
GPIO_WriteBit
:通过传入Bit_SET
和Bit_RESET
参数,将引脚1置高和置低,实现引脚1的电平翻转。 - 使用
GPIO_Write
:通过传入不同的16位值,一次性设置引脚0和引脚1的电平状态。
以下是三种GPIO控制方法的对比及效果分析:
1. 方法对比
方法1:GPIO_SetBits
和 GPIO_ResetBits
GPIO_SetBits(GPIOA, GPIO_Pin_0); // 置高电平
GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 置低电平
- 原理:直接操作寄存器,通过宏定义实现。
- 特点:
- 代码简洁,无函数调用开销,执行效率高。
- 专用于单一操作(置高或置低),功能明确。
方法2:GPIO_WriteBit
配合 Bit_RESET
和 Bit_SET
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); // 置低电平
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); // 置高电平
- 原理:通过函数调用设置电平,参数使用枚举值(
BitAction
)。 - 特点:
- 代码直观,参数明确(
Bit_SET
/Bit_RESET
)。 - 灵活性高,可动态指定电平状态(如通过变量控制)。
- 代码直观,参数明确(
方法3:GPIO_WriteBit
配合强制类型转换
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0); // 置低电平
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1); // 置高电平
- 原理:强制将整数
0
和1
转换为BitAction
类型。 - 特点:
- 功能上等同于方法2(
BitAction
内部对应0
和1
)。 - 缺点:代码可读性差,潜在兼容性风险(依赖枚举底层实现)。
- 功能上等同于方法2(
2. 效果是否相同?
- 功能层面:三种方法最终均会修改GPIO输出寄存器的对应位,实现引脚电平控制,效果完全相同。
- 性能层面:
- 方法1(宏操作)直接操作寄存器,无函数调用,效率最高。
- 方法2和方法3(函数调用)有轻微函数调用开销,但实际应用中差异可忽略。
3. 代码规范建议
-
推荐方法2:
使用Bit_SET
和Bit_RESET
,兼顾可读性、规范性和灵活性。GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); // 正确写法
-
避免方法3:
强制类型转换会降低代码可维护性,且不符合ST库的设计意图。 -
方法1的适用场景:
若需极致性能(如高频信号控制),可优先选择GPIO_SetBits
/GPIO_ResetBits
。
4. 补充说明
- 条件编译
#if 0
的作用:
代码中被#if 0
包裹的部分会被编译器忽略,仅用于临时禁用代码块。
若要启用方法1或方法2,需将对应的#if 0
改为#if 1
。
总结
三种方法在功能上完全等效,但代码风格和可维护性差异显著。推荐优先使用方法2(Bit_SET
/Bit_RESET
),既能保证效率,又符合工程规范。