目录
[1 IO 开漏模式](#1 IO 开漏模式)
[1.1 模式介绍](#1.1 模式介绍)
[1.2 主要特性](#1.2 主要特性)
[1.3 开漏 vs 推挽 对比](#1.3 开漏 vs 推挽 对比)
[2 STM32 IO 开漏模式](#2 STM32 IO 开漏模式)
[2.1 电路结构介绍](#2.1 电路结构介绍)
[2.2 STM32 开漏模式应用](#2.2 STM32 开漏模式应用)
[2.2.1 开漏模式配置](#2.2.1 开漏模式配置)
[2.2.2 开漏模式特性](#2.2.2 开漏模式特性)
[2.3 应用实例](#2.3 应用实例)
[2.4 STM32 开漏模式注意事项](#2.4 STM32 开漏模式注意事项)
概述
本文主要介绍IO 开漏模式的特征和STM32 IO开路模式的配置和应用方法,开漏模式为系统设计提供了极大的灵活性,特别是在总线通信、电平转换和多设备接口等场景中。正确理解和使用开漏模式是嵌入式开发的重要技能。
1 IO 开漏模式
1.1 模式介绍
IO开漏模式(Open-Drain)是一种常见的数字电路输出模式,特别是在微控制器的GPIO配置中。
电路结构:
开漏输出内部结构:
VDD
|
| (外部上拉电阻)
|
Pin ---+--- NMOS --- GND
|
保护二极管
|
输入检测
工作原理
输出0:内部NMOS导通,引脚输出低电平(接近GND)
输出1:内部NMOS截止,引脚呈高阻态,电平由外部电路决定
电平输出特征:
开漏输出波形特征: - 下降沿:快速(NMOS主动拉低) - 上升沿:缓慢(依靠上拉电阻充电) - 高电平:由上拉电压决定 - 低电平:接近0V
1.2 主要特性
1) 输出结构:
开漏输出只使用一个NMOS晶体管(或NPN晶体管)连接到输出引脚。当输出为逻辑0时,晶体管导通,将引脚拉低到地;当输出为逻辑1时,晶体管截止,输出引脚处于高阻状态(即不输出高电平)。
2)需要外部上拉电阻
由于开漏输出无法主动输出高电平,因此通常需要在输出引脚和电源(VCC)之间连接一个上拉电阻。当晶体管截止时,上拉电阻将引脚拉至高电平。
3) 电平转换:
开漏输出可以方便地实现电平转换。因为输出高电平由外部上拉电阻所连接的电源电压决定,所以可以使用不同的电源电压来适应不同的逻辑电平。例如,3.3V的微控制器可以通过开漏输出与5V的设备通信。
4) 线与连接
多个开漏输出可以连接到同一根总线上,通过一个共享的上拉电阻拉高。这样可以实现"线与"逻辑:只要有一个输出为低电平,总线就被拉低;只有当所有输出都为高阻态时,总线才被上拉电阻拉高。这种特性在I2C等总线协议中非常有用。
5) 驱动能力
开漏输出的下拉驱动能力通常较强(取决于晶体管的尺寸),但上拉能力取决于外部上拉电阻的阻值和电源电压。上拉电阻的阻值需要根据总线的电容和通信速度来选择,阻值太大会导致上升沿变慢,阻值太小会增加功耗。
6) 功耗
当输出为低电平时,开漏输出会通过晶体管和地形成通路,产生持续电流,从而消耗功率。因此,在设计中需要注意避免长时间保持低电平输出,尤其是在电池供电的设备中。
7) 与推挽输出的区别:
推挽输出使用两个晶体管(一个PMOS和一个NMOS)分别负责输出高电平和低电平,能够主动驱动高电平和低电平,而开漏输出只能主动驱动低电平,高电平需要外部上拉。
8) 注意事项:
-
在使用开漏模式时,必须确保有上拉电阻,否则输出高电平时引脚将处于浮空状态,电平不确定。
-
上拉电阻的阻值需要根据实际应用选择,一般典型值在1kΩ到10kΩ之间,具体取决于总线电容和通信速度。
1.3 开漏 vs 推挽 对比
| 特性 | 开漏输出 | 推挽输出 |
|---|---|---|
| 输出高电平 | 高阻态,靠上拉 | 主动驱动高电平 |
| 输出低电平 | 主动拉低 | 主动拉低 |
| 电平转换 | 支持 | 不支持 |
| 线与功能 | 支持 | 不支持 |
| 总线冲突 | 安全 | 可能损坏 |
| 驱动能力 | 下拉强,上拉弱 | 双向都强 |
| 功耗 | 静态功耗(上拉电阻) | 切换功耗 |
| 速度 | 上升沿较慢 | 速度快 |
2 STM32 IO 开漏模式
STM32的开漏模式为系统设计提供了极大的灵活性,特别是在总线通信、电平转换和多设备接口等场景中。正确理解和使用开漏模式是STM32开发的重要技能。
2.1 电路结构介绍
STM32的IO开漏模式(Open-Drain)是一种常见的GPIO配置。在开漏模式下,输出驱动器由一个NMOS晶体管构成,

1)**当输出设置为0时,**NMOS导通,将引脚拉到低电平;
2)当输出设置为1时, NMOS截止,引脚呈现高阻态,此时引脚电平由外部电路(如上拉电阻)决定。
在STM32中,开漏模式通常用于以下情况:
电平不匹配:当需要与不同电压的设备通信时,开漏输出可以配合上拉电阻到目标电压,实现电平转换。
总线通信:如I2C总线,多个设备可以共享一条线路,通过开漏输出实现"线与"功能。
需要双向通信的场合
**注意:**开漏输出模式下,当输出1时,引脚处于高阻态,因此通常需要外部上拉电阻来确保高电平。
2.2 STM32 开漏模式应用
2.2.1 开漏模式配置
1) HAL库配置方法
cpp
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 基本开漏输出配置
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 不使用内部上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; // 输出速度
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
2) 复用功能开漏配置
cpp
// 用于I2C等外设
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; // SCL, SDA
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // 复用开漏
GPIO_InitStruct.Pull = GPIO_PULLUP; // 使用内部上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; // I2C1复用功能
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
2.2.2 开漏模式特性
1)输出特性
cpp
// 开漏输出行为验证
void Test_OpenDrain_Behavior(void)
{
// 配置为开漏输出
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 输出高阻态
// 此时引脚电平由外部电路决定
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 输出低电平
// 此时引脚被主动拉低到GND
}
2) 读取开漏引脚状态
cpp
// 即使配置为开漏输出,也可以读取引脚实际状态
GPIO_PinState Read_OpenDrain_Pin(void)
{
// 读取引脚当前实际电平
return HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
}
// 应用示例
void Monitor_OpenDrain_Pin(void)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 设置为高阻态
// 等待外部设备拉低引脚
while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) {
// 等待引脚被拉低
}
// 引脚被外部拉低,执行相应操作
}
2.3 应用实例
1) I2C总线应用
cpp
// 完整的I2C GPIO配置
void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
// I2C1 SCL - PB6, SDA - PB7
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // 复用开漏
GPIO_InitStruct.Pull = GPIO_PULLUP; // 内部上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; // I2C1复用功能
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 注意:某些STM32系列可能需要额外配置
}
2) 电平转换应用
cpp
// 3.3V STM32与5V设备通信
void Level_Shift_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
// TX引脚:开漏输出,外部上拉到5V
GPIO_InitStruct.Pin = GPIO_PIN_9; // TX
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL; // 不使用内部上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// RX引脚:浮空输入(确保5V耐受)
GPIO_InitStruct.Pin = GPIO_PIN_10; // RX
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
3) 多设备中断共享
cpp
// 多个外部设备共享一个中断线
void Shared_Interrupt_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// 所有设备的中断输出配置为开漏
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 内部上拉确保默认高电平
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
// 设备1中断输出 - PA0
GPIO_InitStruct.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 初始化为高阻态
// 设备2中断输出 - PA1
GPIO_InitStruct.Pin = GPIO_PIN_1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
// MCU中断输入引脚配置
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
4) 双向数据线应用
cpp
// 单引脚实现双向数据传输
void Bidirectional_Data_Bus_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置为开漏输出,带内部上拉
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 重要:内部上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 初始化为释放状态(高阻态)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
}
// 发送数据
void Send_Data(uint8_t data)
{
if(data == 0) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低
} else {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); // 释放(高阻态)
}
}
// 接收数据
uint8_t Receive_Data(void)
{
// 确保引脚处于释放状态
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
// 读取引脚状态
return HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8);
}
2.4 STM32 开漏模式注意事项
1) 上拉电阻选择
高速应用: 小电阻值(1k-4.7kΩ)
优点:上升沿快,驱动能力强
缺点:功耗大
低速应用: 大电阻值(10k-100kΩ)
优点:功耗低
缺点:上升沿慢,易受干扰
I2C标准推荐值:
-
标准模式(100kHz):>1.5kΩ
-
快速模式(400kHz):>1.0kΩ
-
快速模式+(1MHz):>0.5kΩ
2) 内部上拉 vs 外部上拉
内部上拉优缺点:
+ 优点:节省PCB空间,成本低
- 缺点:电阻值固定(通常30-50kΩ),不适合高速应用
外部上拉优缺点:
+ 优点:电阻值可精确选择,适合高速应用
- 缺点:占用PCB空间,增加成本
cpp
// 内部上拉使用
GPIO_InitStruct.Pull = GPIO_PULLUP; // 启用内部上拉
// 外部上拉使用
GPIO_InitStruct.Pull = GPIO_NOPULL; // 禁用内部上拉
// 在PCB上添加外部上拉电阻
3) 5V耐受引脚
对于STM32F1系列:
所有IO都是5V耐受
对于STM32F4/F7/H7系列:
- 标记为"FT"或"FTf"的引脚是5V耐受 - 其他引脚只能承受3.3V
操作注意点:
cpp
// STM32的5V耐受引脚(FT)
void FiveVolt_Tolerant_Pins(void)
{
// 电平转换应用必须使用5V耐受引脚
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
// 配合外部上拉到5V,实现3.3V↔5V电平转换
}