
-
保护二极管:当瞬间电压进入I/O引脚时,若此电压大于VDD(3.3V),则上方的保护二极管导通。
当瞬间电压小于VSS(0V),下方保护二极管导通。
若输入电压在0~3.3V之间,那么2个二极管均不会导通。
此结构只能抵御一瞬间的电压波动,长时间高电压接入I/O口依旧会损坏芯片。 -
上拉/下拉电阻输入 :为悬空的输入引脚提供一个默认的稳定电平。
上拉 (P-UP) : 内部电阻将其连接到 VDD,使默认电平为高电平。
下拉 (P-DOWN) : 内部电阻将其连接到 VSS,使默认电平为低电平。 -
施密特触发器/TTL肖特基触发器:电压比较器,有效消除输入信号中的噪声和抖动。高于上限输出高,低于上限输出低。
-
输入数据寄存器-IDR Input Data Register:存储施密特触发器输出的电平值(0或1),CPU 通过读取IDR得知引脚电平。
-
输出数据寄存器-ODR Output Data Register):存储引脚期望的输出电平,ODR 的值会直接控制P-MOS 和 N-MOS,CPU 读取 ODR 读取“期望的输出值”,而非“引脚的实际电平”。
-
位设置/清除寄存器-BSRR Bit Set/Reset Register:快速地设置或清除 ODR 中的某一位。
-
N-MOS和P-MOS:

在推挽模式下,STM32对IO口输出有绝对的控制权
输出数据寄存器为1,P-MOS激活,N-MOS断开,输出接入VDD,输出高电平;
输出数据寄存器为0,P-MOS断开,N-MOS激活,输出接入VSS,输出低电平; 在开漏模式下,P-MOS管失效,只有N-MOS管工作
输出数据寄存器为1,P-MOS无效,N-MOS关闭,GPIO引脚处于“高阻态”,输出电压由外部电路决定。
输出数据寄存器为0,P-MOS无效,N-MOS导通,输出接入VSS,输出低电平;
[!NOTE] 应用
开漏输出一般应用在
① I2C、SMBUS 通讯等需要“线与”功能的总线电路中。
②除此之外,还用在电平不匹配的场合,如需要输出 5 伏的高电平,就可以在外部接一个上拉电阻,上拉电源为 5 V,并且把引脚设置为开漏模式,当输出高阻态时,由上拉电阻和电源向外输出 5V电平,如右图所示。
[!NOTE]
“高阻态”:就是悬空,既不是高电平,也不是低电平,引脚上的电压未定义,易收到电磁信号的干扰。
# 1.定义
通用I/O:简称GPIO,是I/O的最基本形式。一般使用正逻辑,高电平对应数字信号“1”,低电平对应数字信号“0”

# 2.实战
# 编程步骤:
第一步:使能GPIOx口的时钟
第二步:指明GPIOx口的哪一位,这一位的速度大小以及模式。
第三步:调用GPIOx口初始化函数,进行初始化。
第四步:调用GPIO-SetBits函数,进行相应为的置位。
# 函数一览

# 实例如下:
对于单个GPIO口的初始化如下
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
输出:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);//输出低电平
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
//翻转电平
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
第三步:调用GPIOA口初始化函数,进行初始化。
第四步:调用GPIO-SetBits函数,进行相应为的置位。
把第二、三、四步合并分别设置GPIOA和GPIOE
先设置GPIOA
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOA,&GPIO-InitST); //根据设定参数初始化GPIOA
GPIO_SetBits(GPIOA,GPIO_Pin_4); //输出高
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOE,&GPIO-InitST); //根据设定参数初始化GPIOE
GPIO_SetBits(GPIOE,GPIO_Pin_3); //输出高
配置终端触发方式(上升沿/下降沿/双边沿)
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
{
if (GPIO_Pin == GPIO_PIN_13)
{
// 按键响应代码
}
}
(1)、在STM32内部为了省电,所有外设(GPIO、USART、TIM…)默认是“关机”状态,即时钟被关闭。第一步永远是打开这个部门的电源(时钟)。

GPIO 配置与操作函数
[!NOTE] GPIO_Init(GPIOA, &GPIO_InitStruct) 为什么第一个参数实际填写
GPIOA而不是&GPIOA
在stm32f10x.h头文件中,有这样一行定义:#define GPIOA ((GPIO_TypeDef *) 0x40010800)所以GPIOA本身已经是一个指针**,它不是一个变量,而是一个宏定义 ,它代表的就是一个地址。
(2)、这些参数在stm32f10x_gpio.h头文件搜索

GPIO初始化结构体

GPIO模式

GPIO引脚宏定义

# 3、stm32寄存器与c语言

-
核心原理: STM32中每一个硬件寄存器(ODR, IDR…)都对应一个固定的物理内存地址。CPU 通过读写固定的物理内存地址来控制硬件(寄存器)。
-
基地址 (Base Address):
- 每个外设(如
GPIOA)都有一个起始地址。 GPIOA基地址 =0x40010800GPIOB基地址 =0x40010C00
- 每个外设(如
-
GPIO_TypeDef结构体:- 它是一个C语言的 “地图” ,定义了
GPIOA内部所有寄存器(CRL,CRH,IDR,ODR…)的排列顺序和偏移量。
- 它是一个C语言的 “地图” ,定义了
-
偏移地址 (Offset):
- 寄存器在“地图”上相对于基地址的位置。
- 因为寄存器大多是
uint32_t(4字节),所以偏移量是0x00,0x04,0x08… ODR寄存器的偏移量 =0x0C
-
绝对地址 (Absolute Address):
- 绝对地址 = 基地址 + 偏移地址
- 举例 (GPIOA 的 ODR):
0x40010800(GPIOA 基地址) +0x0C(ODR 偏移量) =0x4001080C
-
C 代码翻译:
- C 代码:
GPIOA->ODR = 0x0001; GPIOA是一个指向基地址0x40010800的指针 :*#define GPIOA ((GPIO_TypeDef )GPIOA_BASE)- ->ODR是C语言语法,告诉编译器去
GPIO_TypeDef结构体“地图”上查找ODR的偏移量(0x0C)。 - 编译器操作: 生成机器码,将
0x0001这个值,写入到最终的绝对地址0x4001080C。
- C 代码:
[!NOTE] Title
个人理解:STM32 正是利用了struct结构体能自动处理偏移量的特性,来满足了访问硬件寄存器时“基地址 + 偏移地址”的需求。
GPIOA:获取基地址 (0x40010800)。
ODR:查找 ODR 成员对应的偏移地址 (0x0C)。
= 0x0001:将值 0x0001 写入到算出来的绝对地址 (0x40010800 + 0x0C = 0x4001080C) 中。

# 1. 单个 GPIO 引脚电流限制

- 常见 MCU(如 STM32):
- 最大电流:≈ 20 mA
- 推荐工作电流:2 ~ 8 mA
超过推荐值可能导致:
- IO 发热
- 电平不稳
- 长期可靠性下降
# 2. MCU 总电流限制
- 多个 GPIO 同时工作时,总电流也有限制
- 典型值(以 STM32 为例):
- 所有 GPIO 累计电流:≈ 120 ~ 150 mA
常见问题:
- 单个 LED 正常
- 多个 LED 同时点亮 → 芯片异常
# 3. GPIO 高低电平驱动能力不同
- 输出高电平:拉电流(Source)
- 输出低电平:灌电流(Sink)
- 两者能力 通常不对称
# 二、拉电流(Source Current)
拉电流:
GPIO 输出 高电平 时,向外部负载提供电流 的能力。
# 三、灌电流(Sink Current)
灌电流:
GPIO 输出 低电平 时,从外部负载吸收电流 的能力。
# 四、拉电流 vs 灌电流
| 对比项 | 拉电流 | 灌电流 |
|---|---|---|
| GPIO 电平 | 高电平 | 低电平 |
| 电流方向 | GPIO → 负载 | 负载 → GPIO |
| 驱动能力 | 较弱 | 较强(推荐) |
| 工程常用 | 少 | 多 |
工程经验:
MCU 的 灌电流能力通常强于拉电流能力


