☰
STM32驱动0.96寸OLED:I2C地址、SSD1306初始化与滚动显示实战
2026/10/14 1:08:14 网站建设 项目流程

简介:本资源面向STM32嵌入式初学者与显示驱动开发者,提供基于STM32F103VE的0.96寸OLED(SSD1306)显示、水平滚动及垂直水平滚动示例程序,帮助解决超长文字滚动显示与屏幕多方向滚动效果的实现问题。压缩包共283个文件,约8.22MB,包含42个c源文件、44个h头文件、58个o目标文件及57个crf编译中间文件,另有uvprojx、uvoptx工程文件、hex烧录文件、map映射文件与sct分散加载文件,覆盖STM32F10x标准外设库中TIM、FLASH、RCC、I2C、USART等模块,便于直接编译与二次开发。目前已有1166人学习下载。读者可获取完整Keil工程与可烧录固件,对照源码理解SSD1306命令配置、显存组织与滚动设置流程,快速将滚动显示效果移植到自己的项目中,同时借助中间文件与映射信息排查编译链接问题。

1. 0.96 寸 OLED 屏点亮实录:从 I2C 地址到滚动显示的完整工程

手头这块 0.96 寸 OLED 是嵌入式项目里出镜率极高的显示件,分辨率 128×64,驱动芯片多为 SSD1306 或 SH1106,接口分 I2C 和 SPI 两种。这次拆的是一份基于 STM32F103VE 的示例工程,核心价值在于它把「基础字符显示」和「水平滚动、垂直水平混合滚动」这两块都做成了可编译、可烧录的完整代码,不是那种只给个初始化函数的半成品。对正在用 F103 系列做仪表、小家电面板或者调试信息屏的工程师来说,这份源码能直接省掉翻数据手册配寄存器的时间。它适合两类人:一是刚接触 OLED 驱动、想搞懂 SSD1306 命令集怎么下发的新手;二是已经能点亮屏幕、但滚动效果一直调不对的老手。下面按「硬件怎么接、驱动怎么写、滚动怎么配、坑在哪」的顺序拆开讲。

2. 硬件连接与 I2C 地址确认:先让屏幕有反应

2.1 模块引脚与上拉电阻的取舍

0.96 寸 OLED 模块通常引出四个引脚:VCC、GND、SCL、SDA。VCC 接 3.3V,这点没有商量余地,虽然有些模块标称支持 5V,但 SSD1306 的 IO 电平是 3.3V 逻辑,直接上 5V 的 I2C 总线长期跑会缩短寿命。SCL 和 SDA 分别接 STM32F103VE 的 I2C1 引脚 PB6、PB7,或者用软件模拟 I2C 挂在任意 GPIO 上。

关键在 上拉电阻。很多模块背面已经焊了 4.7kΩ 或 10kΩ 的上拉,这时候你只需要把 SCL、SDA 直接连到单片机引脚即可。但如果你的模块是裸屏转接板,没有上拉,就必须在 SCL 和 SDA 上各加一个 4.7kΩ 电阻到 3.3V。我见过有人省掉上拉,结果 I2C 波形上升沿塌成圆弧,屏幕时亮时不亮,查了半天以为是地址错了。常见做法是:先用万用表量一下模块 SCL 对 VCC 的阻值,如果接近 4.7k 或 10k,说明已有上拉,不用再加。

2.2 扫描 I2C 地址:0x78 还是 0x7A

SSD1306 的 I2C 从机地址由模块上的电阻跳线决定,常见是 0x78(写地址)或 0x7A。注意这里说的是 8 位地址格式,即 7 位地址 0x3C 左移一位。很多新手在代码里填 0x3C,结果 HAL 库的HAL_I2C_Master_Transmit要求传 8 位地址,导致通信失败。

确认方法很简单,写一段扫描代码,遍历 0x00 到 0xFF,看哪个地址有应答:

// I2C 地址扫描,基于 HAL 库 #include "stm32f1xx_hal.h" extern I2C_HandleTypeDef hi2c1; void OLED_ScanI2C(void) { uint8_t addr; for (addr = 1; addr < 128; addr++) { // HAL 库要求传入 8 位地址,这里左移一位 if (HAL_I2C_IsDeviceReady(&hi2c1, addr << 1, 3, 10) == HAL_OK) { printf("Found device at 0x%02X\r\n", addr << 1); } } }

逻辑说明:HAL_I2C_IsDeviceReady的第二个参数是 8 位地址,所以要把 7 位地址左移一位。第三个参数是重试次数,第四个是超时毫秒。如果扫到 0x78 或 0x7A,就说明屏幕在线。参数调整:如果扫描不到,先把超时从 10ms 加到 50ms,再检查上拉和接线。注意,有些模块标称地址是 0x3C,但实际焊的是 0x3D,扫描能帮你省掉猜地址的功夫。

3. SSD1306 初始化与显存映射:把像素点管起来

3.1 初始化命令序列的逐条含义

SSD1306 上电后必须按顺序下发一串命令,才能进入正常显示模式。这份示例工程里的初始化函数大致如下:

// SSD1306 初始化命令序列,I2C 模式 void OLED_Init(void) { uint8_t cmd[] = { 0xAE, // 关闭显示 0xD5, 0x80, // 设置时钟分频因子 0xA8, 0x3F, // 设置多路复用率为 64 0xD3, 0x00, // 设置显示偏移为 0 0x40, // 设置显示起始行 0x8D, 0x14, // 使能电荷泵 0x20, 0x00, // 设置内存寻址模式为水平寻址 0xA1, // 段重映射,左右翻转 0xC8, // 扫描方向,上下翻转 0xDA, 0x12, // 设置 COM 引脚硬件配置 0x81, 0xCF, // 设置对比度 0xD9, 0xF1, // 设置预充电周期 0xDB, 0x40, // 设置 VCOMH 电压 0xA4, // 全局显示开启 0xA6, // 正常显示,非反色 0xAF // 开启显示 }; for (uint8_t i = 0; i < sizeof(cmd); i++) { OLED_WriteCmd(cmd[i]); } }

逻辑说明:0xAE先关显示,避免初始化过程中出现花屏。0xD5设时钟分频,0x80是默认值。0xA8设多路复用率,0x3F 对应 64 行。0x8D后跟0x14是使能内部电荷泵,这一步不能省,否则屏幕不亮。0x20设寻址模式,0x00 是水平寻址,适合连续写显存。0xA1和0xC8控制显示方向,如果屏幕内容上下颠倒,把0xC8改成0xC0。参数调整:对比度0xCF可以根据实际亮度微调,范围 0x00 到 0xFF。

3.2 显存与屏幕像素的对应关系

SSD1306 的显存是 128×64 位,分成 8 页,每页 8 行,共 64 行。每页有 128 个字节,每个字节的 8 个位对应这一页的 8 行。也就是说,显存中的一个字节控制屏幕上一个 8 像素高的竖条。

写显存时,先设置页地址和列地址,然后连续写入数据。水平寻址模式下,写完一个字节后列地址自动加一,写满一行后页地址自动加一。这份工程里的OLED_SetCursor函数就是干这个的:

// 设置光标位置,页地址 0-7,列地址 0-127 void OLED_SetCursor(uint8_t page, uint8_t col) { OLED_WriteCmd(0xB0 + page); // 设置页地址 OLED_WriteCmd(0x10 + ((col >> 4) & 0x0F)); // 列地址高四位 OLED_WriteCmd(0x00 + (col & 0x0F)); // 列地址低四位 }

逻辑说明:0xB0 + page设置页地址,page 范围 0 到 7。列地址分两次发送,高四位和低四位。注意,有些 SH1106 驱动的屏幕列地址有偏移,需要加 2,比如0x10 + ((col + 2) >> 4)。如果你用的是 SH1106 但显示内容整体右移两列,就是这个原因。

4. 水平滚动与垂直水平滚动:命令配置与参数拆解

4.1 水平滚动命令的五个参数

SSD1306 的水平滚动由0x26或0x27命令触发,前者向右滚动,后者向左滚动。命令后面跟五个参数:空字节、起始页、滚动速度、结束页、空字节。示例代码:

// 水平向右滚动,页 0 到页 7,速度等级 0 void OLED_ScrollRight(uint8_t startPage, uint8_t endPage, uint8_t speed) { OLED_WriteCmd(0x26); // 水平向右滚动 OLED_WriteCmd(0x00); // 空字节 OLED_WriteCmd(startPage); // 起始页 OLED_WriteCmd(speed); // 滚动速度,0-7 OLED_WriteCmd(endPage); // 结束页 OLED_WriteCmd(0x00); // 空字节 OLED_WriteCmd(0x2F); // 开启滚动 }

逻辑说明:0x26是向右,0x27是向左。起始页和结束页决定滚动区域,比如只让第 2 到第 5 页滚动,就传 2 和 5。速度参数 0 到 7,0 最快,7 最慢。最后必须发0x2F开启滚动,否则命令不生效。参数调整:如果滚动区域不对,检查起始页和结束页是否在 0 到 7 之间,且起始页小于等于结束页。

4.2 垂直水平混合滚动的命令差异

垂直水平滚动用0x29或0x2A,前者向右上,后者向左上。参数比水平滚动多两个:垂直滚动偏移量和垂直滚动区域。示例:

// 垂直水平向右上滚动 void OLED_ScrollDiagonal(uint8_t startPage, uint8_t endPage, uint8_t speed, uint8_t vOffset) { OLED_WriteCmd(0x29); // 垂直水平向右上滚动 OLED_WriteCmd(0x00); // 空字节 OLED_WriteCmd(startPage); // 起始页 OLED_WriteCmd(speed); // 滚动速度 OLED_WriteCmd(endPage); // 结束页 OLED_WriteCmd(vOffset); // 垂直偏移,0-63 OLED_WriteCmd(0x2F); // 开启滚动 }

逻辑说明:0x29是向右上,0x2A是向左上。垂直偏移量vOffset控制每次滚动时垂直方向移动的行数,范围 0 到 63。注意,垂直水平滚动会同时改变页地址和列地址,所以滚动区域内的显存内容会被循环搬移。如果只想让部分区域滚动,起始页和结束页要设对。参数调整:vOffset设太大滚动会跳变,一般 1 到 3 比较自然。

4.3 滚动启停与显存刷新的配合

滚动开启后,屏幕内容会持续移动,但显存里的数据不会自动更新。如果你在滚动过程中写显存,会出现撕裂或错位。常见做法是:先停止滚动,写显存,再重新开启滚动。

// 停止滚动 void OLED_ScrollStop(void) { OLED_WriteCmd(0x2E); // 停止滚动 }

逻辑说明:0x2E停止滚动,0x2F开启滚动。在写显存前调用OLED_ScrollStop,写完后再调用滚动函数。注意,有些屏幕在停止滚动后需要重新发送滚动命令才能恢复,不能只发0x2F。

5. 避坑排查:I2C 通信、滚动异常与显示偏移

5.1 屏幕不亮,但 I2C 扫描有应答

现象:扫描到 0x78,但屏幕全黑,没有任何显示。原因:初始化序列里电荷泵没使能,或者对比度设得太低。解决:检查0x8D后面是否跟了0x14,这是使能电荷泵的关键命令。如果电荷泵没开,屏幕背光不亮。另外,对比度命令0x81后面的值如果小于 0x10,屏幕会非常暗,调到 0xCF 左右。

5.2 滚动命令发了但屏幕不动

现象:调用滚动函数后,屏幕内容静止。原因:滚动命令后没有发0x2F,或者滚动区域设置错误。解决:确认滚动函数最后有0x2F。另外,起始页和结束页必须在 0 到 7 之间,且起始页不能大于结束页。如果只让第 0 页滚动,结束页也写 0,但这样滚动效果不明显,建议至少两页。

5.3 显示内容整体偏移或上下颠倒

现象:字符显示在错误的位置,或者上下颠倒。原因:段重映射和扫描方向配置不对。解决:0xA1是左右正常,0xA0是左右翻转;0xC8是上下正常,0xC0是上下翻转。如果内容上下颠倒,把0xC8改成0xC0。如果左右偏移,检查列地址是否加了 SH1106 的偏移量 2。

5.4 滚动时出现花屏或撕裂

现象:滚动过程中屏幕出现乱码或撕裂。原因:滚动开启时写显存,或者滚动区域与写显存区域重叠。解决:写显存前先发0x2E停止滚动,写完后再重新开启。另外,滚动区域内的显存不要频繁改写,否则会干扰滚动逻辑。

5.5 I2C 通信偶尔失败

现象:屏幕偶尔不亮,复位后正常。原因:I2C 上拉电阻不足,或者总线电容过大。解决:检查 SCL 和 SDA 的上拉电阻,4.7kΩ 是常用值。如果模块已有上拉,不要再并联。另外,I2C 速率不要设太高,100kHz 或 400kHz 都可以,但 400kHz 对布线要求更高,杜邦线连接时建议降到 100kHz。

6. 进阶技巧:用取模软件生成自定义滚动内容

滚动显示的内容不限于字符,也可以是图标或自定义图案。常见做法是用 PC 端取模软件,比如 PCtoLCD2002,设置好取模方式为「阴码、逐列式、顺向」,生成字节数组,然后写入显存。

// 自定义图案取模数据,128×64 全屏 const uint8_t logo[] = { 0x00, 0x00, 0x00, 0x00, // ... 省略中间数据 }; // 写入全屏图案 void OLED_DrawLogo(const uint8_t *buf) { for (uint8_t page = 0; page < 8; page++) { OLED_SetCursor(page, 0); for (uint8_t col = 0; col < 128; col++) { OLED_WriteData(buf[page * 128 + col]); } } }

逻辑说明:取模数据按页排列,每页 128 字节。OLED_SetCursor设置页和列,然后连续写 128 个字节。参数调整:如果图案显示不全,检查取模软件的「输出格式」是否选了「C51 格式」,以及「取模方式」是否选了「逐列式」。我一般会在写入前先清屏,避免残留数据干扰。

滚动速度的微调也有讲究。速度参数 0 到 7,实际滚动一屏的时间从约 1 秒到 8 秒不等。如果觉得太快,把速度调到 3 或 4;如果觉得太慢,调到 1 或 2。垂直水平滚动的vOffset建议从 1 开始试,超过 3 会明显跳变。

从那以后我每次调 OLED 滚动,都强制先跑一遍地址扫描和初始化命令回读,确认电荷泵和寻址模式对了再开滚动。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询