【免费下载链接】TobudOS
TobudOS 是面向物联网领域开发的实时操作系统,早期版本基于腾讯自研的物联网操作系统TencentOS Tiny,2020年由腾讯捐赠到开放原子开源基金会进行孵化,2023年正式更名为TobudOS,TobudOS具有低功耗,低资源占用,模块化,安全可靠等特点,可有效提升物联网终端产品开发效率,提供精简的 RTOS 内核,内核组件可裁剪可配置,可快速移植到多种主流 MCU (如 STM32 全系列) 及模组芯片上。而且,基于 RTOS 内核提供了丰富的物联网组件,内部集成主流物联网协议栈(如 CoAP/MQTT/TLS/DTLS/LoRaWAN/NB-IoT 等),可助力物联网终端设备及业务快速接入物联网云平台。
本文基于 TobudOS 仓库中集成的 MicroPython 组件(components/language/micropython),讲解如何用纯 Python 代码驱动 SSD1306 OLED 显示屏:涵盖硬件 SPI、软件 SPI(SoftSPI)、I2C 三种接口的完整接线与初始化代码,poweron/contrast/invert等基础控制函数,以及继承自framebuf.FrameBuffer的全部图形原语。读完本文,你可以把一块 128x64(或其他尺寸)的 SSD1306 模块接到基于 TobudOS MicroPython 的 IoT 终端上,并绘制文字、线条、矩形和位块转移(blit)等图形。
一、SSD1306 模块概述:接口与常见规格
SSD1306 是一款常用于 IoT 终端小屏显示的 OLED 驱动芯片,具有两类物理接口:SPI和I2C(来源见 tutorial 文档)。常见规格包括:
- 尺寸:128x64、128x32、72x40、64x48;
- 颜色:白、黄、蓝、黄+蓝双色;
- 接口:SPI(硬/软)或 I2C。
TobudOS 仓库中对应的驱动是 ssd1306.py,它同时提供SSD1306_SPI与SSD1306_I2C两个类,二者共享同一个基类SSD1306(framebuf.FrameBuffer)。由于基类继承自 MicroPython 的framebuf.FrameBuffer,所以 OLED 天然获得fill、pixel、line、text、blit等图形原语支持——这是下文图形部分的能力来源。
二、硬件 SPI 接口接入
以教程中的 ESP8266 引脚为例(不同开发板引脚请对照原理图替换):
from machine import Pin, SPI import ssd1306 hspi = SPI(1) # sck=14 (scl), mosi=13 (sda), miso=12 (unused) dc = Pin(4) # data/command rst = Pin(5) # reset cs = Pin(15) # chip select, some modules do not have a pin for this display = ssd1306.SSD1306_SPI(128, 64, hspi, dc, rst, cs)要点说明:
SPI(1)使用硬件 SPI 外设;miso引脚在此场景中不接(显示屏是单向写入设备);- 三根控制线必不可少:DC(data/command)区分命令与数据、RST(reset)硬件复位、CS(chip select)片选。注释特别提示:部分模块没有 CS 引脚,此时需按模块电路处理;
- 构造函数参数依次为
宽度, 高度, spi对象, dc, rst, cs,与 ssd1306.py 中SSD1306_SPI.__init__(self, width, height, spi, dc, res, cs, external_vcc=False)的签名一致,末位external_vcc默认False(板载升压电路供电)。
初始化完成后,第一行打印 Hello World:
display.text('Hello, World!', 0, 0, 1) display.show()三、软件 SPI(SoftSPI)接入
当硬件 SPI 外设被占用、或引脚不在硬件 SPI 复用脚上时,可用软件模拟的SoftSPI:
from machine import Pin, SoftSPI import ssd1306 spi = SoftSPI(baudrate=500000, polarity=1, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12)) dc = Pin(4) # data/command rst = Pin(5) # reset cs = Pin(15) # chip select, some modules do not have a pin for this display = ssd1306.SSD1306_SPI(128, 64, spi, dc, rst, cs)与硬件 SPI 的区别仅在spi对象的构造方式:SoftSPI允许任意引脚、可配置baudrate(示例取 500kHz)与polarity/phase时序。后续用法(text/show等)完全相同。
从源码结构看(SSD1306_SPI),驱动内部每次发送命令或数据前都会调用self.spi.init(baudrate=self.rate, polarity=0, phase=0)重新初始化 SPI,速率取10 * 1024 * 1024(约 10MHz);polarity=1的 SoftSPI 构造参数只影响首帧,驱动随后会统一改成 mode 0(polarity=0, phase=0)来匹配 SSD1306 的数据手册时序。
四、I2C 接口接入
I2C 是接线最简的方式,只需 SDA/SCL 两根线:
from machine import Pin, I2C import ssd1306 # using default address 0x3C i2c = I2C(sda=Pin(4), scl=Pin(5)) display = ssd1306.SSD1306_I2C(128, 64, i2c)要点:
- 默认器件地址为0x3C,与 SSD1306_I2C 构造函数 的签名
SSD1306_I2C(self, width, height, i2c, addr=0x3C, external_vcc=False)一致;如果你的模块 A0 脚接在高电平上,可以推断需要改用addr=0x3D参数传入; external_vcc参数控制芯片内部电荷泵配置(源码中SET_CHARGE_PUMP与SET_PRECHARGE依此取不同值,见下文源码解析)。
五、基础控制函数:电源、对比度、反转、旋转
教程列出的基础控制函数,全部直接映射到 SSD1306 的控制寄存器:
display.poweroff() # power off the display, pixels persist in memory display.poweron() # power on the display, pixels redrawn display.contrast(0) # dim display.contrast(255) # bright display.invert(1) # display inverted display.invert(0) # display normal display.rotate(True) # rotate 180 degrees display.rotate(False) # rotate 0 degrees display.show() # write the contents of the FrameBuffer to display memory这些函数的语义(见 ssd1306.py 第 78-93 行):
| 函数 | 发送的寄存器命令 | 说明 |
|---|---|---|
poweroff() | SET_DISP(0xAE) | 关显示,GRAM 内容仍保留在显存中 |
poweron() | SET_DISP \| 0x01 | 开显示,像素按 RAM 内容重绘 |
contrast(0~255) | SET_CONTRAST(0x81)+ 数值 | 调节亮度 |
invert(1/0) | SET_NORM_INV(0xA6) | 黑白反转 |
rotate(True/False) | SET_COM_OUT_DIR+SET_SEG_REMAP | 180 度旋转 |
show() | 列/页地址 + 整帧数据 | 把帧缓冲写入显示屏 GRAM |
注意poweroff并不断电,GRAM 中画面仍在;重新poweron后画面原样恢复,这是 OLED 快速消隐的常用手法。
六、FrameBuffer 图形原语
因为SSD1306继承自framebuf.FrameBuffer(1-bitMONO_VLSB模式),可以直接调用完整的图形原语。以下示例以 128x64 屏为准(来源见 ssd1306.rst 文档):
display.fill(0) # fill entire screen with colour=0 display.pixel(0, 10) # get pixel at x=0, y=10 display.pixel(0, 10, 1) # set pixel at x=0, y=10 to colour=1 display.hline(0, 8, 4, 1) # draw horizontal line x=0, y=8, width=4, colour=1 display.vline(0, 8, 4, 1) # draw vertical line x=0, y=8, height=4, colour=1 display.line(0, 0, 127, 63, 1) # draw a line from 0,0 to 127,63 display.rect(10, 10, 107, 43, 1) # draw a rectangle outline 10,10 to 117,53, colour=1 display.fill_rect(10, 10, 107, 43, 1) # draw a solid rectangle 10,10 to 117,53, colour=1 display.text('Hello World', 0, 0, 1) # draw some text at x=0, y=0, colour=1 display.scroll(20, 0) # scroll 20 pixels to the right所有图元参数中,颜色取0或1(单色屏的黑色/白色,若已invert(1)则视觉相反)。scroll(dx, dy)为整屏平移。
位块转移(blit):叠加另一个 FrameBuffer
可以把任意小尺寸位图(图标、汉字点阵等)先在独立FrameBuffer里画好,再用blit叠加到屏幕上:
# draw another FrameBuffer on top of the current one at the given coordinates import framebuf fbuf = framebuf.FrameBuffer(bytearray(8 * 8 * 1), 8, 8, framebuf.MONO_VLSB) fbuf.line(0, 0, 7, 7, 1) display.blit(fbuf, 10, 10, 0) # draw on top at x=10, y=10, key=0 display.show()说明:bytearray(8 * 8 * 1)是 8x8 单色位图所需字节数(每像素 1 bit);blit第三个参数key=0为透明键值(本例全绘制所以键值无实际作用);改动必须最后show()才会刷到屏幕。
七、完整示例:绘制 MicroPython Logo 并显示文字
把以上原语组合起来,就是文档给出的完整启动画面示例:
display.fill(0) display.fill_rect(0, 0, 32, 32, 1) display.fill_rect(2, 2, 28, 28, 0) display.vline(9, 8, 22, 1) display.vline(16, 2, 22, 1) display.vline(23, 8, 22, 1) display.fill_rect(26, 24, 2, 4, 1) display.text('MicroPython', 40, 0, 1) display.text('SSD1306', 40, 12, 1) display.text('OLED 128x64', 40, 24, 1) display.show()效果:屏幕左上角 32x32 区域绘出 MicroPython 的“M”形 Logo(白色外框 + 内凹 + 三条竖笔 + 右下角点缀),右侧三行文字分别标注 “MicroPython”“SSD1306”“OLED 128x64”。
八、源码解析:ssd1306 驱动的内部实现
理解 drivers/display/ssd1306.py 可以让你在非标准尺寸模块、异常现象排查时更有底。
1)寄存器定义(第 8-25 行)
驱动用const()定义了 SSD1306 常用命令寄存器:SET_CONTRAST(0x81)、SET_ENTIRE_ON(0xA4)、SET_NORM_INV(0xA6)、SET_DISP(0xAE)、SET_MEM_ADDR(0x20)、SET_COL_ADDR(0x21)、SET_PAGE_ADDR(0x22)、SET_MUX_RATIO(0xA8)、SET_COM_PIN_CFG(0xDA)、SET_PRECHARGE(0xD9)、SET_CHARGE_PUMP(0x8D) 等,与基础函数一节中各 API 一一对应。
2)初始化序列init_display(第 39-76 行)
构造函数按固定顺序下发约 30 条命令:先关显示并设置水平寻址模式(SET_MEM_ADDR, 0x00),再配置行列映射(SET_SEG_REMAP | 0x01使列 127 映射到 SEG0,SET_COM_OUT_DIR | 0x08反向扫描)、时序(SET_DISP_CLK_DIV = 0x80、SET_PRECHARGE、SET_VCOM_DESEL = 0x30即 0.83*Vcc)、对比度上限(0xFF)、最后开显示并清屏刷新。其中两个参数依赖硬件:
SET_COM_PIN_CFG:当width > 2 * height时取0x02(非复用 COM 引脚,典型 128x32 长条屏),否则取0x12(含引脚复用,典型 128x64 方屏)——这解释了为什么同一驱动能覆盖 128x64 / 128x32 等不同长宽比模块;SET_PRECHARGE与SET_CHARGE_PUMP依external_vcc区分:外接电源时预充电取0x22、电荷泵取0x10(关闭内部泵),默认板载供电则取0xF1与0x14(开启内部升压泵)。
3)show()的窄屏居中处理(第 95-109 行)
show()先把列地址设为0 ~ width-1;若width != 128(如 64x48、72x40 屏),驱动会计算col_offset = (128 - width) // 2,把列窗口整体右移居中后再写入整帧。所以小尺寸模块无需自己换算 X 起点,画面自动水平居中。
4)I2C 传输细节(第 112-127 行)
- 写命令:先构造 2 字节缓冲
[0x80, cmd](Co=1, D/C#=0控制字节),经i2c.writeto(addr, temp)发出; - 写整帧数据:控制字节为
0x40(Co=0, D/C#=1),并通过i2c.writevto(addr, write_list)一次性把控制字节与整帧buffer(height // 8 * width字节)拼包传输,避免逐字节写带来的性能损耗。
5)SPI 初始化中的硬复位(第 130-147 行)
SSD1306_SPI构造时会依次把 DC/RST/CS 初始化为输出,然后执行拉高 RST → 延时 1ms → 拉低 RST → 延时 10ms → 拉高 RST的硬件复位时序,再进入init_display。如果你换用poweroff/poweron无法恢复显示,可参考此复位序列手动操作 RST 脚。
九、在 TobudOS MicroPython 中启用该驱动的前提
该驱动运行依赖三个 MicroPython 模块:framebuf、machine.SPI/SoftSPI、machine.I2C。从 TobudOS 的移植层 mpconfigport.h 可见它们均以宏开关形式裁剪:
MP_USING_FRAMEBUF控制MICROPY_PY_FRAMEBUF(mpconfigport.h 第 148-150 行)——驱动首行import framebuf依赖它;MP_USING_MACHINE_SPI同时控制MICROPY_PY_MACHINE_SPI与MICROPY_PY_MACHINE_SOFTSPI(第 108-111 行);MP_USING_MACHINE_I2C同时控制硬件与软 I2C(第 112-115 行)。
因此实际使用时的操作路径是:在板级配置(mpconfigboard.h,见 port 目录)中确认上述宏已启用 → 把 ssd1306.py 复制到板子文件系统(flash)或编译进镜像 → 按本文第二至四节选择接口接线 → 按第五至七节绘图。同目录还有 lcd160cr.py 等其它显示驱动可作参考。
十、常见问题速查
- 屏幕全黑/全亮:先核对
external_vcc是否与板子实际供电方式一致(电荷泵配置不同会导致亮度异常或完全不亮);I2C 模块确认地址 0x3C/0x3D。 - 画面上下或左右颠倒:用
display.rotate(True)翻转 180 度;方向由SET_COM_OUT_DIR/SET_SEG_REMAP控制(rotate 实现)。 - 128x32 长条屏显示位置怪异:
SET_COM_PIN_CFG已按width > 2 * height自动选择引脚复用模式,若仍异常可检查模块 COM 引脚是否短接(模块上的短接桥需与屏型匹配)。 - SoftSPI 卡顿:软件 SPI 占 CPU,
baudrate不宜盲目拉高;本驱动内部会按约 10MHz 重新初始化 SPI 时序(mode 0)。
本文全部内容均可在当前仓库中对照验证:教程文档位于 ssd1306.rst,教程索引见 tutorial/index.rst,驱动实现见 drivers/display/ssd1306.py。
【免费下载链接】TobudOS
TobudOS 是面向物联网领域开发的实时操作系统,早期版本基于腾讯自研的物联网操作系统TencentOS Tiny,2020年由腾讯捐赠到开放原子开源基金会进行孵化,2023年正式更名为TobudOS,TobudOS具有低功耗,低资源占用,模块化,安全可靠等特点,可有效提升物联网终端产品开发效率,提供精简的 RTOS 内核,内核组件可裁剪可配置,可快速移植到多种主流 MCU (如 STM32 全系列) 及模组芯片上。而且,基于 RTOS 内核提供了丰富的物联网组件,内部集成主流物联网协议栈(如 CoAP/MQTT/TLS/DTLS/LoRaWAN/NB-IoT 等),可助力物联网终端设备及业务快速接入物联网云平台。
相关推荐
Luma.OLED:Python驱动SSD1306等OLED显示屏的神器
Luma.OLED:Python驱动SSD1306等OLED显示屏的神器 项目基础介绍 Luma.OLED 是一个专为Python 3设计的库,用于驱动包括SS
嵌入式物联网硬件开发驱动开发OpenMMLab Playground实战教程:用MMDet+SAM实现零代码图像分割
OpenMMLab Playground实战教程:用MMDet+SAM实现零代码图像分割 OpenMMLab Playground是一个集成了MMDet(目标检
示例工程GitHub_Trending/aig/ai-guide 小程序开发:TRAE 框架快速构建 AI 应用
GitHub_Trending/aig/ai guide 小程序开发:TRAE 框架快速构建 AI 应用 GitHub_Trending/aig/ai guid
文档教程知识库人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考