
第二章 GPIO:输出输入模式与 LED、按钮实验
2.1 [GPIO]4种输出模式
单片机内部结构框图

Cortex-M3 CPU 是CPU内核,专门执行运算任务
GPIOA, B, C, D 等是片上外设,每个片上外设能独立完成一个任务,类似于人的手,而引脚PA0类似于手GPIOA上的手指
GPIO的8种工作模式
每个GPIO的引脚都有8种工作模式

I/O端口位的基本结构图

上半部分主要负责输入
下半部分主要负责输出
输出
通用输出推挽
推 - Push - 向外推电流 与P-MOS有关
挽 - Pull - 从外向里拉电流 与N-MOS有关
0 低电压
1 高电压
两个MOS管交替导通,不能同时导通
通用:通过CPU直接控制电路
复用:通过其它片上外设控制电路
通用输出开漏
开漏 - Open-Drain -MOS管漏极开路
漏极保持断开,称为开漏(P-MOS管保持断开)
0 低电压
1 高阻抗

复用输出开挽
复用输出开漏
输入
输入上拉
输入下拉
输入浮空
模拟模式
2.2 [GPIO]IO最大输出速度
IO最大输出速度
向IO交替写0和1且输出不失真的最快速度,高低电平变化的波形不失真
上升时间、下降时间、保持时间

限制IO输出速度的因素
上升时间、下降时间的长短影响最大输出速度,保持时间越长,性能越好
选取最大输出速度的方法
在芯片上标注了3种最佳速度,分别是低速、中速、高速
选取满足要求的最小值,过于陡峭会增加耗电并引入EMI问题(对其他电子元器件产生电磁干扰)
人眼识别的临界频率为45.8Hz,交流电的频率为50Hz

2.3 [GPIO]LED闪灯实验
D1:LED 指示灯,单片机接电源时亮
D2:PC13 开漏接法
第一步:开启GPIOx的时钟
参数1:需要开启的模块的名称;参数2:ENABLE 开启
// 示例:开启GPIOx时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE);
// 示例:开启GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO模块编程接口
GPIO_Init
- 初始化IO引脚
配置引脚的工作模式、最大输出速度
GPIO_WriteBit
- 向输出数据寄存器写 1 个比特位
通过CPU向寄存器写0/1,从而控制引脚的输出电压
GPIO_ReadInputDataBit
- 从输入数据寄存器读 1 个比特位
GPIO_ReadQutputDataBit
- 从输出数据寄存器读 1 个比特位
读取最近一次 GPIO_WriteBit 在输出数据寄存器中写入的值
第二步:初始化IO引脚
// 示例:初始化IO引脚
void GPIO_Init(GPIOTypeDef *GPIOx, // 端口号,取ABCD
GPIO_InitTypeDef *GPIO_InistStruct); // 初始化的参数结构体指针
struct GPIO_InitTypeDef {
uint16_t GPIO_Pin; // 引脚编号,可选值:GPIO_Pin_0 ~ GPIO_Pin_15
GPIOSpeed_TypeDef GPIO_Speed; // 最大输出速度
GPIOMode_TypeDef GPIO_Mode; // 引脚工作模式
};
(1)引脚速度选项(GPIO_Speed)
GPIO_Speed_2MHz:2MHzGPIO_Speed_10MHz:10MHzGPIO_Speed_50MHz:50MHz
(2)引脚模式选项(GPIO_Mode)
输出模式:
GPIO_Mode_Out_PP:通用推挽输出GPIO_Mode_Out_OD:通用开漏输出GPIO_Mode_AF_PP:复用推挽输出GPIO_Mode_AF_OD:复用开漏输出
输入模式:
GPIO_Mode_IPU:上拉输入GPIO_Mode_IPD:下拉输入GPIO_Mode_IN_FLOATING:浮空输入GPIO_Mode_AIN:模拟输入
// 初始化IO引脚,PC13设置为开漏模式,2MHz
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &GPIO_InitStruct);
第三步: 向指定的 IO 引脚写入电平值(0 或 1)
向指定的 IO 引脚写入电平值(0 或 1)。
void GPIO_WriteBit(GPIO_TypeDef *GPIOx, // 端口号,如 GPIOA、GPIOB、GPIOC 等
uint16_t GPIO_Pin, // 引脚编号,如 GPIO_Pin_13
BitAction BitValue); // 要写入的电平值
BitValue:要写入的电平值:
Bit_RESET:写入低电平(0)Bit_SET:写入高电平(1)
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); // 向PC13引脚写0,LED点亮
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); // 向PC13引脚写1,LED熄灭
#include "delay.h"
while(1)
{
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); // 向PC13引脚写0,LED点亮
Delay(100); // 延迟100ms,Delay是从外部引入的函数
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); // 向PC13引脚写1,LED熄灭
Delay(100); // 延迟100ms
}

完整实验代码
#include "stm32f10x.h"
#include "delay.h"
int main(void)
{
// #1.开启GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
// 初始化IO引脚,PC13设置为开漏模式,2MHz
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &GPIO_InitStruct);
// GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); // 向PC13引脚写1,LED熄灭
// GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); // 向PC13引脚写0,LED点亮
while(1)
{
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); // 向PC13引脚写0,LED点亮
Delay(100); // 延迟100ms
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); // 向PC13引脚写1,LED熄灭
Delay(100);
}
}
2.4 [GPIO]4种输入模式
简化输入电路

输入
输入上拉
闭合 上拉电阻 所在电路的开关
作用:提供默认的高电压。当引脚悬空时,提供高电压,输入数据寄存器得到稳定的1,引脚连接时无影响
施密特触发器:将高低电压转换为 0/1,施密特触发器经过的电流必须为 0,R=∞

输入下拉
闭合 下拉电阻 所在电路的开关
作用:提供默认的低电压。当引脚悬空时,提供低电压,输入数据寄存器得到稳定的0,引脚连接时无影响
输入浮空
同时断开 上拉电阻 和 下拉电阻 所在电路的开关
模拟模式
2.5 [GPIO]按钮实验

LED的接线方法


测试电路连接是否正确
测试代码
#include "stm32f10x.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 开启GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStruct;
// 选择要初始化的引脚为PA0
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
// 设置引脚模式为通用推挽输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
// 设置引脚最大输出速度为2MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化PA0引脚
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); // 向PA0写入高电平(1),点亮LED
}
编译 -> 下载 -> 按复位按钮
按钮的接线方法
按钮电路工作原理图


注意:按钮中有引脚的一端朝上
// RCC_APB2PeriphClockCmd(GPIOA, ENABLE); // 已开启,无需再次开启
// GPIO_InitTypeDef GPIO_InitStruct; // 已声明,无需再次声明
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入模式
// GPIO_InitStruct.GPIO_Speed = ...; // 最大输出速度只对输出模式有效
GPIO_Init(GPIOA, &GPIO_InitStruct);
编程接口和代码
GPIO_ReadInputDataBit 是STM32 标准库中专门用于读取单个 GPIO 引脚输入电平状态的核心函数,直白来说,它的作用就是「检测指定 IO 口当前是高电平还是低电平」。
GPIO_ReadInputDataBit是读单个 GPIO 引脚输入电平的专用函数,返回高 / 低电平状态;- 使用前必须将引脚配置为输入模式(上拉 / 下拉 / 浮空);
- 核心用途是检测外部输入信号(如按键、传感器),是嵌入式开发中最常用的函数之一。
GPIO_ReadInputDataBit 函数笔记
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, // 端口号,如 GPIOA、GPIOB 等
uint16_t GPIO_Pin); // 引脚编号,如 GPIO_Pin_1
作用:读取指定 IO 引脚的输入电平值,返回值:
Bit_SET:表示高电平(1)Bit_RESET:表示低电平(0)
while(1) {
// 读取 PA1 引脚电平,判断按钮是否按下
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == Bit_RESET) {
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); // 按钮按下,LED 亮
} else {
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); // 按钮松开,LED 灭
}
}
完整实验代码
#include "stm32f10x.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 开启GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStruct;
// 选择要初始化的引脚为PA0
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
// 设置引脚模式为通用推挽输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
// 设置引脚最大输出速度为2MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化PA0引脚
// GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); // 向PA0写入高电平(1),点亮LED
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入模式
GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化PA1引脚
while(1)
{
// 读取 PA1 引脚电平,判断按钮是否按下
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == Bit_RESET)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); // 按钮按下,LED 亮
}
else
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); // 按钮松开,LED 灭
}
}
}
本文由 FormerlyXZ 创作, 采用CC BY-NC-SA 4.0 许可协议。
转载请注明出处,禁止商业使用。
评论
评论由 Giscus提供。首次评论需要在评论框里登录一次 GitHub(与页头的站点登录互相独立,这是浏览器安全限制), 之后当前浏览器会记住,不用反复登录;评论框里可以直接粘贴图片、拖入 PDF 等附件(GitHub 会帮你存好)。
