Grove OLED 1.12‘ SH1107显示屏:SPI/I2C双模驱动与嵌入式显示实战

发布时间:2026/8/2 16:11:53

Grove OLED 1.12‘ SH1107显示屏:SPI/I2C双模驱动与嵌入式显示实战 1. 项目概述一块能说会道的“小窗口”如果你玩过Arduino、树莓派或者ESP32这类开发板大概率见过那种小小的、能显示几行文字或简单图形的屏幕。今天要聊的这块Grove - OLED 显示屏 1.12 (SH1107) V3.0就是这类屏幕中一个非常经典且实用的型号。它不像手机屏幕那样色彩斑斓但胜在结构简单、功耗极低、接口灵活特别适合嵌入到各种需要显示状态、数据或简单交互的电子项目中比如智能家居的温湿度显示、迷你气象站、或者一个桌面电子时钟。这块屏幕的核心是一块1.12英寸的OLED面板驱动芯片是SH1107。你可能听过更常见的SSD1306SH1107可以看作是它的“兄弟”驱动逻辑相似但分辨率更高一些128x128像素显示面积也稍大。V3.0版本最大的亮点在于其接口的“全能性”它同时支持SPI和I2C两种通信协议并且供电电压兼容3.3V和5V。这意味着无论你手头是古老的5V Arduino Uno还是现代的3.3V ESP32甚至是树莓派几乎都能即插即用省去了电平转换的麻烦。Grove接口的加持更是让连接变得傻瓜化一个四芯线缆搞定电源、地和数据线极大降低了硬件接线的门槛和出错概率。对于开发者尤其是嵌入式入门者和创客来说这块屏幕的价值在于它提供了一个极其友好的显示解决方案。你不用去研究复杂的LCD时序不用焊接排针甚至不用太担心电源问题。你的精力可以完全集中在“让屏幕显示什么”这个核心创意上。无论是想显示传感器读数、系统状态还是滚动播放一段文字它都能成为一个可靠的信息输出“窗口”。2. 核心硬件与接口深度解析2.1 OLED屏幕与SH1107驱动芯片首先得明白OLED和普通LCD的区别。LCD液晶显示屏本身不发光需要背光灯照亮所以即使显示全黑背光也在耗电。而OLED有机发光二极管的每个像素点都是独立发光的显示黑色时像素点直接关闭因此可以实现极高的对比度和真正的黑色功耗也更低特别适合电池供电的设备。这块1.12英寸的屏幕就是单色通常是蓝白或黄白OLED显示效果清晰锐利可视角度大。驱动芯片SH1107是屏幕的“大脑”。它内部集成了显示RAM显存你通过SPI或I2C接口发送的命令和数据实际上就是写入了这片RAM。SH1107会按照固定的扫描频率自动将RAM中的数据转换成控制每个OLED像素点亮灭的电压信号。它支持的最大分辨率是128x128但请注意这块Grove屏幕的物理像素是128x128而更常见的0.96寸OLEDSSD1306驱动通常是128x64。多出一倍的行数意味着你可以显示更多行文字或更复杂的图形。SH1107与SSD1306的指令集高度兼容这意味着为SSD1306编写的绝大多数库例如Arduino的Adafruit_SSD1306或U8g2经过简单配置修改分辨率、I2C地址等后通常可以直接用于SH1107这是其生态友好的一个重要体现。2.2 SPI vs I2C两种通信协议的选择与配置这是本模块最核心的部分。SPI和I2C是嵌入式领域两种最常用的短距离、板级同步串行通信协议。这块屏幕的V3.0版本通过板载的一个微型拨码开关或零欧姆电阻焊点来切换这两种模式。I2C模式特点仅需两根信号线——SDA数据线和SCL时钟线所有设备都挂在这两根总线上通过唯一的设备地址来寻址。这是一种多主多从、半双工的总线协议。接线在Grove系统中连接到主控板的I2C接口例如Arduino的A4/SDA, A5/SCL。屏幕的I2C地址通常是0x3C这是SH1107的常见地址但需以实际扫描为准。优点节省引脚布线简单总线上可以挂多个设备如同时连接OLED、温湿度传感器、气压计。缺点通信速度相对较慢标准模式100kbps快速模式400kbps协议中有应答机制软件开销稍大。适用场景对引脚数量敏感、对显示刷新速度要求不高的项目例如数据仪表盘、状态显示器。当你的主控板I2C引脚被占用或需要更高速刷新时就要考虑SPI。注意I2C总线需要上拉电阻。幸运的是这块Grove模块和大多数开发板如Arduino、ESP32的I2C接口通常已经内置了上拉电阻。如果你是自己用MCU的GPIO模拟I2C软件I2C则必须在SDA和SCL线上各接一个4.7kΩ - 10kΩ的电阻到正电源3.3V或5V否则通信无法建立。SPI模式特点通常需要四根线——SCK时钟、MOSI主机输出从机输入即数据线、DC数据/命令选择有时也叫D/C、CS片选低电平有效。这是一种全双工、高速的通信协议。接线需要连接到主控板的硬件SPI接口如Arduino Uno的 13/SCK, 11/MOSI, 10/SS。除了电源线你需要连接SCK, MOSI, DC, CS四根线。RST复位和D/C引脚在某些库中是必需的。优点速度极快轻松达到10Mbps以上协议简单直接无应答机制效率高。缺点占用引脚多至少4个且每个从设备需要独立的片选线CS当连接多个SPI设备时布线会复杂。适用场景需要快速刷新、显示动态图形或动画的项目比如迷你游戏机、频谱可视化等。SPI是追求性能的首选。如何选择新手、引脚紧张、多设备优先选I2C。需要高速刷新、动态内容优先选SPI。这块Grove模块的背面有一个很小的拨动开关标有“I2C”和“SPI”用镊子或笔尖拨动即可切换。切换后务必断电再重新上电让屏幕驱动芯片重新初始化以识别新的接口模式。2.3 宽电压供电3.3V/5V与Grove接口便利性V3.0版本的另一个巨大优势是电源兼容性。模块内部集成了高效的电源管理电路能够自动适应3.3V或5V的输入电压。这意味着无需电平转换器你可以直接将5V的Arduino Uno与3.3V的传感器或其他模块混用在这块屏幕上而不用担心烧毁屏幕。供电灵活无论你的主控系统是3.3V逻辑如ESP32、STM32F103C8T6“蓝板”还是5V逻辑Arduino Uno、Mega都可以直接供电。Grove接口是Seeed Studio推广的一套标准化连接器系统它把电源VCC、地GND和两条信号线封装在一个四针接口里。对于这块屏幕在I2C模式下这四条线分别是VCC, GND, SDA, SCL。在SPI模式下这四条线分别是VCC, GND, SDA此时用作MOSI, SCL此时用作SCK。而DC和CS信号则通过模块上的两个单独的排针孔引出通常标记为DC和CS你需要用杜邦线另行连接到主控板。RST复位引脚也可能作为一个排针孔存在。使用配套的Grove线缆可以防止电源反接防呆设计并且连接牢固可靠非常适合原型开发和教育场景。3. 软件驱动与库函数实战要让屏幕亮起来并显示内容你需要借助软件库。下面以最流行的Arduino平台为例讲解两种最常用库的用法。3.1 使用U8g2库全能型选择U8g2是一个功能极其强大的单色图形库支持数百种显示控制器包括SH1107。它的优势在于统一的API一旦学会可以驱动几乎所有单色屏。安装在Arduino IDE中通过“库管理器”搜索“U8g2”并安装。选择构造函数这是最关键的一步。你需要根据你的连接方式SPI/I2C和具体引脚在代码开头实例化一个U8G2对象。I2C连接示例#include U8g2lib.h #include Wire.h // I2C库 // 对于Grove OLED 1.12‘ (SH1107) I2C连接 U8G2_SH1107_128X128_F_HW_I2C u8g2(U8G2_R0, /* reset*/ U8X8_PIN_NONE); // 参数解释U8G2_R0不旋转复位引脚未接用U8X8_PIN_NONESPI连接示例假设使用Arduino Uno硬件SPI#include U8g2lib.h // 对于SPI连接需要指定DC和CS引脚。假设DC接D9 CS接D10 复位接D8 U8G2_SH1107_128X128_F_4W_HW_SPI u8g2(U8G2_R0, /* cs*/ 10, /* dc*/ 9, /* reset*/ 8);初始化与基本绘图void setup() { u8g2.begin(); // 初始化显示屏 u8g2.setFont(u8g2_font_ncenB08_tr); // 设置字体 u8g2.setDrawColor(1); // 设置绘制颜色1为白色0为黑色 } void loop() { u8g2.clearBuffer(); // 清除内部缓冲区 u8g2.drawStr(0, 20, Hello World!); // 在坐标(0,20)处绘制字符串 u8g2.drawCircle(64, 64, 20, U8G2_DRAW_ALL); // 画一个圆 u8g2.sendBuffer(); // 将缓冲区内容发送到显示屏 delay(1000); }核心概念U8g2采用“缓冲区”机制。所有drawXxx()函数都是在内存中的缓冲区作图最后调用sendBuffer()一次性更新到屏幕。这避免了屏幕闪烁。3.2 使用Adafruit SSD1306/GFX库经典组合Adafruit的库同样非常流行它分为Adafruit_SSD1306驱动层和Adafruit_GFX图形层两个部分。虽然名为SSD1306但通过修改参数可以兼容SH1107。安装安装“Adafruit SSD1306”和“Adafruit GFX Library”。配置与初始化#include Adafruit_GFX.h #include Adafruit_SH110X.h // 注意需要专门支持SH110X的库或使用修改后的SSD1306库 // 对于I2C使用以下构造函数地址0x3C 无复位引脚 Adafruit_SH1107 display(128, 128, Wire, -1); // 宽度高度Wire对象复位引脚-1表示无 // 对于SPI假设DCD9 CSD10 RSTD8 // Adafruit_SH1107 display(128, 128, SPI, D10, D9, D8); void setup() { Serial.begin(9600); if(!display.begin(0x3C, true)) { // I2C地址0x3C Serial.println(F(SH1107 allocation failed)); for(;;); // 死循环初始化失败 } display.display(); // 显示Adafruit的启动logo delay(2000); display.clearDisplay(); display.setTextSize(1); display.setTextColor(SH110X_WHITE); }绘图与显示void loop() { display.clearDisplay(); display.setCursor(0,0); display.println(Hello World!); display.drawCircle(64, 64, 20, SH110X_WHITE); display.display(); // 与U8g2的sendBuffer()作用相同 delay(1000); }Adafruit库的特点API直观但部分高级功能如多种字体可能不如U8g2丰富。它采用“立即模式”思维调用display()后才真正更新屏幕。库的选择建议追求功能全面、字体丰富、跨平台选U8g2。项目简单、喜欢Adafruit生态系统选Adafruit SSD1306/GFX SH1107补丁。对于ESP32等平台可能还有基于ESP-IDF的专用驱动性能优化更好。3.3 字体、图形与动画创建技巧单色屏的显示效果全靠创意。以下是一些实用技巧字体U8g2内置了海量字体u8g2_font_开头。使用setFont()设置。注意字体高度会影响drawStr的y坐标基准通常是字体的基线。getStrWidth()函数可以获取字符串的像素宽度用于居中计算x (displayWidth - u8g2.getStrWidth(text)) / 2。图形除了基本的点、线、圆、矩形U8g2和Adafruit GFX都支持绘制位图。你需要先将图片转换为单色位图数组。可以使用在线工具如LCD Assistant或Python脚本如PIL库进行转换。动画实现动画的关键是“清屏-重绘-显示”循环。避免在loop()中直接使用delay()进行长时间等待这会导致界面卡顿。推荐使用millis()进行非阻塞定时实现平滑的帧刷新。unsigned long previousMillis 0; const long interval 100; // 刷新间隔100ms void loop() { unsigned long currentMillis millis(); if (currentMillis - previousMillis interval) { previousMillis currentMillis; // 更新动画帧并重绘屏幕 updateAnimationFrame(); u8g2.clearBuffer(); drawFrame(); u8g2.sendBuffer(); } // 这里可以执行其他任务 }页面管理对于多页面显示如第一页温度第二页湿度可以定义一个状态变量如int currentPage 0根据变量值在drawFrame()函数中绘制不同的内容通过按键或定时切换currentPage。4. 典型项目应用与实战连接示例理论说得再多不如动手接一个。下面以两个最典型的开发板为例展示完整的连接和代码。4.1 连接Arduino UnoI2C模式这是最简单的连接方式充分利用了Grove的便利性。硬件连接将Grove OLED模块背面的拨码开关拨到I2C一侧。使用Grove四芯线缆一端连接OLED模块另一端连接Arduino Uno的I2C接口通常标记为I2C或A4/A5附近。如果Arduino Uno上没有标准的Grove插座你需要一个Grove转杜邦线模块将线缆的四个引脚分别接到VCC - 5V, GND - GND, SDA - A4, SCL - A5。代码示例使用U8g2库#include U8g2lib.h #include Wire.h U8G2_SH1107_128X128_F_HW_I2C u8g2(U8G2_R0, /* reset*/ U8X8_PIN_NONE); void setup(void) { u8g2.begin(); u8g2.setFont(u8g2_font_ncenB10_tr); } void loop(void) { u8g2.clearBuffer(); u8g2.drawStr(10, 30, Arduino Uno); u8g2.drawStr(10, 60, Grove OLED); u8g2.drawStr(10, 90, I2C Test); u8g2.sendBuffer(); delay(1000); u8g2.clearBuffer(); u8g2.setFont(u8g2_font_7Segments_26x42_mn); u8g2.drawStr(15, 70, 1234); // 绘制大型数字 u8g2.sendBuffer(); delay(1000); }上传与测试将代码上传到Arduino Uno屏幕应该会交替显示文本和大数字。4.2 连接ESP32开发板SPI模式ESP32性能强大引脚多适合用SPI驱动以获得更流畅的体验。硬件连接将OLED模块拨码开关拨到SPI一侧。电源模块VCC连接ESP32的3.3V引脚。地模块GND连接ESP32的GND。SPI信号使用杜邦线连接。模块SCL (SPI CLK) - ESP32的GPIO 18(常用SPI时钟引脚)。模块SDA (SPI MOSI) - ESP32的GPIO 23(常用SPI MOSI引脚)。模块DC - ESP32的任意GPIO例如GPIO 4。模块CS - ESP32的任意GPIO例如GPIO 5。模块RST如有- ESP32的任意GPIO例如GPIO 2或者接3.3V常高如果模块内部已上拉。注意ESP32的默认硬件SPI引脚是VSPICLKGPIO18, MOSIGPIO23, MISOGPIO19。我们这里没有用到MISO。代码示例使用U8g2库硬件SPI#include U8g2lib.h #include SPI.h // 构造函数指定旋转、CS引脚、DC引脚、复位引脚 U8G2_SH1107_128X128_F_4W_HW_SPI u8g2(U8G2_R0, /* cs*/ 5, /* dc*/ 4, /* reset*/ 2); void setup(void) { u8g2.begin(); u8g2.setFont(u8g2_font_unifont_t_chinese2); // 设置中文字体需包含相应字体文件 u8g2.setFontDirection(0); } void loop(void) { u8g2.clearBuffer(); u8g2.drawStr(0, 20, ESP32 SPI驱动); u8g2.drawStr(0, 40, 刷新率测试); // 绘制一个移动的小方块测试刷新速度 static int x 0; u8g2.drawBox(x, 60, 10, 10); u8g2.sendBuffer(); x; if (x 118) x 0; // 去掉delay以最大速度刷新观察方块移动是否流畅 // delay(10); }效果上传代码后你会看到文字和一个快速移动的方块。由于SPI的高速特性动画会非常流畅。你可以尝试注释掉delay(10)观察极限刷新率下的表现。4.3 进阶项目构想掌握了基础显示后可以尝试这些项目网络时钟用ESP32连接Wi-Fi通过NTP协议获取网络时间并显示在OLED上可以加上日期、星期。环境监测站连接DHT11/DHT22温湿度传感器、BMP280气压传感器均可通过I2C在OLED上轮播显示实时环境数据。系统状态监视器用于树莓派通过Python脚本如luma.oled库读取CPU温度、内存使用率、IP地址并显示。迷你游戏利用128x128的分辨率可以制作简单的贪吃蛇、Flappy Bird等像素游戏。5. 常见问题排查与调试心得即使按照教程操作也难免会遇到屏幕不亮、显示乱码、通信失败等问题。下面是我在多年项目中总结的排查清单和心得。5.1 屏幕完全不亮无显示这是最常见的问题请按顺序排查电源检查电压确认用万用表测量模块VCC和GND之间的电压确保在3.3V-5V之间。电压过低3V可能无法启动。电流能力某些微型开发板如某些ESP-01的3.3V引脚输出电流能力有限可能只有100-200mA而OLED在点亮所有像素时瞬时电流可能较大几十mA可能导致电压被拉低。尝试使用外部稳压电源或开发板上电流输出能力更强的引脚如USB直接输入的5V引脚如果模块支持5V。接触不良检查Grove线缆或杜邦线是否插紧特别是GND线。虚焊或接触不良是硬件项目的头号杀手。复位信号检查代码中是否正确地控制了RST引脚。对于SH1107一个完整的复位序列通常是拉低RST 延时 拉高RST。如果硬件上RST引脚悬空而代码中又配置了某个引脚为RST可能会导致初始化失败。如果硬件未连接RST在库的构造函数中应使用U8X8_PIN_NONE或-1来指定无复位引脚。通信协议与模式开关双重确认肉眼再次确认模块背面的拨码开关是否拨到了正确的位置I2C或SPI。这个开关非常小容易看错。软件匹配确保代码中使用的库构造函数与硬件模式完全匹配。用I2C的构造函数去驱动SPI接线的屏幕必然失败。5.2 显示花屏、乱码或内容错位初始化序列问题SH1107和SSD1306的初始化指令大部分相同但可能有细微差别。确保你使用的库明确支持SH1107或者使用了正确的SH1107构造函数。使用为SSD1306写的库而不修改参数可能导致显示错位或乱码。尝试在setup()的begin()函数后增加一个短暂的delay(100)给屏幕足够的初始化时间。缓冲区与显示区域不匹配确认你设置的显示缓冲区大小与屏幕物理分辨率一致。例如对于128x128的屏幕构造函数中的参数必须是128X128。如果误用了128X64的构造函数会导致只有上半屏或下半屏显示正常另一半花屏。SPI模式下的DC引脚DC引脚数据/命令选择是SPI通信的关键。必须在发送命令前拉低发送数据前拉高。如果这个引脚接触不良或配置错误屏幕会收到一堆被误解的指令导致完全乱码。用逻辑分析仪或示波器检查DC引脚的波形是最直接的排查方法。5.3 I2C通信失败找不到设备地址扫描首先运行一个I2C扫描程序确认屏幕是否被MCU识别。// Arduino I2C扫描程序 #include Wire.h void setup() { Wire.begin(); Serial.begin(115200); Serial.println(I2C Scanner ...); } void loop() { byte error, address; int nDevices 0; for(address 1; address 127; address ) { Wire.beginTransmission(address); error Wire.endTransmission(); if (error 0) { Serial.print(I2C device found at address 0x); if (address16) Serial.print(0); Serial.print(address,HEX); Serial.println(); nDevices; } } if (nDevices 0) Serial.println(No I2C devices found); delay(5000); }常见的SH1107地址是0x3C有时是0x3D。如果扫描不到检查接线。上拉电阻这是I2C总线最经典的问题。如果总线上没有上拉电阻约4.7kΩSDA和SCL线无法被拉高到逻辑1通信会失败。测量方法断电状态下用万用表测量SDA和SCL引脚对VCC的电阻。如果电阻在4.7kΩ左右说明已有上拉。如果电阻极大兆欧级则需要外接上拉电阻。总线冲突总线上是否有其他设备地址冲突或者有其他设备如某些传感器在通信时拉低了总线导致死锁可以尝试暂时断开其他I2C设备只连接OLED进行测试。5.4 SPI通信问题无显示或速度慢引脚映射错误确认代码中MOSI、SCK、DC、CS的引脚号与硬件连接完全一致。ESP32等芯片的引脚功能可以重映射但硬件SPI的默认引脚如GPIO 23, 19, 18, 5for VSPI通常最可靠。软件片选CS管理在SPI通信中必须在每次传输前拉低对应从设备的CS引脚传输完成后拉高。如果使用硬件SPI库函数通常会处理CS。但如果使用软件模拟SPI或者多个SPI设备你必须手动控制CS。常见错误是忘记在初始化序列后拉高CS导致屏幕一直处于被选中状态影响其他SPI设备。时钟极性与相位SPI ModeSPI有4种模式CPOL和CPHA的组合。SH1107通常工作在Mode 0CPOL0 CPHA0。绝大多数库如U8g2、Adafruit默认使用Mode 0。如果你自己编写底层SPI驱动务必设置正确。使用不正确的模式会导致数据采样错位显示乱码。实操心得备一个逻辑分析仪一个几十块钱的简易逻辑分析仪配合Sigrok软件是调试I2C/SPI通信的“神器”。它能直观地显示总线上的时钟、数据波形帮你一眼看出是地址不对、应答缺失还是数据错误比串口打印调试信息高效十倍。库的版本很重要不同版本的U8g2或Adafruit库其构造函数或底层驱动可能有变化。如果从网上找的例程不工作检查一下库的版本或者查看库自带的例程Examples。电源去耦在OLED模块的VCC和GND之间靠近引脚处焊接一个10uF的电解电容和一个0.1uF的陶瓷电容可以显著改善因电源纹波导致的显示闪烁或随机复位问题尤其是在使用长导线或面包板连接时。初始化失败后的重试在生产环境或长期运行的项目中可以在代码中加入初始化重试逻辑。如果begin()失败不要立刻死循环可以记录错误、延时几秒后重试提高系统的鲁棒性。

相关新闻