SSD1322灰度OLED驱动实战:STM32初始化与DMA刷新指南 📅 发布时间:2026/9/1 1:49:27 👁 浏览次数: 简介本资源是一套面向嵌入式开发初学者与STM32项目实践者的SSD1322 OLED显示屏驱动工程专为STM32F103系列MCU设计解决OLED图形界面开发中控制器初始化、像素绘制与显示刷新等核心问题。压缩包共78个文件含33个头文件.h用于接口定义与寄存器配置、32个源文件.c实现底层驱动逻辑与图形函数、8个汇编启动文件.s及Keil工程配置文件.uvprojx/.uvoptx总大小291KB结构完整可直接导入Keil uVision编译运行。已有790人学习下载适用于智能仪表、小型HMI、便携设备等需单色高对比度显示的嵌入式场景。读者可获得已验证的SPI通信适配代码、128×64分辨率帧缓冲管理、基础绘图API点/线/矩形、对比度调节与显示开关控制等功能模块并可通过修改Display.c与Mode.h快速扩展字符库或动画效果。 最近这个周末我基本都耗在一个从资源站拉回来的压缩包上名字就叫SSD1322驱动.rar后面还拖着一串_OLED SSD1322_SSD1322 STM32_ssD1322程序_ssd1322_ssd13这样的标签。解压之前我以为又是一个 SSD1306 的变种结果打开一看完全不是一回事这是一颗支持 16 级灰度的 OLED 驱动芯片分辨率可以到 256x64而不是我们熟悉的 0.96 寸小屏那种单色页面。如果你之前只玩过 SSD1306第一次碰 SSD1322 大概率会卡在初始化看不懂、花屏不知道怎么排查、显示图片方向不对这几个坎上。这篇文章就从一个真实的驱动压缩包出发把 SSD1322 这颗芯片、它和 SSD1306 的区别、STM32 上的接线方式、初始化序列、取模方法、常见坑位全部串起来最后给出一套可以直接抄作业的驱动思路。适合已经能用 HAL 库点亮普通 OLED、想更进一步玩灰度屏或者做仪表类界面的朋友哪怕你是跟着江科大视频刚入门 STM32 的萌新只要跟着步骤走也能把它调出来。1. 这块屏和这个驱动包到底解决什么问题先搞清楚 SSD1322 和 SSD1306 到底差在哪。SSD1306 是 128x64 单色屏每个像素只有亮和灭两个状态数据写入按页来一页 8 行单片机往显存里填 0 或 1 就行。SSD1322 则是 256x64 像素支持 16 级灰度内部有一个 128x64x4bit 的显存区域通过 4bit 表示一个像素的亮度等级所以显示效果比单色屏细腻得多可以做类似黑白照片的界面也能通过不同灰度做立体感很强的仪表盘。当然代价是数据量上来了单色屏整屏 1KB 显存SSD1322 在 4bit 模式下整屏要 8192 字节如果按像素展开写SPI 速度不够的话帧率会很难看。这个压缩包的价值就在这里。网上很多 SSD1322 驱动包里的程序结构其实都是类似的核心就是三部分初始化序列、写命令/写数据接口、清屏和显示图片函数。打开之后通常能看到oled.c、oled.h、spi.c、gui.c、font.c有的还带一个oled.bmp.c里面放着一张已经取模好的图片数组。初始化序列那几十行代码是全网通用的不同厂商模组差异不大主要区别在 pin 脚映射和数据扫描方向。把这三部分吃透了你拿到手的不只是一个能亮的程序而是一套以后换屏、换主控都能复用的驱动模板。不过我要先泼一盆冷水别指望这个压缩包解压出来直接编译就能用。大概率会遇到这几个问题接线引脚和你手里的模组对不上、SPI 模式不对导致花屏、初始化序列里有个0xFD 0x12命令被注释掉导致屏幕完全不亮、取模方向和驱动配置不一致导致图形上下颠倒。下面我一个个拆开讲。2. 硬件连接点亮之前先把每个引脚对齐2.1 SSD1322 模组的常见引脚定义STM32 驱动 SSD1322首先要学会看模组丝印。常见 2.4 寸、2.8 寸、3.12 寸 SSD1322 模组引脚大同小异一般包括VCI、VDD、VLSS或 GND、VCOMH、VCOML、BS0、BS1、BS2、SCLK、SDIN、D/C或 A0、CS、RES、Busy。其中 VCI 是面板驱动电压有些模组需要 5V 输入有些可以 3.3VVDD 是逻辑电压通常 3.3VVLSS 接地。Busy 脚有些模组引出来有些没有用到时通常接单片机 IO 读取忙状态但 4 线 SPI 模式下不接也能跑。最容易被忽略的是 BS0、BS1、BS2 这三个接口选择脚。SSD1322 支持 8 位 6800/8080 并行、4 线 SPI、3 线 SPI 等模式具体走哪种接口由 BS0/BS1/BS2 的电平组合决定。驱动包里的代码默认是 4 线 SPI那就必须把这三个脚按 4 线 SPI 拉高或拉低。如果你手里的模组把 BS 脚固定死在并行模式那 SPI 怎么调都不会亮这种情况下要么飞线改电平要么干脆走并行接口。这里没有统一的电平表因为不同厂商模组封装不一样正确姿势是翻你的模组数据手册搜 “BS0 BS1 BS2 interface selection” 表格对着表格设置。2.2 供电、上电时序和电平匹配SSD1322 对供电时序比较敏感官方手册建议 VCI 上电后经过一段延时再让 RES 引脚从低变高完成复位之后才能发命令。如果上来就发命令IC 可能处于锁定状态命令全部被忽略。很多驱动包里的OLED_Init函数一开始会摆一段 HAL_Delay比如上电后延时 50ms然后 RES 拉低 10ms、拉高 10ms再延时 100ms这个顺序最好别省。我之前偷懒去掉这个延时结果屏幕偶尔亮偶尔不亮加了之后稳得很。电平方面需要注意STM32F103 的普通 IO 是 3.3V 电平SSD1322 逻辑部分 VDD 也是 3.3V直接连没问题。但如果你用的是 5V 供电的 Arduino 或者 STM32 的 5V 容忍脚和 VDD3.3V 的模组之间就需要确认电平兼容尤其 SCLK、SDIN 这些信号线输入电压超过 VDD0.3V 就可能损坏 IC。最稳妥的办法是选 VDD3.3V 的模组主控用 3.3V 供电信号线直连。SCLK 和 SDIN 串个小电阻比如 100Ω也可以能降低噪声和过冲。2.3 以 STM32F103 SPI1 为例的接线表我这次实验用的是 STM32F103C8T6SPI1 外设。SPI1_SCK 在 PA5SPI1_MOSI 在 PA7CS 我随便选了 PA4D/C 选了 PA1RES 选了 PA0。如果你手里的主控不是 SPI1记住一个原则只要你的 SPI 主机能输出模式匹配的时钟和数据CS、D/C、RES 用普通 GPIO 控制就行不一定非要挂到 SPI 复用引脚上。4 线 SPI 默认不需要 MISO因为 SSD1322 不往回传数据Busy 是独立引脚所以 SPI1_MISO 脚可以空着。实际的接线表格长这样SSD1322 引脚STM32F103 引脚说明VCI5V 或 3.3V按模组要求面板电源注意看丝印VDD3.3V逻辑电源VLSS/GNDGND共地SCLKPA5SPI1_SCKSDINPA7SPI1_MOSICSPA4片选低有效D/CPA1低电平发命令高电平发数据RESPA0复位低有效BS0/BS1/BS2按模组手册接必须设置为 4 线 SPI 模式接完之后不要急着通电。先用万用表测一下 VCI 和 GND 之间有没有短路很多模组焊接、挤压后容易短路一上电烧的不是屏幕就是单片机。另外 CS 和 D/C 不能悬空悬空时电平不确定初始化很可能跑飞驱动包里如果没做上拉建议代码初始化之前也把这两个引脚的电平状态明确设置好。3. 驱动程序的架构初始化、写命令、刷屏是怎么配合的3.1 压缩包里常见文件结构和核心函数定位解压SSD1322驱动.rar之后核心文件通常就是oled.c和oled.h。oled.c里一般有OLED_WR_CMD、OLED_WR_DATA、OLED_Init、OLED_Clear、OLED_ShowPicture这几个函数。如果驱动包还带了 GUI那oled.c会再提供OLED_SetPixel、OLED_Line、OLED_Circle这类画图函数。我们的目标是先把底层跑通别一上来就折腾 GUI。OLED_WR_CMD和OLED_WR_DATA是驱动的地基。它们两个长得几乎一样唯一的区别是 D/C 引脚的电平。写命令时 D/C 拉低写数据时 D/C 拉高。SPI 传输本身没差别都是往 SPI 外设的数据寄存器里塞一个字节。一个典型实现void OLED_WR_CMD(uint8_t cmd) { OLED_DC_LOW(); OLED_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); OLED_CS_HIGH(); } void OLED_WR_DATA(uint8_t dat) { OLED_DC_HIGH(); OLED_CS_LOW(); HAL_SPI_Transmit(hspi1, dat, 1, HAL_MAX_DELAY); OLED_CS_HIGH(); }注意 CS 是在每次传输前后拉低拉高还是在整个数据块传输期间一直拉低两种写法都有人用。我实测只要驱动包自身保持一致两种都能跑。但如果后面要接 DMA 批量刷新建议把 CS 控制从单字节函数里拿出来变成“开始传输时拉低一整块数据、传完再拉高”不然 DMA 中断处理会很啰嗦。3.2 SSD1322 初始化序列逐条拆解SSD1322 的初始化序列是重点也是最容易出问题的地方。网上流传的初始化代码基本源于数据手册的参考序列我在驱动包里拿到的版本和常见的 SSD1306 驱动对比了一下发现多了好几条指令。下面挑关键的逐条说明。void OLED_Init(void) { // 上电延时 复位 HAL_Delay(100); OLED_RES_HIGH(); HAL_Delay(10); OLED_RES_LOW(); HAL_Delay(10); OLED_RES_HIGH(); HAL_Delay(100); OLED_WR_CMD(0xFD); OLED_WR_DATA(0x12); // 解锁命令锁定寄存器 OLED_WR_CMD(0xAE); // 关闭显示 OLED_WR_CMD(0xA8); OLED_WR_DATA(0x3F); // 设置 multiplex ratio 64 OLED_WR_CMD(0xA0); OLED_WR_DATA(0x51); // 重映射配置 OLED_WR_CMD(0xA1); OLED_WR_DATA(0x00); // 起始行 OLED_WR_CMD(0xA2); OLED_WR_DATA(0x00); // 显示偏移 OLED_WR_CMD(0xA4); // 正常显示关闭全亮 OLED_WR_CMD(0xAB); OLED_WR_DATA(0x01); // 开启内部 VDD 稳压器 OLED_WR_CMD(0xB1); OLED_WR_DATA(0xE0); // 相位 1/相位 2 时长 OLED_WR_CMD(0xB3); OLED_WR_DATA(0xF0); // 时钟分频 OLED_WR_CMD(0x81); OLED_WR_DATA(0x7F); // 对比度 OLED_WR_CMD(0xB8); OLED_WR_DATA(0x03); // 灰度查找表 OLED_WR_CMD(0xBC); OLED_WR_DATA(0x08); // 预充电电压 OLED_WR_CMD(0xBE); OLED_WR_DATA(0x07); // VCOMH 电压 OLED_WR_CMD(0xAF); // 开启显示 HAL_Delay(50); }先看0xFD 0x12这条的作用是给 IC 解锁。SSD1322 有个命令锁机制如果 IC 处于锁定状态后续命令都不会被接受。很多 ssd1306 转过来的朋友移植代码时容易漏掉这条结果屏幕死活不亮。再看0xAB 0x01这条是使能内部 VDD 稳压器。如果你的模组 VDD 引脚没有外部供电而是靠内部稳压出来这条漏掉也是白屏。然后0x81设置对比度0x7F 是个比较中性的初始值后面觉得亮度不够可以现场调大比如 0xFF但注意对比度拉太满且刷新率高的话长时间显示同一画面容易有烧屏风险。0xA0 0x51这个重映射配置是 SSD1322 最让人头大的地方。它决定了列地址和物理像素的对应关系、数据 Nibble 的排列顺序、以及左右上下扫描方向。0x51 是很多标准 256x64 模组上比较通用的一组配置但并不是所有模组都认。如果初始化之后出现图形左右对调、上下颠倒、或者一个像素的亮度和数据对不上先改这里改成其他值试试比如 0x42、0x51 的变体、0x60 等直到显示方向正确。这个踩坑过程我没法用一张表说死因为模组厂商的层压方向不一样必须靠实测。另外还有一个细节0xB8后面的数据是灰度查找表相关配置不同厂商给的参考值五花八门有的写 0x03有的写 0x00有的还会带一串 16 字节的 Gamma 曲线数据。我的建议是如果你手里的驱动包能正常显示就保留原包的值如果换了模组出现灰度层次很怪异的情况优先找这个模组厂商的参考代码覆盖初始化序列。3.3 显示一张图的完整数据流程初始化完之后显示图片的流程比 SSD1306 更接近传统 LCM。SSD1306 是用页地址模式把显示分成 8 页每页 8 行SSD1322 则是完全的行列窗口模式先设置列地址范围再设置行地址范围然后连续写数据。列地址是 0 到 127行地址是 0 到 63。这里要特别提醒列地址 0 到 127 不是你以为的 0 到 127 像素列而是底层的显存地址单元。在 4bit 灰度模式下每个地址单元对应两个相邻显示像素一个字节里高 4 位和低 4 位分别表示这两个像素的灰度值。对应到代码上设置窗口和发送图片数据的函数如下void OLED_SetWindow(uint8_t col_start, uint8_t col_end, uint8_t row_start, uint8_t row_end) { OLED_WR_CMD(0x15); // 设置列地址范围 OLED_WR_DATA(col_start 0x7F); OLED_WR_DATA(col_end 0x7F); OLED_WR_CMD(0x75); // 设置行地址范围 OLED_WR_DATA(row_start 0x3F); OLED_WR_DATA(row_end 0x3F); } void OLED_Clear(uint8_t dat) { OLED_SetWindow(0, 127, 0, 63); OLED_DC_HIGH(); OLED_CS_LOW(); for (uint16_t i 0; i 128 * 64; i) { // 8192 字节 HAL_SPI_Transmit(hspi1, dat, 1, HAL_MAX_DELAY); } OLED_CS_HIGH(); }发送图片的核心逻辑就是先把窗口设到全屏然后连续写入图片数组。列地址在写入过程中会自动递增到窗口右边界后回卷到左边界且行地址加 1所以一整屏数据 8192 字节可以一路写下去不需要中途再次设置窗口。这个自动回卷行为给 DMA 批量刷新提供了便利后面第 6 部分会细说。3.4 把驱动包移植到自己的 STM32 工程拿到驱动包之后最直接的做法不是直接编译而是先找到oled.h里的引脚宏定义改成自己板子的引脚。在 HAL 库工程里常见的宏如下#define OLED_RES_PIN GPIO_PIN_0 #define OLED_RES_PORT GPIOA #define OLED_DC_PIN GPIO_PIN_1 #define OLED_DC_PORT GPIOA #define OLED_CS_PIN GPIO_PIN_4 #define OLED_CS_PORT GPIOA然后把HAL_SPI_Transmit(hspi1, ...)里的句柄换成你自己工程的hspi变量名。如果驱动包里用的不是 HAL 库而是标准外设库差别主要在 SPI 发送函数标准库写法是while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, cmd);其余初始化逻辑可以原样保留。移植完成后先做一个最低限度的验证调用OLED_Init()然后全屏写 0x0F看屏幕是不是均匀显示最高亮度再全屏写 0x00 清屏。如果这两步都正常说明底层 SPI 和初始化没问题再往上层加图片和 GUI。反过来如果全屏 0x0F 出现亮暗不均、横向条纹先别查代码多半是供电或者 SPI 时钟线的信号完整性问题。4. 灰度显示和取模方向别让图片上下颠倒4.1 灰度模式下的数据组织方式SSD1322 的灰度是 4bit所以一个像素的亮度范围是 0 到 150 是纯黑15 是最亮。从 0x00 到 0x0F数值越大越亮。当你向显存写一个字节时这个字节被拆成两个像素的灰度。比如你写0x12高 4 位0x1是第一个像素的低灰度低 4 位0x2是第二个像素稍亮一点的灰度。因此图片数据天生是“一个字节管两个像素”的状态取模软件必须按这个数据组织方式来生成数组。如果你使用的图片取模工具比如 Image2Lcd 或者 PCtoLCD2002需要设置成灰度输出模式。以 Image2Lcd 为例打开图片后要设置输出灰度、最大宽度和高度设为 256 和 64扫描方向选择从左到右、从上到下输出格式选 C 语言数组每个点灰度 4bit。这样生成的数组里第一个字节高 4 位是图片左上角第一个像素低 4 位是它右边相邻的那个像素第二个字节就是第三、第四个像素以此类推。4.2 取模方向要和 Remap 配置对应上这里有个必须实测的点取模软件的扫描方向必须和你初始化里0xA0重映射配置的实际显示方向一致。如果你的驱动包用的0xA0 0x51是从左到右、从上到下扫描那取模也按从左到右、从上到下如果初始化配置里用了奇奇怪怪的 Remap 值屏幕显示方向可能反了图片内容也就跟着反了。调整方向不用重新取模优先调驱动初始化里的 Remap或者调取模软件的扫描顺序看哪个方便。我在实际调试中遇到一次图片左右镜像、一次上下颠倒最后都是通过修改取模软件的扫描方向解决没有动驱动里的 0x51因为改 0x51 可能影响后续所有绘图函数的坐标对应关系。如果你用的是自己写画点函数画线画圆那更不建议瞎改 Remap保持驱动和取模同一套方向体系然后靠软件层面调整坐标映射。4.3 显示中文字符和简单界面的思路SSD1322 不像 SSD1306 那样一个字节正好是一列 8 个点所以字符显示的思路要调整。最朴素的办法是先把字符点阵按灰度 1 和 0 展开然后逐点填充到显存。具体步骤先在内存里准备好一个 128x64 的缓冲数组4bit 模式下是 8192 字节所有绘图操作都写进这个缓冲区最后调用一次刷新函数把缓冲区整体推到屏幕。这种“显存缓冲 整体刷新”的做法比直接一字节一字节操作 SSD1322 的列地址窗口要省心得多尤其是做界面的时候。缓冲区大小 8KB在 STM32F103 上放进全局数组没问题但注意芯片内部 RAM 只有 20KB如果工程里还有其他大缓冲区要留意别爆内存。F103C8T6 的 20KB RAM 应付 8KB 显存缓冲区还能接受再多个摄像头帧缓冲就危险了。你也可以把缓冲区砍成半个屏或者只在变更区域刷新但代码复杂度会上来新手阶段先用整屏缓冲最稳。5. 常见问题排查白屏、花屏、亮度不对逐个击破5.1 白屏完全没反应白屏是最常见也最让人恼火的问题。按照经验先按“供电 → 复位 → 命令锁 → 内部稳压 → 对比度 → 接线”这个顺序排查。首先确认 VCI 和 VDD 都正常模组有没有发烫电流是否异常。然后确认 RES 上电后有完成一次低脉冲复位可以用示波器或者逻辑分析仪看 RES 波形没有示波器就在代码里故意拉高拉低几次一般能解决。接着检查初始化序列是否包含0xFD 0x12解锁命令以及0xAB 0x01开启内部 VDD 稳压器。这两个命令漏一个屏幕就是纯白或纯黑。如果这些都没问题再看 CS、D/C 两个引脚是否接反。CS 接错的话SPI 数据根本没进芯片D/C 接反的话命令被当数据、数据被当命令屏幕行为会非常诡异。最后检查一下 SPI 发送函数里有没有把hspi1句柄传错比如工程里同时初始化了 SPI1 和 SPI2代码里写的却是hspi2这种低级错误我也犯过。5.2 花屏、乱码、图形左右颠倒花屏的排查重点在 SPI 模式和数据方向。SSD1322 的 SPI 模式可以是 mode 0 或 mode 3不同模组驱动的锁存沿可能不同。如果你发现屏幕上出现规律的雪花点、或者相同的画面重复出现先试一下 SPI 初始化里SPI_CPOL和SPI_CPHA的组合STM32 上的操作就是在这两个参数上做排列组合哪个能正常显示就用哪个。另外确认 SDIN 是不是接在了主控的 MOSI 上别接到 MISO这种错误花屏表现也非常典型。图形左右颠倒一般是 Remap 配置和取模方向不一致。左右对调时改取模软件的水平扫描方向上下颠倒改垂直扫描方向或者尝试调整0xA0后面的 Remap 参数。不要同时改两处一次只改一个变量改完重新显示看效果这样能快速定位是驱动问题还是数据问题。5.3 亮度不足、残影、闪烁亮度不足先查对比度寄存器0x81初始值 0x7F 可以往上调0xFF 是最亮。如果调到最亮还是觉得暗检查0xBC预充电电压和0xBEVCOMH 电压这两个值影响 OLED 像素的充电程度。但注意并不是越大越好VCOMH 过高可能出现“鬼影”就是切换画面后上一帧的残影还留在屏幕上。闪烁问题优先查0xB3时钟分频和刷新率。如果 SPI 速度太低、刷一帧需要几十毫秒那屏幕刷新率上不去人眼就会看到闪烁。DMA 刷新可以明显改善这个现象因为主循环不再阻塞在 SPI 发送上屏幕刷完一帧立刻能刷下一帧。还一种情况是电源纹波太大特别是用廉价 DC-DC 给模组供电时OLED 亮度会跟着电源波动这时候在 VCI 和 GND 之间并一个 10uF 和 100nF 电容通常能压下来。5.4 问题排查速查表现象可能原因处理办法全屏白或全屏黑未解锁命令锁检查 0xFD 0x12全屏白或全屏黑内部 VDD 稳压器未开启检查 0xAB 0x01全屏白或全屏黑RES 复位时序不对上电后加延时再拉高花屏、雪花SPI 模式不匹配切换 CPOL/CPHA 组合图形左右颠倒Remap 或取模扫描方向不一致修改取模方向或 0xA0 参数图形上下颠倒行扫描方向反了修改取模垂直方向亮度不足对比度太低调大 0x81 的值残影明显预充电或 VCOMH 设置偏高调低 0xBC 或 0xBE画面闪烁SPI 刷新率不够启用 DMA提高 SPI 分频偶尔花屏电源纹波、CS 浮空加电容代码里显式控制 CS6. 性能优化用 DMA 把刷新率提上去别让主循环卡死6.1 为什么阻塞式刷新不可取用HAL_SPI_Transmit一字节一字节发送或者在 for 循环里反复调用发送函数是非常慢的。STM32F103 的 SPI1 最高 18MHz一帧 8192 字节理论上最快也要 8192 * 8 / 18MHz ≈ 3.6ms。但 HAL 库每次发送都有函数调用开销、标志位等待实际测下来一帧可能要 10ms 以上这意味着刷新率只有 100Hz 不到而且整个 MCU 都被这一个 for 循环占死没法同时做按键扫描、数据处理、界面逻辑。解决方案是用 SPI DMA 传输。DMA 能把内存里的图片数据自动搬到 SPI 发送寄存器中间不需要 CPU 干预。这样刷新 8KB 数据只需要启动一次 DMA 传输发完触发一次中断主循环可以继续干别的事。我把这部分的代码贴出来基本思路是先初始化 SPI 的 DMA 发送通道然后刷新时设置窗口、启动 DMA、等待完成标志。// 前提在 CubeMX 里给 SPI1 添加 DMA 发送通道 extern DMA_HandleTypeDef hdma_spi1_tx; volatile uint8_t oled_transfer_done 1; void OLED_Refresh(uint8_t *buffer) { while (oled_transfer_done 0); // 等上一帧发完 OLED_SetWindow(0, 127, 0, 63); OLED_DC_HIGH(); OLED_CS_LOW(); oled_transfer_done 0; HAL_SPI_Transmit_DMA(hspi1, buffer, 8192); // 在 HAL_SPI_TxCpltCallback 里置 oled_transfer_done 1 } void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi-Instance SPI1) { oled_transfer_done 1; OLED_CS_HIGH(); } }注意这里有个细节CS 不要在整个 DMA 传输期间一直拉低而是在 DMA 发送完成中断里拉高否则下一次刷新如果提前启动CS 时序会乱。另外 DMA 缓冲区必须保持有效不能在函数返回后缓冲区地址失效所以要么用全局数组要么用静态数组不能在栈上临时申请 8KB 缓冲区。6.2 帧率和显存策略的取舍如果启用 DMA 后还想进一步提高帧率可以考虑两个方向。一是降低刷新数据量使用 4bit 灰度模式时如果只是显示简单界面可以隔行刷新或者只刷新变化区域二是提高 SPI 时钟分频F103 的 SPI1 挂 APB2 总线PCLK2 36MHzSPI 时钟可以设到 18MHz再往上需要超频或者换更强主控比如 STM32F4 系列SPI 能到 42MHz 甚至更高刷新一帧 8KB 只需要不到 2ms配合 DMA 就能跑出 30fps 以上的动画效果。显存策略上整屏缓冲是最省心的。如果你对内存敏感可以改成 4bit 灰度下的“双半屏缓冲”一个 4KB 缓冲存上半屏另一个 4KB 缓冲存下半屏刷新完上半屏再刷下半屏。不过个人建议新手不要为了省 RAM 把代码搞复杂STM32F103 的 20KB RAM 放 8KB 显存并不紧张等界面复杂到真的缺内存时再优化不迟。6.3 还有哪些可以扩展的方向驱动跑通之后可以往几个方向扩展。一是加一个简单的灰度字体库把常用 ASCII 字符点阵按 4bit 灰度展开做一个简单的文本显示函数。二是用 SSD1322 的灰度特性做开机 Logo、电池电量、波形曲线这些界面效果比单色 OLED 好很多。三是如果手头有多余的 SPI Flash可以把图片数组放到外部 Flash 里需要显示时再读出来省点内部 Flash 空间因为一张 8192 字节的图片数组放内部 Flash 还是有点心疼的。我个人的体会是SSD1322 不像 SSD1306 那样“一把就能点亮”它更需要你耐心地把初始化序列、Remap 方向、取模方式、刷新方式这几个环节逐一理顺。一旦理顺了它的灰度显示能力带来的界面质感是普通单色屏远远给不了的。最后再分享一个小技巧如果你在调试时发现图片整体灰度偏亮或偏暗不要急着改代码直接把0x81后面的对比度值拿来调这个寄存器是灰度屏的“全局亮度旋钮”比改任何一个其他参数都直观。本文还有配套的精品资源点击获取