☰
ST7565驱动12864液晶屏:SPI配置、显存映射与初始化避坑指南
2026/10/15 19:24:33 网站建设 项目流程

简介:ST7565 12864液晶屏驱动方案是一套基于STM32的完整实现,面向嵌入式初学者及需要自制图形显示界面的开发者。资料通过硬件SPI接口控制128×64像素矩阵,细致讲解初始化命令、显示区域设置、光标定位及背光与寄存器操作等核心环节,并针对液晶无内置字库的特点,给出点阵生成与逐像素写入的实用思路,同时涵盖电源管理、锁存器更新和响应延时等注意事项。压缩包共111个文件,以C源码和H头文件为主,辅以Keil工程配置、编译中间文件及可直接烧录的Hex固件,整体仅1.55MB,轻量完整、目录清晰。目前已有676人学习下载,适合希望快速在STM32项目中接入图形显示的用户,可对照示例代码理解SPI通信协议,并移植到自有板卡,实现汉字、图标等自定义内容的显示。

1. ST7565 驱动的坑,比屏本身多:从一张看得到却画不出来的 12864 说起

做单片机项目的人大概率都遇到过这种屏:接口写着 SPI,时序看着也简单,可你按着卖家给的例程把引脚一一对接,屏幕要么白屏,要么雪花,要么死活不出字。我第一次从某厂商手里拿到一块 ST7565 控制器的 12864 液晶屏时,整整耗了三个晚上才点亮。后来复盘发现,问题根本不在于屏幕,而在于 ST7565 的指令时序、寄存器初始化和 SPI 参数配置,这三样只要有一项不匹配,整个显示就是黑的。这篇笔记就是把这个“玄学”拆开:从 ST7565 的显存结构、SPI 硬件连接、初始化指令序列到实际绘图和常见翻车点,一步一步复现一遍。适合刚拿到 ST7565 模块却对着数据手册无从下手的 STM32 开发者,也适合想搞明白为什么例程改了引脚还是白屏的人。

2. 先搞清楚 ST7565 的脾性:显存映射与 SPI 引脚连接

2.1 12864 不等于 128×64 字节:分页显存的结构

ST7565 是点阵 LCD 控制器,内部自带 128×64 位显存,这 128 列 × 64 行不是按字节一行行排的,而是按“页(Page)”划分,每页 8 行像素,共 8 页。每一列作为一个字节的位段,纵向排列。画点的时候,你得先定位到哪一页(0~7),再定位到哪一列(0~127),然后在这个字节里操作对应位。

这个结构和 SSD1306(OLED)非常像,你甚至可以沿用那套页面寻址思路。以下是一个常见的内存映射关系:

显存项范围说明
页面地址0x0 ~ 0x7每页对应 8 行像素,从屏幕顶部开始
列地址0x00 ~ 0x7F对应屏幕的 128 列
每页字节数128每页 128 个字节,1 字节 = 8 个垂直点
总显存8 × 128 = 1024 字节64 像素 × 128 像素 ÷ 8

画点操作的本质,是先读出对应页列字节的当前值,改掉其中一位,再写回去。ST7565 不提供“单独写一个像素”的指令,它只有“设置页地址”“设置列地址”“写显示数据”这几条基础指令,所以绘图函数必须建立在“读改写”的基础上。这就是为什么网上许多精简驱动里都会保留一整块 8×128 的 RAM 缓冲区:用数组模拟屏内显存,画图时直接改数组,刷新时一次把 1024 字节丢过去,避免频繁读屏。

我一般这样定义缓冲区:

// 显存缓冲区:8 页 × 128 列 static uint8_t lcd_buffer[8][128];

每页作为一个数组行,lcd_buffer[page][col]的第 n 位代表该列下第 n 行像素。这样逻辑清晰,后面写画点函数时只需要关心页内行号row % 8与位掩码的关系。

提示:ST7565 上电后默认页地址、列地址是随机的,初始化时一定要明确写入 B0~B7 和 10~1F 系列指令,否则你写的每一个字节都可能落在显存的某个未知角落,表现就是屏幕稀稀拉拉出现几个点,毫无规律。

2.2 SPI 接线与“看起来能通”的误区

ST7565 模块一般引出 7 个引脚:VCC、GND、SCL、SDA、A0(有些标 D/C)、RST、CS。这里的 A0 决定当前传输的是指令还是数据:A0 拉低表示指令,拉高表示数据。SPI 通信时,SCL 是时钟,SDA 是数据线。

接线常见做法是:

STM32 引脚ST7565 引脚说明
PA5SCLSPI 时钟,复用为 SCK
PA7SDASPI MOSI,数据输出
PA4CS片选,低有效
PA6A0 / D/C指令/数据选择,普通 GPIO 即可
PB0RST复位,低有效
3.3VVCC逻辑电源,通常为 3.3V
GNDGND共地

看起来很简单,但这里有个高频翻车点:很多人直接把 STM32 的 SPI 引脚配置成复用功能后,发现屏幕没反应,于是怀疑接线,不断重插。其实真正的原因往往是 A0 线没有在发送字节前被切到正确的电平。它不是 SPI 协议的一部分,而是 LCD 控制器内部的寄存器选择线,必须由软件在“写指令”和“写数据”之间切换。

另外一点,VCC 电压。多数 ST7565 模块的逻辑电压是 3.3V,LCD 偏压由内部升压电路产生,所以 3.3V 供电完全够。但如果你用的模块板上自带了 5V 转 3.3V 的 LDO,且背光 LED 串联电阻是按 5V 设计的,直接接 3.3V 会导致背光偏暗,这个不是驱动问题,是硬件设计问题,别在代码里死磕。

SPI 模式上,ST7565 明确要求 SPI Mode 0 和 Mode 3 都可以,因为它在时钟上升沿采样、下降沿锁存数据,但我建议统一用 Mode 0,后面排查更省心。时钟速度方面,数据手册上限通常标 20MHz,实际使用中 6~10MHz 是安全范围,跑 4MHz 完全够用且抗干扰更好。快速切换页和列时,SPI 速度过快还容易在信号线上产生振铃,导致屏上随机出现杂点,所以宁稳勿快。

SPI 初始化代码可以这样写:

// SPI1 初始化,Mode 0,8 位数据,MSB 先行 void SPI1_Init(void) { RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; // 开启 SPI1 时钟 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // 开启 GPIOA 时钟 // PA5-SCK, PA7-MOSI, PA4-CS 复用推挽输出 GPIOA->MODER &= ~(0xF << 10); // 清掉 PA5、PA7 模式位 GPIOA->MODER |= (0xA << 10); // 设为复用功能 GPIOA->AFR[0] |= (0x5 << 20) | (0x5 << 28); // 复用为 SPI1_SCK / SPI1_MOSI // PA4 片选,普通推挽输出 GPIOA->MODER &= ~(0x3 << 8); GPIOA->MODER |= (0x1 << 8); GPIOA->OSPEEDR |= (0x3 << 8); // 高速 SPI1->CR1 = 0; SPI1->CR1 |= SPI_CR1_SSM | SPI_CR1_SSI; // 软件管理 NSS,避免硬件片选干扰 SPI1->CR1 |= SPI_CR1_MSTR; // 主机模式 SPI1->CR1 &= ~SPI_CR1_CPOL; // CPOL = 0 SPI1->CR1 &= ~SPI_CR1_CPHA; // CPHA = 0 SPI1->CR1 |= SPI_CR1_BR_2; // 8 分频,72MHz 下约为 9MHz SPI1->CR1 |= SPI_CR1_LSBFIRST; // MSB 先行,注意这里要清 0 SPI1->CR1 |= SPI_CR1_SPE; // 使能 SPI }

有几点要特别说明。SPI_CR1_LSBFIRST这个位如果置 1,会变成 LSB 先出,ST7565 要求高位在前,所以这里必须为零。SSM|SSI两位置位是为了让 STM32 不依赖硬件 NSS 引脚。很多人初始化 SPI 后总觉得发送不出去,问题经常就出在 NSS 被硬件拉低或拉高,导致主模式振荡器没启动。最后,SPI_CR1_BR_2只是分频系数,实际速率PCLK / 分频,如果你的 F1 跑 72MHz,8 分频就是 9MHz,完全满足刷新需求。

片选写起来也有讲究,不能用硬件 NSS,要手动控制:

#define LCD_CS_LOW() GPIOA->BSRR = (1 << 4) #define LCD_CS_HIGH() GPIOA->BSRR = (1 << (4 + 16))

CS 拉低后,再进行后续 SPI 传输,传输完拉高。对 ST7565 来说,单次读写时 CS 的一次低脉冲就够了,不需要每个字节都切换。

3. 初始化序列是最大分水岭:ST7565 寄存器配置步骤拆解

3.1 必须走的开关机与偏压设置

ST7565 的初始化比 SSD1306 稍微繁琐一点,核心原因是它沿用了很多老旧 LCD 控制器的遗留指令,比如电源控制、偏压比、电压调节、COM 扫描方向等,很多位都有默认值,但不同厂家模块的出厂差异很大。你要是只发 0xAF(开显示)和几条清屏指令,大概率看到的是“淡淡的残影”,屏幕上什么都没有。

参考初始化序列如下:

void ST7565_Init(void) { // 硬件复位引脚拉低至少 10us,再拉高 LCD_RST_LOW(); Delay_Ms(20); LCD_RST_HIGH(); Delay_Ms(50); // 软件复位 ST7565_WriteCmd(0xE2); Delay_Ms(10); // 关闭显示,开始配置 ST7565_WriteCmd(0xAE); // 偏压比 1/9 ST7565_WriteCmd(0xA2); // 段输出方向正常 ST7565_WriteCmd(0xA0); // COM 扫描方向正常 ST7565_WriteCmd(0xC8); // 内部电源:开启升压 ST7565_WriteCmd(0x2F); // 电压调节:设置参考电压 ST7565_WriteCmd(0x26); // 对比度设置:需要 0x81 加参数 ST7565_WriteCmd(0x81); ST7565_WriteCmd(0x24); // 显示起始行:默认第 0 行 ST7565_WriteCmd(0x40); // 正常显示,非全亮 ST7565_WriteCmd(0xA4); // 开显示 ST7565_WriteCmd(0xAF); }

这段代码里,0xA2是偏压比 1/9,0xA3是 1/7。偏压比会影响 LCD 的驱动电压幅度,选错会出现显示很淡或者有底影。大部分 12864 屏使用 1/9 偏压比,也就是0xA2,这一条最好别随意改。0x2F是内部升压电路开启,对应电源控制指令中的最高位组合:0x2E只开升压,0x2F同时开升压和调节电路。如果你只发0x28或者0x2C,屏幕可能完全没有显示内容,但背光是亮的——这是初始化最常见的问题之一。0x26设置电压调节到内部默认值,后面紧跟0x81 0x24的对比度参数。有些模块出厂时在0x20~0x2F这个电源段有设置偏压大小的作用,所以两个比较关键的电压寄存器都要写。

关于对比度,0x81后面跟的参数可以在 0x00~0x3F 之间调整,数值越大显示越黑。我自己经验是 0x18~0x28 区间比较舒服,太高会出现重影和拖尾,太低则文字发虚。如果你用是低温环境下做产品,可以适当调高 2~3 格,因为液晶响应速度会变慢,对比度不足会更明显。

3.2 页地址与列地址的配合:清屏也要按规矩

初始化完成后最好立刻做一次清屏操作。清屏的流程不是简单地把 0x00 连续写 1024 次,而是要正确设置页地址、列地址,然后按顺序刷。

void ST7565_Clear(void) { for (uint8_t page = 0; page < 8; page++) { // 设置页地址:0xB0 + page ST7565_WriteCmd(0xB0 + page); // 设置列地址低 4 位 ST7565_WriteCmd(0x00); // 设置列地址高 4 位 ST7565_WriteCmd(0x10); for (uint8_t col = 0; col < 128; col++) { ST7565_WriteData(0x00); } } }

列地址的 6 位分成两次写入:低 4 位用0x00~0x0F,高 4 位用0x10~0x1F。注意先发低 4 位,再发高 4 位,顺序反了会跳到一个随机列。每次写完一页的 128 字节后,页地址不会自动加一,你必须在下一轮开始时重新写入0xB0+page。如果你图省事,想通过连续写 1024 个字节一次清完,会发现屏幕只动前 128 列对应的那几行,后面的全是残留数据。

列地址也不会自动回卷,写到第 127 列后再写数据,会继续停留在第 127 列。同一个字节反复覆盖会显得某些列特别黑,而其他列没清到。这个特性在写滚动效果时会用到,但在清屏时是隐患。

提示:ST7565 的页面地址是 B0~B7,不要和列地址高 4 位的 10~1F 搞混。B0 那组是页地址,10~1F 是列地址高位。初始化时发错组别会导致文件画出来上下错位、左右颠倒。

4. 从像素到字符:显存缓冲区与绘图函数实现

4.1 先画点,让屏幕“听你的话”

驱动里最核心的函数是画点。有了它,后面所有画线、画矩形、显示字符都能堆出来。画点需要先计算行列所在页和位掩码:

void ST7565_DrawPixel(uint8_t x, uint8_t y, uint8_t color) { uint8_t page, bit_mask; if (x > 127 || y > 63) { return; // 越界直接丢,避免越界写数组 } page = y / 8; bit_mask = 0x01 << (y % 8); if (color > 0) { lcd_buffer[page][x] |= bit_mask; } else { lcd_buffer[page][x] &= ~bit_mask; } }

整个函数的含义是:纵坐标除以 8 定位到页,余数就是页内第几行,1 << 余数得到字节内掩码。color 为 1 时置位,为 0 时清位。这个数组就是屏内显存的镜像。

绘制线段时不一定必须依赖专门的画线算法,如果你只是画横线或竖线,可以分别循环调用DrawPixel。但画斜线时,没有 Bresenham 算法会很丑陋,而且速度慢。我一般会把一个简单的 Bresenham 画线函数直接放进驱动文件:

void ST7565_DrawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { int dx = (x1 > x0) ? (x1 - x0) : (x0 - x1); int dy = (y1 > y0) ? (y1 - y0) : (y0 - y1); int sx = (x0 < x1) ? 1 : -1; int sy = (y0 < y1) ? 1 : -1; int err = dx - dy; while (1) { ST7565_DrawPixel(x0, y0, 1); if (x0 == x1 && y0 == y1) break; int e2 = 2 * err; if (e2 > -dy) { err -= dy; x0 += sx; } if (e2 < dx) { err += dx; y0 += sy; } } }

这段代码的实现思路基于误差项err的迭代:横纵方向的步进量分别由sx、sy决定,每一步在两个方向中选择一个误差更小的方向前进,保证线条整体均匀。整数运算,无浮点,在 STM32 上跑非常快。如果两点相同,循环内会立即判定退出,所以不会出现死循环。

但是要注意,上面的坐标类型是uint8_t,两点差值的正负判断会因无符号数回绕而出错。我在实际项目里会把参数改成int16_t,或者在入口处做坐标合法性检查,否则画从左上到右下的线时,x0 < x1判断永远为假,线条就反着走了。这小问题当年折腾了我半个小时,排查时对着屏半天没想明白。

4.2 刷新策略:整屏刷新与局部刷新怎么选

ST7565 没有自动增量回读功能,所以刷新策略直接影响帧率和屏幕闪烁程度。常见两种方案:

方案一,整屏刷新。每次更新显存后,把 8 页 1024 字节全部重新发送一遍。代码直接复用清屏时的循环结构:

void ST7565_Refresh(void) { for (uint8_t page = 0; page < 8; page++) { ST7565_WriteCmd(0xB0 + page); ST7565_WriteCmd(0x00); // 列低 4 位 ST7565_WriteCmd(0x10); // 列高 4 位 for (uint8_t col = 0; col < 128; col++) { ST7565_WriteData(lcd_buffer[page][col]); } } }

这种方式每帧固定 1024 字节,加上 24 字节指令开销,按 9MHz SPI 计算,大约 1 毫秒多一点就能完成。肉眼几乎看不出闪烁,所以大多数场景直接整刷就行。

方案二,局部刷新。适合像仪表盘、菜单高亮这种只改局部区域的场景。局部刷新需要定位脏矩形,只把矩形覆盖的页和列写一遍:

void ST7565_RefreshRegion(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { uint8_t start_page = y0 / 8; uint8_t end_page = y1 / 8; for (uint8_t page = start_page; page <= end_page; page++) { ST7565_WriteCmd(0xB0 + page); ST7565_WriteCmd((x0 & 0x0F)); // 列低 4 位 ST7565_WriteCmd(0x10 | (x0 >> 4)); // 列高 4 位 for (uint8_t col = x0; col <= x1; col++) { ST7565_WriteData(lcd_buffer[page][col]); } } }

注意列地址写入顺序和初始化时一致:先低 4 位,再高 4 位。如果你从非零列开始刷新,比如 x0 = 40,对应的低 4 位是 8,高 4 位是 2,合起来就是 0x28 列地址。很多人在局部刷新时忘了在每次页切换后重新设置起始列,导致后续页从 0 列开始,画面就变成斜向错位了。

局部刷新减少了数据量,但省不了指令开销。每次页切换都要 3 条指令,如果脏矩形跨 8 页,实际省下的时间并不明显。我的经验是只有刷写面积小于 1/4 屏时才值得用局部刷新,否则直接整刷反而更省心。

4.3 字符与字模的放置逻辑

字符显示的本质是把字模数据逐字节填入显存缓冲区。字模的存储格式要与显存结构完全对应,即每个字节是某一列的 8 个垂直点。下面是一个 8×16 字符字模的典型放置方式:

void ST7565_DrawChar(uint8_t x, uint8_t y, char ch, const uint8_t font[][16]) { uint8_t idx = (uint8_t)ch - 0x20; for (uint8_t i = 0; i < 8; i++) { // 上半部分 8 行 ST7565_DrawPixel(x + i, y, (font[idx][i] >> 0) & 0x01); ST7565_DrawPixel(x + i, y + 1, (font[idx][i] >> 1) & 0x01); // ... 依次取 8 位 } }

这段代码我简化了,实际上我不会真的用DrawPixel逐位画字符,那样太慢。更高效的方式是直接对显存数组做位操作:

void ST7565_DrawChar(uint8_t x, uint8_t y, const uint8_t *chr) { uint8_t page = y / 8; uint8_t offset = y % 8; for (uint8_t col = 0; col < 8; col++) { uint8_t data = chr[col]; lcd_buffer[page][x + col] |= (data << offset); if (offset > 0) { lcd_buffer[page + 1][x + col] |= (data >> (8 - offset)); } } }

这种写法把字模数据的 8 位一次性“拆”到相邻两页里,效率比逐点画高一个数量级。当y正好是 8 的倍数(offset = 0)时,只有一页被改动,否则当前页放低offset位,下一页放剩下的位。这里最关键的思维是:字模每一行看作一个字节,而在显存里它是竖直排列,所以需要按字节位移,而不是按位去DrawPixel。很多从 51 单片机转过来的人,习惯用逐点方式,结果画一幅 16×16 的汉字都要循环 256 次,帧率自然上不去。

5. 避坑手册:ST7565 调试中必须知道的五个坑

5.1 屏幕全黑但背光亮,SPI 模式或命令发送根本没出去

现象:背光正常,初始化函数也执行了,但屏幕什么都没有。

原因:我曾遇到过一次是 SPI 的时钟极性和相位放错了。有人把 ST7565 当成普通 SPI 从设备,默认配了 CPOL=1、CPHA=1,结果数据在错误的时钟沿被采样,控制器什么都没收到。另一次是片选脚被硬件 NSS 联动控制了,SPI 发送时 NSS 被拉了高,从设备直接失能。

解决:先用逻辑分析仪抓 SCL 和 SDA,确认是否有时钟脉冲和数据位输出。如果发现时钟只有几个脉冲就停了,多半是 SPI 的 TXE 标志没清,或者你在字节间做延迟时 SPI 外设被关了。检查初始化代码里SPI_CR1_SPE是否一直为 1。最简单的验证是发 0xAF 开显示指令后,用万用表量一下 SDA 引脚上是否有持续几微秒的低电平脉冲。

5.2 对比度调到最大还是看不清内容

现象:屏幕上有内容,但非常淡,像没睡醒一样。

原因:ST7565 内部升压电路没有完全打开。只发了0x2E(仅升压)或者发了0x28(关闭电源全部电路),液晶驱动电压不足。还有可能是偏压比设成了0xA3(1/7),和屏体不匹配。

解决:初始化里必须写0x2F,它会开启升压和电压调节电路。偏压比确认屏体规格,常规 12864 用0xA2。对比度寄存器0x81的值从 0x20 起往上调,每调一格看一次效果,直到文字边缘清晰但不毛刺为止。如果仍然很淡,检查 VCC 引脚实际电压,不要只信万用表的空载读数,背光一亮电压可能被拉到 3.0V 以下。

5.3 显示错位:上半屏的内容跑到了下半屏

现象:画一个从左上角到右下角的对角线,结果是上下两段断裂错位,或者字符横穿页边界时被截断。

原因:页面地址和列地址的设置顺序不对。向 B0 写页地址后,紧接着写列地址;但如果先写了0x10(列高 4 位)再写页地址,列地址就被重置到未知位置。另一个常见原因是清屏后页地址未重置到 0,直接从上次结束的页开始写。

解决:每次Refresh前,强制写一次0xB0页地址和列地址。页地址切换时,必须重新写完整的列地址低 4 位和高 4 位,不要只改页地址。调试时可以用“先画一整列的点”来验证页切换是否正确:在循环里固定 x=0,y 从 0 到 63 画一条竖线,看是否连续。

5.4 全屏亮但无法显示内容,屏内 RAM 读写异常

现象:初始化后执行0xA5(全亮)能亮,但退出全亮模式后,写入数据没反应。

原因:0xA5是不读显存的强制全亮模式,说明 LCD 面板和偏压电路都正常,问题出在显存地址与写入数据的映射上。大多数情况是列地址写入时高低位顺序错了,或者发送数据的 A0 电平被固定在高,导致所有命令都被当成数据写入显存。

解决:检查写指令和写数据两个函数的 A0 切换是否在同一个字节内正确执行。正确顺序是:先拉低 A0,再拉低 CS,等待一小段延时让 A0 稳定,然后发送 SPI 字节,结束后拉高 CS。不要在一个函数里只切换 A0 不拉高 CS。调试时可以发0xA4(从显存显示)然后写 0xFF 到第一页前几列,如果屏幕顶部出现一条水平亮线,说明路径通了。

5.5 显示有重影或残影,刷新时看到上一帧内容

现象:更新画面时,新内容叠在旧内容上面,像没擦干净的黑板。

原因:局部刷新时只写了脏矩形区域,没有清掉区域外的旧数据。或者整屏刷新前,显存缓冲区没有先清零,导致程序运行时把未更新的缓冲字节也发送出去了。

解决:整屏刷新前,先 memset 显存缓冲区,再画新内容,最后统一刷新。局部刷新时,把脏矩形扩展 8 像素对齐到页边界,确保整个字节都被重写。举个例子,如果只更新区域 y 从 3 到 10 的像素,实际刷新的页范围应该是第 0 页和第 1 页,而不是从中间切开一页,因为页面内部是整体字节,无法单独更新其中某几行。

6. 进阶:用反显、滚动和局部更新做出一块可用仪表屏

点亮 ST7565 只是第一步,真正让它变得好用,还需要掌握三个技巧:屏幕方向匹配、反显处理、低功耗刷新。

先说屏幕方向。不同厂家模块的 COM 和 SEG 走线方向不同,同一份初始化代码,在这家屏上字是正的,换一家可能就上下颠倒或左右镜像。ST7565 提供了两个方向控制位:0xA0/0xA1控制段方向(左右镜像),0xC0/0xC8控制 COM 方向(上下翻转)。调整物理方向时,四个组合试一遍,找到正确的那个。注意改完方向后,坐标映射也要跟着改,不然画出来的图形是镜像的。我一般会在驱动里加一个方向宏:

#define LCD_CMD_DIRECTION 0xA1 // 左右镜像时改为 0xA0 #define LCD_CMD_SCAN_DIR 0xC8 // 上下镜像时改为 0xC0

反显功能比较实用,尤其在菜单选中项上。ST7565 通过0xA6/0xA7指令切换显示极性,0xA6 正常,0xA7 反显。这条指令不修改显存内容,直接改面板输出逻辑,所以响应很快,适合做光标选中效果。反显之后,缓冲区里的数据和图上坐标关系不变,你不需要额外处理像素反转,只是屏幕最后呈现的颜色相反。

低功耗刷新方面,ST7565 支持进入睡眠模式0xAC,但注意别和关显示0xAE混淆。睡眠模式下内部振荡器和升压电路会停掉,功耗下降明显,但唤醒时需要重新走一遍电源开启序列。做电池供电设备时,我习惯在进入休眠前执行0xAE然后再0xAC,唤醒时先0xAF再延时 50ms,避免屏体因为升压未稳而出现闪烁。

局部更新配合一个分时刷新机制,能让菜单切换非常顺滑。做法是:定义一个脏矩形结构体,记录最小外接矩形;每次按键或数据变化时,调用RefreshRegion只重绘变化区域。但前面说过,局部刷新的页对齐问题很关键。比如你只改了 y=10~20 之间的内容,直接按这个范围刷新会破坏第 1 页里 16~23 行的旧数据。正确做法是把页范围扩展到整个 1 页,即从第 1 页到第 2 页都重写,哪怕第 2 页只有 20~23 行需要更新,也要把整页 128 字节刷一遍。

我在一个手持数据采集器的项目里,用了一颗主频 48MHz 的 MCU 驱动 ST7565 屏,菜单切换频率大约 10Hz,局部刷新每帧只需要发送约 300 字节,加上指令开销,总耗时不到 0.5ms,CPU 占用几乎可以忽略。而那之前用整屏刷新时,每帧 1ms 其实也没问题,但那种“强迫症式”的局部优化,让整机在低功耗模式下能多省约 5% 的电流。

从那次以后,我每次拿到一块新的 ST7565 屏,都会强制自己先跑一遍初始化、全亮、清屏、画斜线这四步,确认基础链路没问题后再谈 UI 开发。这个习惯帮我绕开了无数“板上看着连线没问题,程序里却是玄学”的尴尬。希望这篇笔记也能帮你少走几步弯路,把屏幕尽快点亮。

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

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

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

立即咨询