1. 项目概述
HS12864TG10B是一款基于ST7567驱动芯片的128x64点阵LCD显示屏,在嵌入式领域有着广泛应用。作为一名长期从事STM32开发的工程师,我在多个工业项目中都使用过这款显示屏。它的性价比高、接口简单,但驱动开发过程中有不少需要注意的技术细节。
这款LCD采用COG(Chip On Glass)工艺,将ST7567驱动芯片直接绑定在玻璃基板上,使得整个模块非常轻薄(通常厚度不超过3mm)。显示区域实际尺寸为70.7x38.8mm,像素间距0.55x0.6mm,对比度可通过软件调节,典型工作电流仅2mA(不带背光)。
2. 硬件接口设计
2.1 引脚定义与连接
HS12864TG10B通常提供20pin FPC连接器,核心信号线包括:
| 引脚号 | 符号 | 说明 | 连接STM32建议 |
|---|---|---|---|
| 1 | VSS | 地 | GND |
| 2 | VDD | 逻辑电源(3.3V) | 3.3V |
| 3 | VO | 对比度调节 | 接10K电位器 |
| 4 | RS | 数据/指令选择 | GPIO输出 |
| 5 | R/W | 读/写选择 | 通常接地(只写) |
| 6 | E | 使能信号 | GPIO输出 |
| 7-14 | DB0-DB7 | 数据总线 | GPIO输出 |
| 15 | CS1 | 片选1(左半屏) | GPIO输出 |
| 16 | CS2 | 片选2(右半屏) | GPIO输出 |
| 17 | RES | 复位 | GPIO输出 |
| 18 | VOUT | 负压输出(-10V) | 接对比度电路 |
| 19 | A | 背光阳极 | 通过限流电阻接电源 |
| 20 | K | 背光阴极 | GND |
注意:实际接线时建议在数据线串联100Ω电阻,防止信号反射。背光电流通常限制在20mA以内。
2.2 硬件初始化时序
正确的上电时序对LCD寿命至关重要:
- 先提供VDD电源(3.3V)
- 保持RESET低电平至少1ms
- 释放RESET后等待30ms再发送指令
- 初始化完成后才能开启背光
典型电路设计中,VO引脚应连接10K电位器的中间抽头,两端分别接VOUT和VDD,通过调节电位器可获得最佳显示对比度。
3. 驱动程序设计
3.1 底层接口函数
首先需要实现基本的GPIO控制函数:
c复制// 硬件抽象层
typedef struct {
GPIO_TypeDef* gpio_port;
uint16_t pin_mask;
} LCD_Pin;
void LCD_WriteByte(uint8_t data) {
// 并行方式写入一个字节
HAL_GPIO_WritePin(D0_GPIO_Port, D0_Pin, (data>>0)&0x01);
HAL_GPIO_WritePin(D1_GPIO_Port, D1_Pin, (data>>1)&0x01);
// ... 其他数据位类似
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, GPIO_PIN_SET);
HAL_Delay(1);
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, GPIO_PIN_RESET);
}
void LCD_WriteCmd(uint8_t cmd) {
HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_RESET); // 命令模式
LCD_WriteByte(cmd);
}
void LCD_WriteData(uint8_t data) {
HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_SET); // 数据模式
LCD_WriteByte(data);
}
3.2 显示屏初始化
ST7567需要严格的初始化序列:
c复制void LCD_Init(void) {
// 硬件复位
HAL_GPIO_WritePin(RES_GPIO_Port, RES_Pin, GPIO_PIN_RESET);
HAL_Delay(5);
HAL_GPIO_WritePin(RES_GPIO_Port, RES_Pin, GPIO_PIN_SET);
HAL_Delay(30);
// 软件初始化序列
LCD_WriteCmd(0xE2); // 系统复位
HAL_Delay(1);
LCD_WriteCmd(0xA3); // 偏置设置(1/9 bias)
LCD_WriteCmd(0xA0); // 段方向正常
LCD_WriteCmd(0xC8); // 行方向反向
LCD_WriteCmd(0x25); // 内部电阻比
LCD_WriteCmd(0x81); // 电子音量模式
LCD_WriteCmd(0x1F); // 对比度设置(可调范围0-0x3F)
LCD_WriteCmd(0x2F); // 电源控制(开启电压转换器)
LCD_WriteCmd(0xAF); // 开启显示
HAL_Delay(100);
}
实测发现:对比度值0x10-0x20在常温下显示效果最佳,但需要根据环境温度调整。温度每升高10℃,建议增加3个对比度值。
3.3 显存管理
ST7567内部没有显存,需要开发者自行维护:
c复制uint8_t LCD_RAM[8][128]; // 8页 x 128列
void LCD_Refresh(void) {
for(uint8_t page=0; page<8; page++) {
LCD_WriteCmd(0xB0 | page); // 设置页地址
LCD_WriteCmd(0x10); // 列地址高4位
LCD_WriteCmd(0x00); // 列地址低4位
for(uint8_t col=0; col<128; col++) {
LCD_WriteData(LCD_RAM[page][col]);
}
}
}
显存组织方式:
- 每页对应LCD的8行像素
- 每字节对应8个垂直像素(LSB在上)
- 写入顺序从左到右,从上到下
4. 高级显示功能实现
4.1 字符显示
实现ASCII字符显示(8x8点阵):
c复制// 标准ASCII字模(8x8)
const uint8_t Font8x8[][8] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 空格
{0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00}, // !
// ... 其他字符定义
};
void LCD_PutChar(uint8_t x, uint8_t y, char c) {
if(x > 120 || y > 7 || c < 32) return;
uint8_t page = y;
for(uint8_t i=0; i<8; i++) {
LCD_RAM[page][x+i] = Font8x8[c-32][i];
}
}
void LCD_Print(uint8_t x, uint8_t y, const char* str) {
while(*str) {
LCD_PutChar(x, y, *str++);
x += 8;
if(x >= 120) {
x = 0;
if(++y >= 8) y = 0;
}
}
}
4.2 汉字显示
对于16x16汉字显示,需要外接字库:
c复制void LCD_PutChinese(uint8_t x, uint8_t y, uint8_t index) {
if(x > 112 || y > 6) return;
// 从外部Flash读取字模
uint8_t font[32];
SPI_ReadFont(index, font, 32);
for(uint8_t p=0; p<2; p++) {
for(uint8_t i=0; i<16; i++) {
LCD_RAM[y+p][x+i] = font[p*16+i];
}
}
}
实际项目中,汉字字库通常存储在外部SPI Flash中。一个16x16汉字占用32字节,GB2312字库约需要240KB存储空间。
4.3 图形绘制
基本绘图函数实现:
c复制void LCD_SetPixel(uint8_t x, uint8_t y, uint8_t on) {
if(x >= 128 || y >= 64) return;
uint8_t page = y / 8;
uint8_t bit = y % 8;
if(on) {
LCD_RAM[page][x] |= (1 << bit);
} else {
LCD_RAM[page][x] &= ~(1 << bit);
}
}
void LCD_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
int dx = abs(x2 - x1);
int dy = abs(y2 - y1);
int sx = (x1 < x2) ? 1 : -1;
int sy = (y1 < y2) ? 1 : -1;
int err = dx - dy;
while(1) {
LCD_SetPixel(x1, y1, 1);
if(x1 == x2 && y1 == y2) break;
int e2 = 2 * err;
if(e2 > -dy) {
err -= dy;
x1 += sx;
}
if(e2 < dx) {
err += dx;
y1 += sy;
}
}
}
5. 性能优化技巧
5.1 局部刷新
只刷新修改过的区域可大幅提高效率:
c复制typedef struct {
uint8_t x1, y1, x2, y2;
} DirtyArea;
DirtyArea dirty = {127, 7, 0, 0};
void LCD_MarkDirty(uint8_t x, uint8_t y, uint8_t w, uint8_t h) {
// 更新脏区域坐标
if(x < dirty.x1) dirty.x1 = x;
if(y < dirty.y1) dirty.y1 = y;
if(x+w > dirty.x2) dirty.x2 = x+w;
if(y+h > dirty.y2) dirty.y2 = y+h;
}
void LCD_PartialRefresh(void) {
uint8_t start_page = dirty.y1 / 8;
uint8_t end_page = dirty.y2 / 8;
for(uint8_t page=start_page; page<=end_page; page++) {
LCD_WriteCmd(0xB0 | page);
LCD_WriteCmd(0x10 | (dirty.x1 >> 4));
LCD_WriteCmd(dirty.x1 & 0x0F);
for(uint8_t col=dirty.x1; col<=dirty.x2; col++) {
LCD_WriteData(LCD_RAM[page][col]);
}
}
// 重置脏区域
dirty.x1 = 127; dirty.y1 = 7;
dirty.x2 = 0; dirty.y2 = 0;
}
5.2 双缓冲技术
避免画面撕裂现象:
c复制uint8_t LCD_RAM1[8][128];
uint8_t LCD_RAM2[8][128];
uint8_t *front_buffer = LCD_RAM1;
uint8_t *back_buffer = LCD_RAM2;
void LCD_SwapBuffers(void) {
// 交换缓冲区指针
uint8_t *temp = front_buffer;
front_buffer = back_buffer;
back_buffer = temp;
// 标记整个区域为脏
dirty.x1 = 0; dirty.y1 = 0;
dirty.x2 = 127; dirty.y2 = 7;
}
6. 常见问题解决
6.1 显示模糊或对比度异常
可能原因及解决方案:
- VO电压不正确 - 调节电位器直到显示清晰
- 初始化时序不当 - 确保复位脉冲宽度>1ms
- 对比度命令参数错误 - 尝试0x10-0x30范围值
- 电源不稳定 - 在VDD和VSS间加0.1uF去耦电容
6.2 显示内容错位
检查以下设置:
- 扫描方向命令(0xA0/A1, 0xC0/C8)
- 页地址和列地址设置顺序
- 显存与物理像素的对应关系
- 硬件连接是否接触不良
6.3 功耗优化建议
- 降低刷新频率(最低可至30Hz)
- 不使用背光时可节省约20mA电流
- 在初始化后设置静态驱动模式(0xAC, 0x00)
- 休眠模式下电流可降至10uA以下(0xAE命令)
7. 项目实战建议
在工业环境中使用时,建议采取以下加固措施:
- 在FPC连接器处点胶固定,防止振动导致接触不良
- 数据线串联100Ω电阻并靠近MCU端放置33pF电容滤波
- 背光电路增加PWM调光功能,通过以下代码实现:
c复制void LCD_Backlight_Set(uint8_t brightness) {
// 使用TIM3 CH1 PWM输出
TIM3->CCR1 = brightness;
}
// 初始化PWM
void PWM_Init(void) {
// 时基配置
TIM3->PSC = 8-1; // 9MHz
TIM3->ARR = 100-1; // 90kHz PWM频率
TIM3->CCR1 = 50; // 初始亮度50%
// PWM模式1
TIM3->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;
TIM3->CCER |= TIM_CCER_CC1E;
TIM3->CR1 |= TIM_CR1_CEN;
}
- 增加温度补偿功能,根据环境温度自动调整对比度:
c复制float temp_compensation = 0.0f; // 从温度传感器获取
void LCD_AutoContrast(void) {
uint8_t contrast = 0x18 + (uint8_t)(temp_compensation * 0.3f);
if(contrast > 0x3F) contrast = 0x3F;
LCD_WriteCmd(0x81);
LCD_WriteCmd(contrast);
}
通过实际项目验证,这套驱动框架在-20℃~70℃环境下工作稳定,刷新率可达75Hz,满足大多数工业应用需求。在开发类似项目时,建议先使用逻辑分析仪验证时序,再逐步添加高级功能。