【本文发布地址https://blog.csdn.net/Stack_/article/details/113833549,未经许可不得转载,转载须注明出处】
2024年:
不建议使用这屏幕,过于落后,每次写数据都要等待,不然就写入失败,大大拖累整个系统的运行速度。不懂的部分也不会继续研究了。
可以换ST7567芯片驱动的LCD + GB2312字库芯片(https://blog.csdn.net/Stack_/article/details/120432945),都是SPI接口,用单片机的硬件SPI去操作,不需要考虑延时,读写速度快,拖影也较小。
显示效果最好的单色屏幕就是OLED了,但是那寿命实在是短。
以前用LCD12864做显示直接用前人的程序,今年过年研究了一下这块屏。
关于这块屏的文章已经有很多了,这里主要记录一下已经解开的我的疑惑。
一、串行通讯下,每次写数据都需要发送至少3个字节
/**
* 串行帧格式:byte0:11111+RW+RS+0 RW:1读 0写 RS:写数据类型,1显示数据 0控制指令
* byte1:HHHH + 0000 读数据类型,1数据 0状态
* byte2:LLLL + 0000 HHHH | LLLL为一个完整字节数据
*/
#define WRITE_COMMAND 0xf8 // 11111 000
#define WRITE_DATA 0xfa // 11111 010
#define READ_STATE 0xfc // 11111 100
#define READ_DATA 0xfe // 11111 110
/**
* @brief 向LCD12864发一字节显示数据/指令
* @note
* @param None
* @retval None
* @author PWH
* @date 2021/1
* @ CSDN Tyrion.Mon
*/
void LCD12864_Write(uint8_t byte_type, uint8_t byte)
{
CS_SET;
LCD12864_CheckBusy();
LCD12864_SendByte(byte_type); //先标明数据字节类型:数据/命令
LCD12864_SendByte(0xf0 & byte); //字节高四位
LCD12864_SendByte(0xf0 & (byte << 4)); //字节低四位
CS_RESET;
}
二、显示地址
1、字符 (基本指令集)
这个不算疑惑,网上一抓一大把,也好理解。
const uint8_t AC_TABLE[4][8]=
{
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, //第一行汉字位置 ST7920芯片分辨率为256 x 64 , 一三为一行,二四为一行,所以指令反白必定两两关联
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, //第二行汉字位置
0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, //第三行汉字位置
0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, //第四行汉字位置
};
/**
* @brief LC12864显示字符串
* @note 中文字符16*16,8192个在CGROM
* 英文、数字字符16*8,126个在HCGROM
* @param None
* @retval None
* @author PWH
* @date 2021/1
* @ CSDN Tyrion.Mon
*/
void LCD12864_ShowStr(uint8_t row, uint8_t col, uint8_t *puts)
{
LCD12864_Write(WRITE_COMMAND, 0x30); //基本指令集合
LCD12864_Write(WRITE_COMMAND, 0x0C);
LCD12864_Write(WRITE_COMMAND, AC_TABLE[row][col]); //起始位置
delay(10, ms);
while(*puts != '\0')
{
if(col >= 8) //判断换行
{ //若不判断,则自动从第一行到第三行
// col = 0;
// row++;
return;
}
if(row >= 4)
// row = 0; //一屏显示完,回到屏左上角
return;
LCD12864_Write(WRITE_COMMAND, AC_TABLE[row][col]);
LCD12864_Write(WRITE_DATA, *puts);
puts++;
LCD12864_Write(WRITE_DATA, *puts);
puts++;
col++;
}
}
2、图片/点阵/打点(扩充指令集)
切换到绘图模式,写入的RAM就从字符显示RAM(DDRAM)切换到点阵显示RAM(GDRAM)了,所以地址和显示字符的地址不一样。
/**
@ CSDN Tyrion.Mon
* 【绘图RAM GDRAM】
*
* 屏左上角 水平坐标(每坐标2字节) (8个列地址 * (2bytes * 8bits)) = 128
* ---------------------------------
* | 00 01 ... 07 |
* 垂 | 00 |
* | 01 上半屏 |
* | ... |
* 直 | 1F(31) |
* | 08 09 ... 0F |
* | 00 |
* 坐 | 01 |
* | ... 下半屏 |
* | 1F(31) |
* 标 |--------------------------------|
* (32行地址 + 32行地址 = 64)
* 总共分为上下两个屏幕,其中第0->31行和第32->63行的行地址相同
所有地址需要 | 0x80
(上 0->31)上半屏的地址 (0x80,0x80) -> ... -> (0x80,0x87)
....
(0x9F,0x80) -> ... -> (0x9F,0x87)
(下 32->63)下半屏的地址(0x80,0x88) -> ... -> (0x80,0x8F)
...
(0x9F,0x88) -> ... -> (0x9F,0x8F)
*/
/**
* @brief LC12864显示一张128*64像素的图片
* @note
* @param None
* @retval None
* @author PWH
* @date 2021/1
* @ CSDN Tyrion.Mon
*/
void LCD12864_ShowFullImage(uint8_t *puts)
{
uint16_t x = 0;
uint8_t row = 0;
uint8_t col = 0;
// LCD12864_Write(WRITE_COMMAND, 0x34); //切换到扩展功能,不开绘图显示 (开关会导致闪屏)
LCD12864_Write(WRITE_COMMAND, 0x36); //切换到扩展功能,开绘图显示
for(row = 0; row < 64; row++) //逐行写入
{
/* 设定行地址 , 上半屏0x80 - 0x9F ,下半屏0x80 - 0x9F */
LCD12864_Write(WRITE_COMMAND, 0x80 + ((row / 32) ? (row - 32) : row));
/* 设定列地址起始地址,列地址只需给定一次,因其会自动增加 */
/* 上半屏首列地址0x80,下半屏首列地址0x88 */
LCD12864_Write(WRITE_COMMAND, (row / 32) ? 0x88 : 0x80);
/* 每行8个列地址,每个地址2字节数据 */
for(col = 0; col < 16; col++)
{
LCD12864_Write(WRITE_DATA, puts[x++]);
}
}
// LCD12864_Write(WRITE_COMMAND, 0x36); //开绘图显示
// LCD12864_Write(WRITE_COMMAND, 0x30); //切换到基本指令
}
/**
* @brief LC12864描点
* @note
* @param None
* @retval None
* @author PWH
* @date 2021/1
* @ CSDN Tyrion.Mon
*/
void LCD12864_DrawDots(uint8_t row, uint8_t col, uint8_t DotByteH, uint8_t DotByteL)
{
LCD12864_Write(WRITE_COMMAND, 0x36); //切换到扩展功能,开绘图显示
LCD12864_Write(WRITE_COMMAND, 0x80 + ((row / 32) ? (row - 32) : row)); //设定行地址 , 上半屏0x80 - 0x9F ,下半屏0x80 - 0x9F
LCD12864_Write(WRITE_COMMAND, (row / 32) ? (0x88 + col) : (0x80 + col)); //设定列地址起始地址,列地址只需给定一次,因会自动增加
//上半屏首列地址0x80,下半屏首列地址0x88
LCD12864_Write(WRITE_DATA, DotByteH);
LCD12864_Write(WRITE_DATA, DotByteL);
}
三、反白显示
因为硬件设计问题,一三行会同时反白,二四行也同时反白,只能自行实现任意点反白。
因为最终的显示是DDRAM和GDRAM异或的结果,因此调用LCD12864_DrawDots函数将特定位置填充0xFF后,再调用LCD12864_ShowStr函数在此位置写入字符即可达到反白效果。(如下图“周二”)
或者取字模,在调用LCD12864_DrawDots写入时将点阵数据按位取反。如下图的大数字
四、指令
/* 初始化 */
//功能设定 0x30: DL=1 , 8BitMCU
LCD12864_Write(WRITE_COMMAND, 0x30);
//功能设定 0x30: RE=0 , 基本指令集合 DL和RE虽在同一组里,但不能同时设,须发两次
LCD12864_Write(WRITE_COMMAND, 0x30);
//清除显示 0x01: 清屏,AC归0
LCD12864_Write(WRITE_COMMAND, 0x01);
//显示状态 0x0C: 整体显示ON,游标OFF,游标反白OFF
LCD12864_Write(WRITE_COMMAND, 0x0C);
/* 写入字符前 */
//切换到基本指令集合
LCD12864_Write(WRITE_COMMAND, 0x30);
//显示状态 0x0C: 整体显示ON,游标OFF,游标反白OFF
LCD12864_Write(WRITE_COMMAND, 0x0C);
/* 描点前 */
//切换到扩展功能,不开绘图显示(开关会导致闪屏,不关闭也能正常写入、显示)
// LCD12864_Write(WRITE_COMMAND, 0x34);
//切换到扩展功能,开绘图显示
LCD12864_Write(WRITE_COMMAND, 0x36);
/* 进入点设定 */
未搞懂
/* 游标或显示移位控制 */
未搞懂