使用带汉字字库的LCD12864的一个最大优点是,不需要为要显示的汉字提前准备点阵数据。而以串行模式驱动,就不需要太多的消耗IO口。
根据资料,需要使用以下几个IO:
RS(CS) | 串行的片选信号 |
R/W(SID) | 串行的数据口 |
E(CLK) | 串行的同步时钟 |
实际测试中,可以匹配Ai8051U的罗电平。
我手里的这块LCD12864工作电压(VDD)是4.5V~5.5V。
接口时序:

串行数据传送共分三个字节完成:
第一字节:串口控制—格式 11111ABC
A 为数据传送方向控制:H 表示数据从 LCD 到 MCU,L 表示数据从 MCU 到 LCD。
B 为数据类型选择:H 表示数据是显示数据,L 表示数据是控制指令
C 固定为 0
第二字节:(并行)8 位数据的高 4 位—格式 DDDD0000
第三字节:(并行)8 位数据的低 4 位—格式 0000DDDD
在串行模式下,传输的时钟不能太快,也不能太慢,建议保持在500KHz左右。如果使用IO口模拟方传输,注意单片机的主频以及加必要的延时处理。如果使用SPI外设,注意设置正确的SPI传输速度。
以下是使用STC的Ai8051U的测试代码,使用12MHz的工作主频,用的是擎天柱开发板。
为了方便,我把c文件和h文件合并到h文件中了。
Lcd12864.h
/**
* 串行模式驱动12864
* 驱动芯片:ST7920
* PSB引脚必须接低电平(GND),以选择串行通信模式。你需要将微控制器的三个GPIO引脚分别连接到液晶屏的CS(片选)、SID(串行数据)和SCLK(串行时钟) 引脚。
*/
#ifndef _LCD12864_S_H_
#define _LCD12864_S_H_
#include "../comm/AI8051U.h"
#include "stdio.h"
#include "intrins.h"
void LCD12864_Init_S(void);
void SetTextPos_S(unsigned char row, unsigned char col);
void ShowString_S(unsigned char row, unsigned char col, unsigned char *str);
void ShowChinese_S(unsigned char row, unsigned char col, unsigned char *ch);
void LCD_Clear_S(void);
void SetGraphicMode_S(void);
void SetTextMode_S(void);
void WriteGDRAM_S(unsigned char x, unsigned char y, unsigned char high_byte, unsigned char low_byte);
void ShowPicture_S(unsigned char *pic);
// 定义引脚
sbit LCD_CS_S = P2^7; // 片选
sbit LCD_SID_S = P2^6; // 串行数据
sbit LCD_SCLK_S = P2^5; // 串行时钟
// 延时函数(STC8051U 主频较高,适当调整)
void delay_us(unsigned int t)
{
while(t--);
}
// 核心:串行发送一个字节
void SendByte_S(unsigned char dat) {
unsigned char i;
for(i = 0; i < 8; i++)
{
LCD_SCLK_S = 0;
if(dat & 0x80)
LCD_SID_S = 1;
else
LCD_SID_S = 0;
dat <<= 1;
LCD_SCLK_S = 1;
}
}
// 写指令/数据到 ST7920(串行模式)
// rw: 0=写, 1=读
// rs: 0=指令, 1=数据
// wdata: 要发送的 8 位数据
void Write_ST7920_S(unsigned char rw, unsigned char rs, unsigned char wdata)
{
unsigned char high, low;
LCD_CS_S = 1; // 片选有效
// 第一个字节:起始位(5个1) + RW + RS + 0
SendByte_S(0xF8 | (rw << 2) | (rs << 1));
// 第二个字节:数据高4位 + 4个0
high = wdata & 0xF0;
SendByte_S(high);
// 第三个字节:数据低4位左移 + 4个0
low = (wdata << 4) & 0xF0;
SendByte_S(low);
LCD_CS_S = 0; // 片选结束
delay_us(50); // 等待指令执行完成
}
// 快捷写指令
void WriteCmd_S(unsigned char cmd)
{
Write_ST7920_S(0, 0, cmd);
}
// 快捷写数据
void WriteData_S(unsigned char dat)
{
Write_ST7920_S(0, 1, dat);
}
// 12864 初始化
void LCD12864_Init_S(void)
{
LCD_CS_S = 0;
LCD_SID_S = 0;
LCD_SCLK_S = 0;
delay_us(20000); // 上电延时等待
WriteCmd_S(0x30); // 基本指令集,8位数据(串行模式下必须)
delay_us(100);
WriteCmd_S(0x0C); // 显示开,光标关,闪烁关
delay_us(100);
WriteCmd_S(0x01); // 清屏
delay_us(2000); // 清屏需要较长延时
WriteCmd_S(0x06); // 光标右移,地址自动+1
}
// 设置显示地址(基本指令集,文字显示)
// row: 0~3 行
// col: 0~7 列(中文字,每字占16x16,一行8个字)
void SetTextPos_S(unsigned char row, unsigned char col)
{
unsigned char addr;
switch(row)
{
case 0: addr = 0x80; break; // 第一行首地址
case 1: addr = 0x90; break; // 第二行首地址
case 2: addr = 0x88; break; // 第三行首地址
case 3: addr = 0x98; break; // 第四行首地址
default: addr = 0x80;
}
addr += col;
WriteCmd_S(addr);
}
// 显示字符串(支持中文和英文)
// 中文占2字节,英文占1字节
// 支持中英文混排,自动处理地址对齐
void ShowString_S(unsigned char row, unsigned char col, unsigned char *str)
{
unsigned char addr;
unsigned char *p = str;
// 计算起始地址
switch(row)
{
case 0: addr = 0x80; break;
case 1: addr = 0x90; break;
case 2: addr = 0x88; break;
case 3: addr = 0x98; break;
default: addr = 0x80;
}
addr += col;
WriteCmd_S(addr);
while(*p != '\0')
{
// 判断是否为汉字:GB2312 高字节 >= 0xA1
if(*p >= 0xA1)
{
// 写汉字前,确保地址是偶数(对齐到格子开头)
if(addr & 0x01) // 地址为奇数,卡在半格中间
{
WriteData_S(' '); // 补一个半角空格,把地址推到偶数
addr++;
}
// 写汉字(2字节)
WriteData_S(*p++);
addr++;
WriteData_S(*p++);
addr++;
}
else
{
// 英文、数字、符号(半角,1字节)
WriteData_S(*p++);
addr++;
}
}
}
// 显示单个中文字(16x16,使用内置字库)
void ShowChinese_S(unsigned char row, unsigned char col, unsigned char *ch)
{
SetTextPos_S(row, col);
WriteData_S(ch[0]);
WriteData_S(ch[1]);
}
// 清屏
void LCD_Clear_S(void)
{
WriteCmd_S(0x01);
delay_us(2000);
}
// ==================== 图形显示扩展(可选)====================
// 切换到扩充指令集(支持绘图 RAM 访问)
void SetGraphicMode_S(void)
{
WriteCmd_S(0x36); // 扩充指令集 + 绘图显示开
}
// 切换到基本指令集(文字模式)
void SetTextMode_S(void)
{
WriteCmd_S(0x30); // 基本指令集
}
// 写图形数据到 GDRAM(绘图 RAM)
// x: 0~15(水平地址,每地址2字节=16像素)
// y: 0~31(垂直地址,共32行,上下半屏各32行)
void WriteGDRAM_S(unsigned char x, unsigned char y, unsigned char high_byte, unsigned char low_byte)
{
unsigned char row = y;
unsigned char col = x;
if(y < 32) // 下半屏
{
col += 8;
row = 31 - y;
}
else // 上半屏
{
row = 63 - y;
}
WriteCmd_S(0x34); // 扩充指令集,绘图关
WriteCmd_S(0x80 + row); // 设置垂直地址
WriteCmd_S(0x80 + col); // 设置水平地址
WriteData_S(high_byte); // 写入高字节 D15~D8
WriteData_S(low_byte); // 写入低字节 D7~D0
WriteCmd_S(0x36); // 绘图显示开
}
// 显示图片(1024字节,128x64 位图)
// pic: 指向 1024 字节图片数据的指针
void ShowPicture_S(unsigned char *pic)
{
unsigned char i, j, k;
WriteCmd_S(0x34); // 扩充指令集
for(i = 0; i < 2; i++) // 上下半屏
{
for(j = 0; j < 32; j++) // 每半屏32行
{
WriteCmd_S(0x80 + j); // 行地址
WriteCmd_S(0x80 + (i ? 0x08 : 0x00)); // 列地址
for(k = 0; k < 16; k++) // 每行16字节(128点)
{
WriteData_S(*pic++);
}
}
}
WriteCmd_S(0x36); // 绘图显示开
}
#endif /* _LCD12864_S_H_ */
主文件中按照以下顺序添加测试代码即可:
LCD12864_Init_S();
// 文字显示示例
ShowString_S(行编号(0-3),列坐标(0-127), "STC8051U 串行");
我要赚赏金
