2. GPIO 口
📌 本节学习目标
- 理解 GPIO 的基本结构与工作原理
- 掌握 GPIO 的 8 种工作模式
- 学会使用寄存器配置 GPIO
- 学会使用库函数配置 GPIO
2.1 GPIO 概述
GPIO(General Purpose Input/Output)是 STM32 最基本的 I/O 接口,每个引脚可独立配置为输入或输出模式。STM32F103C8T6 有 37 个 GPIO 引脚,分布在 GPIOA(PA0-PA15)、GPIOB(PB0-PB15)、GPIOC(PC13-PC15)端口中。
2.2 GPIO 工作模式
STM32 的 GPIO 有 8 种工作模式,分为输入和输出两大类:
| 模式 | 类型 | 说明 |
|---|---|---|
| 浮空输入 | 输入 | 引脚电平直接进入输入数据寄存器,无上下拉 |
| 上拉输入 | 输入 | 内部上拉电阻接入,默认高电平 |
| 下拉输入 | 输入 | 内部下拉电阻接入,默认低电平 |
| 模拟输入 | 输入 | 引脚接入 ADC 等模拟外设 |
| 开漏输出 | 输出 | 类似 MOS 管漏极开路,需外部上拉;可实现电平转换、线与逻辑 |
| 推挽输出 | 输出 | PMOS + NMOS 互补输出,可输出高/低电平 |
| 复用开漏输出 | 复用 | 由片上外设驱动(如 I2C 的 SDA 线) |
| 复用推挽输出 | 复用 | 由片上外设驱动(如 USART 的 TX、PWM 输出) |
2.3 GPIO 寄存器
每个 GPIO 端口都由一系列寄存器控制。以 GPIOA 为例:
| 寄存器 | 地址偏移 | 描述 |
|---|---|---|
| GPIOx_CRL | 0x00 | 端口配置低寄存器(Pin 0-7) |
| GPIOx_CRH | 0x04 | 端口配置高寄存器(Pin 8-15) |
| GPIOx_IDR | 0x08 | 输入数据寄存器 |
| GPIOx_ODR | 0x0C | 输出数据寄存器 |
| GPIOx_BSRR | 0x10 | 置位/复位寄存器(原子操作) |
| GPIOx_BRR | 0x14 | 复位寄存器 |
| GPIOx_LCKR | 0x18 | 锁定寄存器 |
2.4 寄存器方式编程
C 代码(寄存器操作 LED)
// 寄存器方式点亮 LED(PB0 接 LED)
#define GPIOB_CRL *(volatile unsigned long *)0x40010C00
#define GPIOB_ODR *(volatile unsigned long *)0x40010C0C
#define RCC_APB2ENR *(volatile unsigned long *)0x40021018
// 开启 GPIOB 时钟
RCC_APB2ENR |= (1 << 3); // APB2 总线上 GPIOB 时钟使能
// 配置 PB0 为推挽输出,50MHz
GPIOB_CRL &= ~(0x0F << 0); // 先清除 CNF0 和 MODE0
GPIOB_CRL |= (0x01 << 0); // MODE0 = 01(输出 50MHz)
GPIOB_CRL |= (0x00 << 2); // CNF0 = 00(推挽输出)
// 点亮 LED
GPIOB_ODR |= (1 << 0); // PB0 = 1(根据电路决定高/低电平点亮)2.5 库函数方式编程
C 代码(库函数操作 LED)
#include "stm32f10x.h"
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能 GPIOB 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// PB0 配置为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
int main(void)
{
GPIO_Config();
while (1)
{
GPIO_SetBits(GPIOB, GPIO_Pin_0); // PB0 = 1
delay(500);
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // PB0 = 0
delay(500);
}
}💡 这两段代码怎么用、有什么区别?
- 寄存器方式:直接读写内存地址,性能最高、代码量最少,但可读性差、容易算错偏移。适合理解底层原理
- 库函数方式:用
GPIO_Init()+ 结构体配置,写起来像填表,是课程主流方式,本课程实验都用它 - 两者最终配置的都是同一组寄存器——「先使能时钟,再设模式,最后操作引脚」三步缺一不可