GPIO(General Use Input/Output) ,通用输入输出,可以被配置为输入或输出,以满足不同的应用需求。
开发环境配置
-
选择STM32CubeIDE(本质STM32CubeMX+eclipse)
-
选择STM32CubeMX生成基础代码,并配合其他编辑器使用:
芯片配置
打开STM32CubeMX,选择需要的芯片并创建工程(以STM32F103C8T6为例):
-
左键芯片引脚,可以选择GPIO口负责的功能,例如GPIO_Input可以从外部捕获高低电平,GPIO_Output可以对外输出高低电平。
-
设置对应功能后,可右键引脚,选择Enter User Label,可以为引脚自定义名称,便于之后调用。例如:命名为
LED_Red,之后调用时可输入LED_Red_GPIO_Port和LED_Red_Pin
-
在左侧选择System Core展开后选择SYS,在其中的GPIO选项可以调整GPIO口的具体信息:
GPIO output level:默认输出高电平/低电平;GPIO mode:输出模式,推挽输出/开漏输出;GPIO Pull-up/Pull-down:选择上拉模式/下拉模式/浮空输入。
-
在左侧选择System Core展开后选择SYS,在其中的Debug选项里面选择Serial Wire,以防止之后无法下载程序。
-
设置好后,在顶侧Project Manager中,左侧选择Code Generator,为”Generate peripheral initialization as a pair of .c/.h file per peripheral”打勾,从而将外设初始化从
main.c中分离出来。 -
左侧选择Project,在Project Settings中,Project Name输入项目名称,然后在Toolchain/IDE中选择:
- STM32CubeIDE以在STM32CubeIDE中编辑,
- CMake以在CLion/VSCode中编辑,
- MDK-ARM以在Keil/VSCode中编辑;
- STM32CubeIDE以在STM32CubeIDE中编辑,
-
完成所有操作后,右上角Generate Code以生成项目代码。
编辑代码
主程序位于\Core\Src\main.c:
点灯大师
目的:使LED_Red规律闪烁。
在mianmain函数的while(1)死循环中,在/* USER CODE BEGIN 3 */和 /* USER CODE END 3 */之间,我们写入:
HAL_GPIO_WritePin(LED_Red_GPIO_Port, LED_Red_Pin, GPIO_PIN_SET);若不在此安全区内输入,后续若用CubeMX更改配置并重新生成代码,安全区外的代码会被覆写并丢失
其中:
HAL_GPIO_WritePin函数:为指定的GPIO口写入高电平(GPIO_PIN_SET)/低电平(GPIO_PIN_RESET);LED_Red_GPIO_Port参数:LED_Red对应的GPIO口的组别;LED_Red_Pin参数:LED_Red对应的GPIO口的编号;GPIO_PIN_SET参数:设置为高电平,相反的,GPIO_PIN_RESET设置为低电平。
在这段语句之后,写入:
HAL_Delay(500);其中:
HAL_Delay函数:指示等待;500参数:等待500毫秒。
在这两段语句后,继续写入:
HAL_GPIO_WritePin(LED_Red_GPIO_Port, LED_Red_Pin, GPIO_PIN_RESET);HAL_Delay(500);从而让红色LED实现:“点亮500ms->熄灭500ms”的循环。
之后,下载程序到单片机,按下单片机上的 RST(Reset) 按键,可以看到红色LED按照期望在闪烁。
按键控制
目的:按下KEY1期间LED_Green点亮,按下KEY2切换LED_Red的亮灭。
使用以下代码用于获取按键按下情况:
HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin);其中:
HAL_GPIO_ReadPin函数:用于捕获指定GPIO口的高低电平,要注意返回值是GPIO_PIN_SET或GPIO_PIN_RESET而不是1或0;KEY1_GPIO_Port参数:与LED_Red_Pin一样,KEY1对应的GPIO口的组别;KEY1_Pin参数:与LED_Red_Pin一样,KEY2对应的GPIO口的编号。
写入以下代码块:
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)==GPIO_PIN_RESET){ HAL_GPIO_WritePin(LED_Green_GPIO_Port, LED_Green_Pin, GPIO_PIN_SET);}else{ HAL_GPIO_WritePin(LED_Green_GPIO_Port, LED_Green_Pin, GPIO_PIN_RESET);}这段代码的功能是:在KEY1按下期间,使绿色LED变亮,反之,若KEY没有被按下,则将其熄灭。
写入以下代码块:
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)==GPIO_PIN_RESET){ HAL_Delay(10); if (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)==GPIO_PIN_RESET) { HAL_GPIO_TogglePin(LED_Red_GPIO_Port, LED_Red_Pin); while (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)==GPIO_PIN_RESET) {} }}这段代码的功能是:按下KEY2时,点亮红色LED,再次按下KEY2时,熄灭红色LED,实现一个“切换”的功能。
HAL_GPIO_TogglePin函数:
用于切换指定GPIO口的输出电平,若当前为高电平,则改为低电平;若当前为低电平,则改为高电平。HAL_Delay(10);的作用:
按键在被按下的初期,电平会产生抖动,设置10ms的延时,若10ms后仍是低电平,则确定按键被按下,执行之后操作。while (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)==GPIO_PIN_RESET) {}的作用:
等待松开KEY2,防止重复触发。
实际上,这是非常屎山的写法,在之后学习更多内容后,可以对其进行改良。
GPIO的输入输出模式
推挽输出(Output Push Pull)
P-MOS和N-MOS协同工作:

当输出高电平时:
P-MOS被激活,N-MOS被关闭,此时VDD(3.3V)与IO引脚联通,输出高电平:

当输出低电平时:
N-MOS被激活,P-MOS被关闭,此时VSS(0V)与IO引脚联通,输出低电平:

开漏输出(Output Open Drain)
只有N-MOS进行工作,P-MOS一直处于断开状态:

当输出高电平时:
N-MOS被关闭,形成断路,无法形成回路:

当输出低电平时:
N-MOS被激活,与外侧的电压形成回路,产生电流:

推挽输出和开漏输出的区别:
| 推挽输出 | 开漏输出 | |
|---|---|---|
| 优点 | 可以直接输出3.3V | 配合外部电路,输出更加灵活 |
| 缺点 | 只能输出芯片的3.3V | 高电平实际上是高阻态,无法输出电流 |
复用推挽输出和复用开漏输出:
根据控制来源的不同(例如片上外设的复用功能输入),STM32将两种输出模式细分出复用推挽输出和复用开漏输出。
上拉输入(Pull-up)
启用与VDD(3.3V)相接的电阻:

下拉输入(Pull-down)
启用与VSS(0V)相接的电阻:

浮空输入(No pull-up and no pull-down)
两个电阻都不会启用,可依赖外部电路实现更多可能性:

模拟输入
在施密特触发器之前产生分支,读取模拟信号,即输入电压的具体数值:

复用功能输入
在施密特触发器之后,读取的是数字信号(0或1):

- 施密特触发器:在电压高于高参考电压时输出高电平,在电压低于低参考电压时输出低电平,在高参考电压和低参考电压之间则保持当前电平信号不变:

总结
相关函数:
HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);HAL_GPIO_WritePin:用于向指定的GPIO口输出高电平/低电平
GPIO_TypeDef *GPIOx参数:指定GPIO口对应的组别;uint16_t GPIO_Pin参数:指定GPIO口对应的编号;GPIO_PinState PinState参数:GPIO_PIN_SET则设置为高电平,GPIO_PIN_RESET则设置为低电平。
HAL_Delay(uint32_t Delay);HAL_Delay:用于等待指定的时长
uint32_t Delay:等待的时长(单位:毫秒)。
HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)HAL_GPIO_TogglePin:用于切换指定的GPIO口的输出电平
GPIO_TypeDef *GPIOx参数:指定GPIO口对应的组别;uint16_t GPIO_Pin参数:指定GPIO口对应的编号;
HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);HAL_GPIO_ReadPin:用于读取指定的GPIO口的输出电平
GPIO_TypeDef *GPIOx参数:指定GPIO口对应的组别;uint16_t GPIO_Pin参数:指定GPIO口对应的编号;
输入输出模式:
- 推挽输出,开漏输出
- 复用推挽输出,复用开漏输出
- 上拉输入,下拉输入,浮空输入
- 模拟输入,复用功能输入
部分信息可能已经过时





