2、硬件连接与初始化:树莓派/ESP32与OLED接线图、电源去耦电容布局、初始化序列代码实现
好,咱们进入第二章。这一章是真正动手前的最后一道理论关。说白了,就是要把屏幕和主控芯片「焊」在一起,然后让它「亮」起来。
我个人习惯,每次做新项目,第一步不是写代码,而是先画接线图。为什么?因为硬件一旦焊错,轻则屏幕不亮,重则烧芯片。我当年刚入行时,就曾把OLED的VCC和GND接反了,屏幕当场冒烟——嗯,那味道至今难忘。
2.1 树莓派与OLED接线图
树莓派用的是3.3V逻辑电平,而大部分0.96寸OLED模块也是3.3V供电。所以两者可以直接对接,不需要电平转换。这一点很省心。
我常用的接线方案是这样的(以树莓派4B为例):
| OLED引脚 | 树莓派引脚(BCM编号) | 物理引脚位置 | 说明 |
|---|---|---|---|
| VCC | 3.3V | Pin 1 | 供电,千万别接5V |
| GND | GND | Pin 6 | 共地 |
| SCL | SCL (GPIO 3) | Pin 5 | I2C时钟线 |
| SDA | SDA (GPIO 2) | Pin 3 | I2C数据线 |
gpio readall 命令确认一下。我每次换板子都会跑一遍这个命令,避免低级错误。
2.2 ESP32与OLED接线图
ESP32的情况稍微复杂一点。它也是3.3V逻辑,但有些开发板的I2C引脚默认不是GPIO 21和22。你想想看,如果买了个便宜板子,引脚定义和标准版不一样,那就尴尬了。
我建议用以下标准配置:
| OLED引脚 | ESP32引脚 | 说明 |
|---|---|---|
| VCC | 3.3V | 供电 |
| GND | GND | 共地 |
| SCL | GPIO 22 | I2C时钟 |
| SDA | GPIO 21 | I2C数据 |
2.3 电源去耦电容布局
这个知识点,很多初学者会忽略。但我要说,它真的很重要。
OLED屏幕在刷新时,电流会瞬间变化。如果电源纹波太大,屏幕会出现闪烁、花屏,甚至初始化失败。我遇到过最离谱的一次,屏幕死活不亮,查了半天,最后发现是电源线太长,寄生电感太大导致的。
去耦电容的布局原则很简单:
- 电容要靠近OLED的VCC和GND引脚,越近越好
- 典型值:10µF电解电容 + 0.1µF陶瓷电容并联
- 电解电容滤低频纹波,陶瓷电容滤高频噪声
如果你用的是面包板,建议在面包板上的VCC和GND之间直接插一个10µF电容。如果是自己画PCB,那就在OLED模块的背面焊两个电容,紧贴引脚。
2.4 初始化序列代码实现
好了,硬件接好了,电容也焊上了。接下来就是让屏幕「说话」。
OLED的初始化,说白了就是给它发一串命令。这些命令告诉屏幕:怎么扫描、亮度多少、要不要翻转等等。不同的OLED驱动芯片(SSD1306、SH1106等),初始化序列略有不同。
我以最常用的SSD1306为例,给出初始化代码。这段代码我用了好几年,在树莓派和ESP32上都验证过。
2.4.1 树莓派(Python)初始化代码
import smbus
import time
# I2C地址,通常是0x3C或0x3D
OLED_ADDR = 0x3C
# 初始化I2C总线
bus = smbus.SMBus(1) # 树莓派3B+/4B用总线1
def write_cmd(cmd):
"""发送命令到OLED"""
bus.write_byte_data(OLED_ADDR, 0x00, cmd)
def write_data(data):
"""发送数据到OLED"""
bus.write_byte_data(OLED_ADDR, 0x40, data)
def oled_init():
"""SSD1306初始化序列"""
# 关闭显示
write_cmd(0xAE)
# 设置显示偏移
write_cmd(0xD3)
write_cmd(0x00)
# 设置起始行地址
write_cmd(0x40)
# 设置段重映射(左右翻转)
write_cmd(0xA1)
# 设置COM扫描方向(上下翻转)
write_cmd(0xC8)
# 设置COM引脚硬件配置
write_cmd(0xDA)
write_cmd(0x12)
# 设置对比度
write_cmd(0x81)
write_cmd(0x7F)
# 关闭整个显示(维持RAM内容)
write_cmd(0xA4)
# 设置显示模式(正常)
write_cmd(0xA6)
# 设置振荡频率
write_cmd(0xD5)
write_cmd(0x80)
# 设置预充电周期
write_cmd(0xD9)
write_cmd(0xF1)
# 设置VCOMH电压
write_cmd(0xDB)
write_cmd(0x40)
# 开启电荷泵
write_cmd(0x8D)
write_cmd(0x14)
# 开启显示
write_cmd(0xAF)
print("OLED初始化完成!")
# 调用初始化
oled_init()
i2cdetect -y 1 扫描一下,看看地址到底是哪个。
2.4.2 ESP32(MicroPython)初始化代码
from machine import Pin, I2C
import time
# 初始化I2C
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
# OLED地址
OLED_ADDR = 0x3C
def write_cmd(cmd):
"""发送命令"""
i2c.writeto(OLED_ADDR, bytearray([0x00, cmd]))
def write_data(data):
"""发送数据"""
i2c.writeto(OLED_ADDR, bytearray([0x40, data]))
def oled_init():
"""SSD1306初始化序列(和树莓派版本一致)"""
write_cmd(0xAE) # 关闭显示
write_cmd(0xD3) # 设置显示偏移
write_cmd(0x00)
write_cmd(0x40) # 设置起始行
write_cmd(0xA1) # 段重映射
write_cmd(0xC8) # COM扫描方向
write_cmd(0xDA) # COM引脚配置
write_cmd(0x12)
write_cmd(0x81) # 对比度
write_cmd(0x7F)
write_cmd(0xA4) # 全局显示
write_cmd(0xA6) # 正常显示
write_cmd(0xD5) # 振荡频率
write_cmd(0x80)
write_cmd(0xD9) # 预充电
write_cmd(0xF1)
write_cmd(0xDB) # VCOMH
write_cmd(0x40)
write_cmd(0x8D) # 电荷泵
write_cmd(0x14)
write_cmd(0xAF) # 开启显示
print("ESP32 OLED初始化完成!")
oled_init()
2.5 初始化序列的几点说明
你可能会问:为什么初始化序列要发这么多命令?少发几个行不行?
嗯,理论上可以。但你会发现,少发几个命令后,屏幕可能显示不正常。比如,如果不设置电荷泵(0x8D, 0x14),屏幕根本不会亮。如果不设置对比度,显示可能很暗。
我个人习惯,把初始化序列封装成一个函数,每次上电都调用一次。这样即使屏幕因为干扰而「死机」,也能通过重新初始化来恢复。
2.6 验证初始化是否成功
代码写完了,怎么知道屏幕初始化成功了呢?
最简单的方法:在初始化之后,往屏幕的某个位置写一个像素点。如果那个点亮了,说明初始化成功。
# 在屏幕左上角画一个像素点
def draw_pixel(x, y):
# 计算页地址和列地址
page = y // 8
col = x
# 设置页地址
write_cmd(0xB0 + page)
# 设置列地址(低4位和高4位)
write_cmd(0x00 + (col & 0x0F))
write_cmd(0x10 + ((col >> 4) & 0x0F))
# 写入数据(点亮该列的第一个像素)
write_data(0x01)
# 在(0,0)位置画一个点
draw_pixel(0, 0)
如果屏幕左上角出现了一个亮点,恭喜你,硬件连接和初始化都成功了!
如果没亮,别急。先检查电源,再检查I2C地址,最后检查接线。我遇到过最奇葩的问题,是OLED模块的I2C地址被焊盘上的锡短路改变了。嗯,有时候硬件问题就是这么玄学。
好了,这一章的内容就到这里。下一章,我们会真正开始写UI——让屏幕显示文字、数字,甚至画一个血压计的波形图。