STM32F1移植SSD1306 OLED驱动:IIC模拟时序与避坑指南

发布时间:2026/10/19 17:10:37

STM32F1移植SSD1306 OLED驱动:IIC模拟时序与避坑指南 简介一套适配 STM32F1 微控制器、基于 IIC 接口的 OLED 显示驱动源码采用 HAL 库编写可直接集成到现有工程中。它重点解决屏幕初始化、清屏、命令与数据发送以及中文显示等常见需求适合刚接触嵌入式显示或希望省去底层寄存器配置时间的开发者。资源包共 4 个文件包括 3 个头文件与 1 个 C 源文件整体仅 6KB头文件集中声明函数接口、定义 OLED 配置结构体并内置汉字字库C 源文件则实现 OLED_Init、OLED_Clear、OLED_SendData 等核心逻辑代码紧凑稍作引脚配置即可复用。目前已有 1096 人学习下载实用性得到初步验证。通过阅读源码可以理解 STM32F1 的 GPIO 复用功能配置、IIC 外设的初始化流程以及汉字编码到像素点阵数据的转换方式配合附带的字库与位图头文件还能进一步扩展图片或图形显示为后续开发菜单界面、仪表盘等更丰富的嵌入式人机交互打下基础。1. 项目定位STM32F1 点屏的一个稳当选择做单片机显示这一块最怕的不是屏不亮而是驱动代码和平台死绑换个引脚就重写大半。“OLED .C .H文件”这份资源把 SSD1306 控制器的驱动抽成了两个干净文件——oled.c 负责 IIC 底层时序、命令配置和绘制逻辑oled.h 对外暴露初始化、清屏、显示字符/数字/中文/位图的入口——拿到手上基本不用改一行就能在 STM32F1 标准库工程里点亮一块 0.96 寸 IIC 接口 OLED。它适合三种人刚学完 GPIO 和定时器、想用手上屏做数据展示的入门开发者被硬件 I2C 外设折腾到怀疑人生的老手以及想给自己的小项目快速集成显示模块的硬件爱好者。下面我从文件结构、核心时序、工程移植到避坑记录逐层拆开讲。2. 文件解剖与技术选型oled.c / oled.h / 字模头文件各司其职先看清楚这套资源的文件布局。解压后核心就是三个文件oled.c 是所有功能的实现从 IIC 的 GPIO 时序到 SSD1306 命令配置、字符绘制全部揉在一起正常规模在三四百行oled.h 是对外接口和引脚宏定义改引脚、调地址、换延时都从这里入手oledfont.h 是字模数据里面带一套完整的 ASCII 8×16 点阵如果后续要显示中文再往这个文件里追加中文字模数组即可。把这套文件挪进工程之前先回答一个绕不开的问题为什么用 IIC 而不用 SPI以及为什么很多开源驱动选择 GPIO 模拟 IIC而不是 STM32F1 自带的硬件 I2C 外设2.1 IIC 为何是主角GPIO 模拟比硬件 I2C 更适合入门OLED 模组常见的接口方式有 6800/8080 并行、SPI 和 IIC。0.96 寸 128×64 的 SSD1306 屏绝大多数卖家默认给出四针 IIC 版本VCC、GND、SCL、SDA。四根线一接就能跑做小项目时非常省事——你只需要两个 GPIO 就能点亮屏幕而且 IIC 协议本身是半双工的速度对这个分辨率的屏来说完全够用。128×64×1bit 的显存也就 1KB哪怕位宽只有 8 位刷一帧全屏数据也就写 1024 个字节在 100kHz 到 400kHz 的 IIC 时钟下肉眼完全感觉不到卡顿。那为什么不用 STM32F1 的硬件 I2C 外设这个我在项目里踩过坑F1 的 I2C 外设处理总线错误和 BUSY 标志时如果代码没写好经常出现卡死在等待事件标志里的情况网上流传的“F1 的 I2C 外设有玄学”就是这么来的。GPIO 模拟 IIC 的时序完全由延时控制每一拍时钟都看得见摸得着出问题用示波器一抓就知道是起始条件没拉出来还是 ACK 位没读到。这套资源就是采用的 GPIO 模拟方案好处是引脚不挑任意两个普通 GPIO 都能驱动移植到 F0/F4 或者别的厂家芯片时只要把宏定义里的端口和引脚换掉就行。引脚分配上最常见的是 PB6 和 PB7因为这两个引脚在 F1 上本来就是 I2C1 的 SCL/SDA如果后续你想切回硬件 I2C不用改 PCB。但模拟方式下它们只是普通 IO你也可以用 PA5、PA7 之类的任意组合。驱动代码里对引脚的依赖全部集中在一组宏上这一点在二次开发那一章我会展开说。2.2 oled.h 接口总览7 个函数覆盖 90% 显示需求打开 oled.h头文件保护、引脚宏、延时宏下面就是一串 API 声明。我整理成表方便看函数名参数含义作用OLED_Init无初始化 GPIO 和 SSD1306 寄存器OLED_Clear无全屏填 0清除所有显示内容OLED_SetPosx:0~127y:0~7页设置当前读写的光标位置OLED_ShowCharxy字符显示一个 ASCII 字符8×16OLED_ShowStringxy字符串指针连续显示字符串遇到 \0 停止OLED_ShowNumxyuint32_t 数值长度显示十进制整数支持指定位数OLED_ShowChinesexy汉字序号显示字库里的中文字模整体看下来这套 API 的覆盖面是“够用不花哨”的。日常做仪表盘、传感器数据页、菜单列表上面这些函数换着组合就够了。OLED_ShowNum 在显示传感器数值时尤其好用它会按你指定的长度补齐位数比如要显示 3 位数字传入 42 显示成 042这在做某个固定格式的电压/温度界面时非常省心。额外说明一点OLED_SetPos 里的 y 参数不是像素行而是页Page。SSD1306 把 64 行像素分成 8 页每页 8 行所以 y 的合法范围是 0 到 7。这个设计贯穿整个 oled.c 的实现如果你对它不熟后面看 OLED_ShowChar 的代码时容易懵这里先记住。2.3 oled.c 内部结构时序层、命令层、绘制层三段式排布从函数排布上oled.c 大致分三段。文件开头是一组宏和延时函数中间是四个 static 的 IIC 时序函数——IIC_Start、IIC_Stop、IIC_SendByte以及两个写命令/写数据的入口后面就是所有对外 API每个 API 都调用前面的时序函数。这其实就是裸机驱动最常见的分层对外接口与底层硬件隔离改引脚只动宏改时序只动中间那一坨。很多人初学时会在一开始就把 IIC_Start 这些函数写成非 static 的然后在 main.c 里直接调用。我一般不建议这么干因为这些时序函数属于驱动内部实现暴露出去会让调用关系变乱。这套资源把时序函数用 static 锁住对外只留 WriteCmd 和 WriteData 两个口子属于很规范的写法。你如果自己写驱动也建议照这个方式藏内部细节。3. 核心模块拆解一条 IIC 指令从写入到上屏的完整链路这一章我们顺着数据流走一遍从调用一个显示函数开始到屏幕点亮像素为止中间经历了 IIC 起始条件、器件寻址、控制字节区分、数据写入、SSD1306 内部显存刷新这几个环节。每个环节我都贴出核心代码并说明为什么要这么写。3.1 IIC 底层时序起始、停止、写字节三个状态IIC 协议本身不复杂空闲时 SCL 和 SDA 都是高电平起始条件是在 SCL 为高时把 SDA 拉低停止条件是在 SCL 为高时把 SDA 拉高。数据位则是 SCL 低电平时可以变化SCL 高电平时必须稳定。OLED 驱动里这三个动作的实现如下// IIC 起始条件SCL 高电平期间SDA 产生下降沿 static void IIC_Start(void) { OLED_SDA_H(); // 数据线先拉高 OLED_SCL_H(); // 时钟线拉高进入空闲态 OLED_Delay_5us(); OLED_SDA_L(); // 下降沿从机检测到起始条件 OLED_Delay_5us(); OLED_SCL_L(); // 拉低时钟线准备传第一位数据 }这里有个容易被忽略的细节起始条件产生后要立刻把 SCL 拉低。如果不拉低后面的 IIC_SendByte 在第一次翻转 SDA 时就会因为 SCL 还是高电平而被从机误判为又一个起始或停止条件。我在初学时就翻过车示波器抓出来的波形前后矛盾查了好久才发现是这里少了一句。// 写一个字节高位在前 static void IIC_SendByte(uint8_t dat) { uint8_t i; for (i 0; i 8; i) { if (dat 0x80) // 先取最高位 OLED_SDA_H(); else OLED_SDA_L(); dat 1; // 左移一位准备下一 bit OLED_SCL_H(); // 时钟高电平从机在此时采样数据 OLED_Delay_5us(); OLED_SCL_L(); // 时钟低电平允许 SDA 变化 OLED_Delay_5us(); } }IIC_SendByte 的 5us 延时决定了通信速率。72MHz 主频下这个延时实际跑出来大概是几百纳秒到一微秒的量级整体时钟频率落在 100kHz 至 400kHz 之间SSD1306 完全吃得消。如果你用的是主频更低的芯片可以把两个延时改成 10us稳定性更好追求速度则适当缩小但不要低于 1us否则从机采样可能来不及。停止条件的代码逻辑和起始对称static void IIC_Stop(void) { OLED_SDA_L(); // 先保证 SDA 为低 OLED_SCL_H(); // 拉高时钟 OLED_Delay_5us(); OLED_SDA_H(); // SCL 高电平期间 SDA 上升沿产生停止条件 OLED_Delay_5us(); }这段代码没有读取 ACK。模拟 IIC 可以不管 ACK反正屏幕不响应时你发再多字节也是往空气里写最终表现是屏不亮。如果你要做可靠性要求更高的通信可以在写字节后把 SDA 切换为输入模式读一位 ACK但这会让代码复杂不少对点屏这个场景来说收益很低。3.2 命令通路与数据通路0x00 与 0x40 的区分SSD1306 的 IIC 传输帧由三部分组成器件地址、控制字节、数据字节。器件地址是 0x78写方向如果屏的 SA0 引脚被拉高地址会变成 0x7A。控制字节决定了后面的数据是命令还是显示数据这是 SSD1306 特有的设计。// 写命令控制字节 0x00 表示后跟命令 void OLED_WriteCmd(uint8_t cmd) { IIC_Start(); IIC_SendByte(0x78); // 器件地址 写位0x78 对应 SA0 接地 IIC_SendByte(0x00); // 控制字节Co0D/C#0命令模式 IIC_SendByte(cmd); IIC_Stop(); } // 写数据控制字节 0x40 表示后跟显示数据 void OLED_WriteData(uint8_t dat) { IIC_Start(); IIC_SendByte(0x78); // 器件地址同上 IIC_SendByte(0x40); // 控制字节Co0D/C#1数据模式 IIC_SendByte(dat); IIC_Stop(); }这个接口是整套驱动的地基所有命令配置和像素写入最终都归结到这两个函数上。OLED_WriteCmd 用于配置寄存器比如开关显示、设置对比度、选择页地址OLED_WriteData 用于往显存里写字节点阵和清屏数据。值得注意在命令模式里如果你连续发命令第二个命令命令还可以用 Co1 的 0x80 控制字节省掉一遍地址发送但这个优化对点屏速度没有本质提升一般驱动都不做。3.3 SSD1306 初始化序列每个配置命令的作用初始化是屏能不能正常工作的关键。SSD1306 芯片上电后处于一个未知状态必须通过一段命令序列把它配置到我们想要的显示模式。下面是这套驱动里典型的初始化序列void OLED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // 1. 使能 GPIOB 时钟并配置 PB6/PB7 为推挽输出 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); OLED_Delay_MS(100); // 等待模组内部电源稳定 // 2. SSD1306 寄存器配置序列 OLED_WriteCmd(0xAE); // 关闭显示进入配置态 OLED_WriteCmd(0x20); // 设置内存寻址模式 OLED_WriteCmd(0x02); // 选择页寻址模式一次操作一页 OLED_WriteCmd(0x81); // 设置显示对比度 OLED_WriteCmd(0xCF); // 对比度值 207太暗可以调大 OLED_WriteCmd(0x8D); // 电荷泵设置 OLED_WriteCmd(0x14); // 开启电荷泵这是点亮 OLED 的关键 OLED_WriteCmd(0xAF); // 打开显示屏幕此时才真正亮起来 }这里面最关键的三个命令是 0x8D/0x14 的电荷泵开关、0x20/0x02 的寻址模式选择以及 0xAF 的显示开关。电荷泵不开屏幕永远不亮寻址模式选错清屏和画字符时页面跳转逻辑会乱0xAE 和 0xAF 的顺序不能反先关显示再配置配置完再开显示否则中间状态不可控。有些模组还带有 RES 复位引脚如果这块屏把 RES 引出来且接了 MCU 的 GPIO在初始化开头应该先给 RES 一个低电平脉冲再拉高。这套驱动默认 RES 悬空或接上拉所以直接用延时代替硬件复位。如果你手里的屏带 RES我建议在 OLED_Init 的第一行加上“拉低 RES、延时 10ms、拉高 RES”这个动作能显著提高冷启动成功率。3.4 字符显示与字模查找从 ASCII 到中文的取模约定字符显示是这套驱动里最体现“套路”的部分。SSD1306 的显存按页组织每页 8 行像素正好和 8×16 的字符两页对应。oledfont.h 里的字模数组结构如下// 8×16 ASCII 字模每个字符 16 字节 // 前 8 字节为字符上半部分按列从上到下取值 // 后 8 字节为字符下半部分 const uint8_t F8x16[96][16] { // 0x20 空格 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x21 ! {0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00}, // ... 其余字符按 ASCII 顺序排列 };OLED_ShowChar 的写法体现了页寻址模式的核心用法一个字符占两页先写上半页再写下半页。void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t ch) { uint8_t idx, i; if (x 120) { // 8 像素宽的字符超出 120 就放不下 return; } idx ch - ; // 字模表从空格开始偏移即索引 OLED_SetPos(x, y); // 光标定位到第 y 页字符上半部分 for (i 0; i 8; i) { OLED_WriteData(F8x16[idx][i]); // 写上半 8 字节 } OLED_SetPos(x, y 1); // 光标移动到下一页 for (i 8; i 16; i) { OLED_WriteData(F8x16[idx][i]); // 写下半 8 字节 } }OLED_SetPos 的实现必须配合页寻址模式它由三个命令拼成0xB0页号、列地址低四位、列地址高四位。这里有一个经典的坑列地址命令 0x00 和 0x10 只是“低四位”和“高四位”很多新手误以为这是列地址高低字节顺序搞反之后字符位置全乱。void OLED_SetPos(uint8_t x, uint8_t y) { OLED_WriteCmd(0xB0 y); // 页地址y 取 0~7 OLED_WriteCmd(0x00 (x 0x0F)); // 列地址低 4 位 OLED_WriteCmd(0x10 (x 4)); // 列地址高 4 位 }中文字模的原理完全一样不同的是中文字模是 16×16 点阵占 4 个页两列叠加每字 32 字节。OLED_ShowChinese 里需要连续定位四个页位置横向两页、纵向两页。字模取模时要用“列行式、高位在下”的参数和英文的取模约定保持一致否则中文显示出来是镜像或者错位。4. 快速落地在标准库工程里把 OLED 点亮前面把代码逻辑讲透了这一章讲操作。不管你用的是正点原子、野火还是自己搭的标准库工程移植步骤都差不太多。我按 Keil MDK 环境下的典型流程走一遍。4.1 工程准备文件拷贝、头文件路径与引脚定义第一步把三个文件放进工程目录一般放在 HARDWARE/OLED 文件夹下和你的其他外设驱动并列。然后在 Keil 的工程树里新建一个 OLED 分组把 oled.c 添加进去。这一步容易漏的是头文件路径在 Options for Target → C/C → Include Paths 里加上 HARDWARE/OLED否则编译直接报找不到 oled.h。第二步是核对引脚宏。打开 oled.h找到类似下面的定义// 接线说明VCC-3.3VGND-GNDSCL-PB6SDA-PB7 #define OLED_SCL_PORT GPIOB #define OLED_SCL_PIN GPIO_Pin_6 #define OLED_SDA_PORT GPIOB #define OLED_SDA_PIN GPIO_Pin_7如果你的板子把 SCL/SDA 接到了别的引脚只需要改这四个宏。这也是我前面说“这套资源移植方便”的原因——整个驱动里没有任何一处直接写死 GPIOB 或 Pin_6所有硬件操作都经过这些宏。改完宏之后记得确认 GPIOB 的时钟有没有在 OLED_Init 里打开前面代码里已经写了 RCC_APB2PeriphClockCmd有些移植者会自作聪明把这段删掉然后说屏点不亮。4.2 初始化调用顺序与上电时序初始化顺序看起来简单实际有讲究。正确的顺序是先初始化系统时钟和延时函数再初始化 OLED最后清屏。反过来的后果是 OLED_Init 里用了 delay 但延时没初始化Delay 函数可能变成死循环或者延时为零。// main.c 中典型的初始化顺序 int main(void) { Delay_Init(); // 初始化系统 tick 延时主频 72MHz OLED_Init(); // 内部已包含 100ms 上电延时 OLED_Clear(); // 清屏消除上电随机亮点 // 开始显示内容... }OLED_Init 里那个 100ms 延时是给模组内部电源管理用的。IIC 接口的 OLED 模组一般不带硬件复位电路上电瞬间如果立刻发命令SSD1306 内部电荷泵可能还没起来命令会被吞掉。这个延时我给到 100ms 是比较保守的实际 20ms 以上基本都能过但第一次跑的时候宁可多等。4.3 验证程序跑数字、跑字符串、跑绘图代码移植完先用一个最简单的 demo 验证通路。不要一上来就跑动画或者刷图那样问题点和现象混在一起很难查。我的习惯是分三步验证先显示固定字符串跑通 IIC 基本通路再显示动态数字验证重复写入最后全屏清一次屏确认没有残影。#include stm32f10x.h #include oled.h #include delay.h int main(void) { Delay_Init(); OLED_Init(); OLED_Clear(); OLED_ShowString(0, 0, STM32F1 OLED Test); OLED_ShowString(0, 2, IIC 128x64 OK); OLED_ShowString(0, 4, Hello, World!); while (1) { // 每隔 500ms 刷新一次计数验证动态显示 OLED_ShowNum(0, 6, 2024, 4); Delay_MS(500); OLED_ShowNum(0, 6, 2025, 4); Delay_MS(500); } }OLED_ShowNum 直接传一个 uint32_t 数值进去最后一个参数是指定位数。这里传 4 就表示固定显示 4 位数字不足位补零。注意这个函数不会自动清掉高位的旧数字比如从 9999 变到 1如果 len 传的是 2那前两位的 99 会残留。要避免这种问题要么 len 固定传最大值要么在刷新前用空格覆盖旧值这个我在避坑章会单独讲。5. 避坑笔记IIC 点屏最常见的四个翻车现场裸机驱动这东西跑通很简单出问题却往往很玄学。下面这四个坑是我在实际使用这套 OLED 驱动时真遇到过的每个都按“现象、原因、解决”三段写清楚希望能帮你少走弯路。5.1 坑一OLED 上电全黑程序却在正常跑现象main 里 OLED_Init 和 OLED_ShowString 都执行了单步调试时代码全程走完但屏幕就是黑的一点反应没有。原因最常见的是 SCL 和 SDA 接反了。网上买的模组排针顺序有 GND/VCC/SCL/SDA 和 VCC/GND/SCL/SDA 两种有些卖家标注还不准。接反之后IIC 起始条件发在错误的引脚上SSD1306 根本没被唤醒但 MCU 这边代码照常执行看起来就是“程序在跑屏是黑的”。解决对照模组丝印重新核对四根线然后用万用表量一下 VCC 引脚对地电压。如果 VCC 是 3.3V、GND 是 0V再用示波器或逻辑分析仪抓 SCL 引脚看有没有时钟波形。没有波形就是引脚配置不对有波形还不亮就要查器件地址。5.2 坑二模拟 IIC 抓不到 ACK数据发不出去现象逻辑分析仪抓 IIC 总线只能看到起始条件和一两个字节之后就异常停止或者同一个字节发送两次从机都没有回应。原因GPIO 被配置成了推挽输出。推挽模式下 SDA 引脚一直被 MCU 强驱动从机想拉低 SDA 送回 ACK 时拉不动总线时序直接乱掉。很多初学模拟 IIC 的人都会在这上面栽跟头。解决把 SDA 引脚配置成开漏输出并打开内部上拉或者外部加 4.7k 上拉电阻。开漏模式下MCU 输出低电平可以拉低总线释放引脚时总线由上拉电阻拉高从机才有机会拉低 SDA 发 ACK。SCL 保持推挽输出即可因为时钟线只有主机在驱动。修改后初始化代码里的 GPIO_Mode_Out_PP 要改成 GPIO_Mode_Out_OD。5.3 坑三显示出现残影和字符叠影现象刷新数字后旧数字的笔画残留或者两个字符重叠在一起分不清。清屏之后重影消失但一刷新又出现。原因清屏或刷新时页面范围没覆盖全。SSD1306 共 8 页如果 OLED_Clear 只循环了 4 页或者 OLED_ShowNum 刷新时没有先清掉旧数据显存里的残留位就会和新数据叠加显示。另一个常见原因是列地址没有重置在页寻址模式下写完一页后列地址自动回到 0但页地址不会自动加一如果刷新逻辑没考虑页边界就会出现越界写串。解决检查 OLED_Clear 里的循环次数是否为 8页地址命令 0xB00 到 0xB07 是否全部覆盖。数字刷新时OLED_ShowNum 的外部调用要传入足够大的长度并在写入新数字前先用空格覆盖旧数字区域。我在项目里的做法是写一个 OLED_ShowNumEx 函数参数里带一个 oldLen刷新前先按 oldLen 打空格再写新值。5.4 坑四中文全部乱码英文显示正常现象英文和数字完全正常一旦调用 OLED_ShowChinese屏幕上出现的是镜像字、碎片或者没有规律的横线完全不是预期的汉字。原因字模取模方向和显示扫描方向不匹配。英文 8×16 字模是“列行式、高位在下”的取模方式即每个字节表示一列 8 个像素点从下往上读位。如果你的中文字模用 PCtoLCD2002 之类的软件按“行列式”或“高位在前”的标准取模字节排列顺序和显存的写入顺序完全对不上结果就是乱码。解决重新用字模软件取模参数设置改成“阴码、列行式、逆向低位在前、每行显示 2 个字节”。取模完成后对照字模数组里的前两个字节用笔画验证一下第一个字节的最低位对应着汉字左上角的第一列最上面那个像素如果反了说明取模方向还是不对。这块没有捷径就是反复对照。6. 二次开发把固定驱动改成可裁剪、可移植的显示框架6.1 用宏抽象 GPIO做到换引脚不换逻辑这套驱动已经用宏隔离了引脚变化但如果你要让它跑在非 STM32F1 的平台上还需要更彻底的一步把宏定义提升为结构体或回调函数。比如新平台用 Arduino 的 digitalWrite可以定义一个 HAL 层#define OLED_SCL_W(v) digitalWrite(OLED_SCL_PIN, v) #define OLED_SDA_W(v) digitalWrite(OLED_SDA_PIN, v)这样一来oled.c 里的时序代码完全不用动换平台只改这几行。我一般会在移植新平台时先在 oled.h 顶部加一个“平台适配区”注释把所有硬件相关的宏集中放在一个区块里避免它们在文件中散落。6.2 局部刷新改造原驱动的 OLED_ShowString 和 OLED_ShowNum 都是全量写入字符区域在显示大量数据时会造成不必要的 IIC 流量。SSD1306 支持按页定位写入因此可以针对某个区域只刷变化的数据。实现思路是额外增加一个显存缓冲数组先在内存里修改对应位置的像素再把变化的部分写到屏幕。这样在做多级菜单或者波形绘制时不会因为整屏刷新而闪烁。6.3 内置滚动命令与菜单联动SSD1306 自带硬件滚动功能不需要软件一帧帧搬数据。在 OLED_ShowString 显示完文本后开启水平滚动命令 0x26 和 0x2F 即可让整屏内容平移。实际使用中要把滚动方向、起始页和结束页配置好否则滚动区域会覆盖到不想滚动的行。建议在菜单切换时关闭滚动0x2E切换完成后再重新开启避免切换过程中画面撕裂。从那以后我每次移植这套 OLED 驱动都强制走一遍“引脚核对、电平确认、单字符测试、区域刷新验证、全屏滚动测试”五步流程再也没在点屏这种事上熬过夜。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/19 17:10:37

基于深度学习的智慧识花系统:图像分类到Web部署完整实战

前阵子有同学拿着“基于深度学习的智慧识花系统”这个题目来找我,说课程设计要交源码、万字报告,还要准备讲解。我接过题目第一反应是:这项目看着不起眼,实际上把计算机视觉里图像分类的整条链路都串起来了——数据集怎么搭、模型…

2026/10/19 17:10:37

开源大模型本地部署实战:从硬件选型到企业私有化落地

自从ChatGPT把大模型这个概念彻底带出圈之后,我身边越来越多朋友开始问同一个问题:我也想用AI,但每个月交订阅费有点心疼,数据还全在人家服务器上,有没有别的路子?有,而且这条路这几年越走越宽—…

2026/10/19 17:10:37

open-code-review:开源自建代码审查工具链与团队落地实践

代码审查这事,我在团队里推过很多轮,从最开始的“互相看一眼改了什么”,到后来引入自动化检查工具,再到尝试把审查规范沉淀成团队制度,一路踩了不少坑。前阵子借着一个内部项目的契机,把整套流程重新梳理了…

2026/10/19 18:31:16

The Impact of Fine-tuning Large Language Models on Automated Program Repair

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 18:31:16

EvoSLD: Automated Neural Scaling Law Discovery With Large Language Models

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 18:31:16

在昇腾NPU上用vLLM-ascend加速GRPO强化学习训练

最近在昇腾NPU上折腾 vLLM-ascend 配合 GRPO 强化学习训练,也就是很多人常挂在嘴边的“GPRO”训练。实际上 TRL 框架里对应的是 GRPO(群体相对策略优化)。这段链路跑通之后,采样速度比直接用原生生成方法快了五六倍,整…

2026/10/19 18:31:16

32:求分数序列和

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 18:26:15

10元ESP32-S3裸机跑YOLOv5s:边缘AI轻量化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 18:06:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/19 0:04:56

悟空开源无代码平台实战:从实体建模到应用设计避坑指南

简介:基于悟空开源无代码平台的多种应用设计源码,面向希望在不编写代码的情况下快速搭建CRM、HRM、财务系统、ERP、SRM等业务应用的企业与技术人员,通过拖放式界面和内置模板即可完成应用定制与部署。压缩包共697个文件,约10.74MB…

2026/10/19 0:04:56

基于.NET9的多租户权限系统:数据权限与动态API落地实践

简介:这份资源是一套面向 .NET 与 Vue 全栈开发者的中台权限管理系统,后端基于 .Net9,前端采用 Vue3 Element Plus,并配套 uniapp uviewui 移动端方案,实现前后端分离。系统覆盖多租户隔离、数据权限、动态 Api、任务…

2026/10/19 0:04:56

场地预约小程序毕设实战:SSM+微信小程序+MySQL数据库设计到避坑

简介:面向毕业设计场景的场地预约小程序完整项目,基于微信小程序SSMMySql技术栈开发,适合计算机相关专业学生作为选题参考、功能扩展或二次开发起点。系统实现场地类型管理、场地管理、预约管理、取消申请与押金退回等功能,分为管…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑