0%

4-2OLED显示屏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"


int main(void)
{
OLED_Init();
OLED_ShowChar(1,1,'A');
OLED_ShowString(1,3,"Hello World!");
OLED_ShowNum(2,1,12345,5);
OLED_ShowSignedNum(2,7,-66,2);
OLED_ShowHexNum(3,1,0xAA55,4);
OLED_ShowBinNum(4,1,0xAA55,16);
//OLED_Clear();
}

为什么在OLED(I2C接口)驱动中,推挽输出模式(PP)能正常工作,而开漏输出模式(OD)不能,即使I2C协议标准要求开漏输出?

因为OLED模块内部可能没有上拉电阻,或者上拉电阻的阻值过大

1. I2C协议与开漏输出的标准配置

  • 理论上:I2C总线是一个多主多从、双向、半双工的串行总线。它通过两条线通信:

    • SDA(串行数据线):传输数据。

    • SCL(串行时钟线):提供时钟信号。

  • 开漏输出(Open-Drain)的原理

    • 在OD模式下,GPIO引脚内部的MOSFET只能将线路拉低到地(GND),或者高阻态释放

    • 无法主动输出高电平(VCC)

    • 线路上的高电平必须依靠外部上拉电阻将电压拉至VCC。

  • 为什么I2C标准要求开漏输出?

    • 防止总线冲突:如果多个设备同时向总线输出,一个输出高(推挽),一个输出低(开漏),会造成短路。开漏模式只能拉低,不能主动拉高,避免了电源间的直接冲突,实现了“线与”功能。

    • 电平兼容:不同设备的供电电压可能不同(如3.3V和5V)。只要上拉电阻接到合适的电压,开漏模式可以方便地进行电平转换。

    • 总线可以双向通信:SDA线需要被主机和从机共同驱动。开漏模式使得任何一方都可以安全地拉低线路。

2. 你的实际情况:为什么OD不工作,PP却工作?

  • OD模式不工作(无显示)

    • 当你的PB8/PB9配置为开漏输出时,单片机只能将SDA/SCL线拉低。

    • 当单片机释放总线(输出高电平)时,引脚处于高阻态。此时,线路上的电压完全依赖于外部上拉电阻

    • 如果你的OLED模块没有集成上拉电阻,或者电阻值非常大(例如>100kΩ),那么上拉能力极弱。 导致的结果是:

      1. 时钟线SCL无法上升到稳定的高电平:时钟信号失真,OLED无法正确识别时钟边沿。

      2. 数据线SDA的高电平建立缓慢:在高速通信下,电平还没升到逻辑“1”就被采样了,导致数据错误。

      3. 总体表现:I2C通信失败,初始化或数据传输都无法完成,屏幕自然没有任何显示。用逻辑分析仪或示波器看,会看到波形“爬坡”非常缓慢,幅值不足。

  • PP模式正常工作

    • 当配置为推挽输出时,GPIO内部有PMOS和NMOS两个MOSFET。

    • 输出高电平时,PMOS导通,直接连接到VDD(3.3V),主动驱动线路为高电平。

    • 输出低电平时,NMOS导通,将线路拉低到地。

    • 在这种情况下,即使总线上没有外部上拉电阻,单片机也能靠自身强力输出稳定的高电平和低电平,保证了通信波形的完整性,因此OLED可以正常初始化和显示。

结论:根本原因是你使用的硬件电路(单片机板+OLED模块)组合缺少了I2C总线必需的外部上拉电阻

解决方案(从优到次)

  1. 最佳实践 - 添加上拉电阻并改回OD模式

    • 在 PB8(SCL)和 PB9(SDA) 各自连接到 VCC(3.3V) 的线上,添加一个4.7kΩ - 10kΩ的电阻

    • 将GPIO模式改回 GPIO_MODE_OUTPUT_OD (开漏输出)。

    • 这是最标准、最安全、最兼容I2C协议的做法。它允许总线被多个设备驱动,也符合电平规范。

  2. 临时方案 - 继续使用PP模式(不推荐长期使用)

    • 如果你只是单个主机驱动单个OLED从机,并且没有其他设备挂载到这两根线上,推挽模式在短期内可以“凑合用”

    • 严重缺点

      • 破坏了I2C的“线与”特性,不能再挂载其他I2C设备,否则有短路风险。

      • 如果OLED模块内部有弱上拉,可能会形成电流冲突,长期可能损坏器件。

      • 代码移植到标准硬件上可能反而出问题。