51单片机——LCD12864液晶显示(二)

目录

[3. 硬件设计](#3. 硬件设计)

[4. 软件设计](#4. 软件设计)

[4.1 lcd12864](#4.1 lcd12864)

[4.1.1 写指令函数 lcd12864_write_cmd](#4.1.1 写指令函数 lcd12864_write_cmd)

[4.1.2 写数据函数 lcd12864_write_data](#4.1.2 写数据函数 lcd12864_write_data)

[4.1.3 初始化函数 lcd12864_init](#4.1.3 初始化函数 lcd12864_init)

[4.1.4 清屏函数](#4.1.4 清屏函数)

[4.1.5 字符显示函数](#4.1.5 字符显示函数)

[4.2 main.c](#4.2 main.c)

[4.3 public](#4.3 public)


使用 LCD12864 实现的功能:在其上显示汉字字符

3. 硬件设计

带字库 12864 液晶

开发板上集中了一个 带字库 12864 液晶接口,下面来看 12864 液晶接口电路

LCD12864 的 8 位数据口 LCD_D0 -- LCD_D7 连接 P0.0 -- P0.7 管脚

LCD12864 的 RS、RW、E 脚 与单片机的 P2.6、P2.5、P2.7 管脚连接

LCD12864 的 PSB 脚与单片机的 P3.2 管脚连接,RESET 复位脚直接连接在 VCC 上

该接口是 LCD12864 与 TFTLCD 彩屏共用的,共占用 20 个管脚

MiniLCD12864 是 16 脚,带字库的 LCD12864 通常是 20 脚,这样可以兼容带字库与不带字库的

4. 软件设计
4.1 lcd12864

lcd12864.c

4.1.1 写指令函数 lcd12864_write_cmd

核心逻辑:通过RS引脚选择指令寄存器,WR选择写操作,利用E(使能脚)的上升沿完成指令锁存,是 LCD12864 指令写入的标准时序。

cpp 复制代码
void lcd12864_write_cmd(u8 cmd)
{
	LCD12864_RS=0;//选择命令(RS=0:指令寄存器)
	LCD12864_WR=0;//选择写(WR=0:写操作,1:读操作)
	LCD12864_E=0;
	LCD12864_DATAPORT=cmd;//将指令放到8位数据总线上
	delay_ms(1);//时序延时,确保数据稳定
	LCD12864_E=1;//E上升沿:锁存指令到LCD内部
	delay_ms(1);
	LCD12864_E=0;//E下降沿:完成指令写入
}

delay_ms(1)需确保是 51 单片机的精准毫秒级延时 (需在lcd12864.h中实现delay_ms函数),否则时序不匹配会导致指令写入失败。

4.1.2 写数据函数 lcd12864_write_data

核心区别:仅LCD12864_RS=1(选择数据寄存器),其余时序和写指令完全一致;

cpp 复制代码
void lcd12864_write_data(u8 dat)
{
	LCD12864_RS=1;//选择数据(RS=1:数据寄存器)
	LCD12864_WR=0;//选择写
	LCD12864_E=0;
	LCD12864_DATAPORT=dat;//准备显示数据(字符/汉字编码)
	delay_ms(1);
	LCD12864_E=1;//上升沿锁存数据
	delay_ms(1);
	LCD12864_E=0;
}

用途:写入显示数据(ASCII 字符码 / 汉字内码,带字库屏会自动解析显示)。

4.1.3 初始化函数 lcd12864_init

LCD12864_PSB = 1:必须设置(即单片机 P3.2 管脚),否则会进入串口模式导致 8 位并行通信失效

cpp 复制代码
void lcd12864_init(void)
{
	LCD12864_PSB=1;//选择8位并行接口(PSB=0为串口模式)
	lcd12864_write_cmd(0x30);//8位总线、基本指令集(正确)
	lcd12864_write_cmd(0x0c);//【注释错误】正确含义:显示开、光标关、闪烁关
	lcd12864_write_cmd(0x06);//写入数据后光标右移、屏幕不移动(正确)
	lcd12864_write_cmd(0x01);//清屏【缺少延时】
}
4.1.4 清屏函数
cpp 复制代码
void lcd12864_clear(void)
{
	lcd12864_write_cmd(0x01);	//清屏【缺少延时】
} 
4.1.5 字符显示函数

lcd12864.c

cpp 复制代码
void lcd12864_show_string(u8 x,u8 y,u8 *str)
{
	if(y<=0)y=0;
	if(y>3)y=3;
	x&=0x0f;	//限制x≤15(0~15列),y≤3(0~3行)

	switch(y)
	{
		case 0: x|=0x80;break;//第1行起始地址:0x80 + 列偏移x
		case 1: x|=0x90;break;//第2行起始地址:0x90 + 列偏移x
		case 2: x|=0x88;break;//第3行起始地址:0x88 + 列偏移x
		case 3: x|=0x98;break;//第4行起始地址:0x98 + 列偏移x
	}
	lcd12864_write_cmd(x);//写入地址指令,定位显示位置
	while(*str!='\0')//循环写入字符串,直到结束符'\0'
	{
		lcd12864_write_data(*str);
		str++;		
	}
}

lcd12864.h

cpp 复制代码
#ifndef _lcd12864_H
#define _lcd12864_H

#include "public.h"

//管脚定义
sbit LCD12864_RS=P2^6;//数据命令选择
sbit LCD12864_WR=P2^5;//读写选择
sbit LCD12864_E=P2^7;//使能信号
#define LCD12864_DATAPORT	P0	//LCD12864数据端口定义
sbit LCD12864_PSB=P3^2;//8位或4并口/串口选择

//函数声明
void lcd12864_init(void);
void lcd12864_clear(void);
void lcd12864_show_string(u8 x,u8 y,u8 *str);
#endif

代码逐行解释:

cpp 复制代码
	if(y<=0)y=0;

作用 :行号y的最小值限制

防止用户传入负数行号 (比如 y=-1),导致后续switch分支错误,强制将负数 y 修正为 0(第 1 行)

举例:若传入 y=-2,执行后 y=0,避免地址计算出错。

cpp 复制代码
	if(y>3)y=3;

作用 :行号y的最大值限制(汉字字符占8*8,4行即62,正好对应 12864 的64行)

LCD12864 带字库屏每行最多显示 16 个字符,共 4 行(对应 y=0/1/2/3),若传入 y>3(比如 y=5),强制修正为 3(第 4 行);

核心目的:避免超出屏幕显示范围,导致显示乱码或指令失效。

cpp 复制代码
	x&=0x0f;	//限制x,y不能大于显示范围

作用 :列偏移 x 的范围限制(0~15)

0x0f是 16 进制,二进制为0000 1111

x &= 0x0f 等价于 x = x & 0x0f,按位与运算后,x 的高 4 位被清零,仅保留低 4 位,最终 x 的取值范围只能是 0~15(对应每行 16 列);

举例:若传入 x=20(二进制0001 0100),与0x0f按位与后结果为 4,强制限定在有效列范围;

注释小瑕疵:只限制了 x,y 的限制是前面两个 if,注释写 "限制 x,y" 稍不准确,但不影响功能。

cpp 复制代码
switch(y)
	{
		case 0: x|=0x80;break;//第1行地址+x的偏移
		case 1: x|=0x90;break;//第2行地址+x的偏移
		case 2: x|=0x88;break;//第3行地址+x的偏移
		case 3: x|=0x98;break;//第4行地址+x的偏移
	}

作用:每行的地址拼接

  • 0x80、0x90、0x88、0x98 是 LCD12864 第 0-3 行的起始地址(手册规定);
  • x |= 0x80 等价于 x = x | 0x80,将 x 的最高位设为 1,拼接成 "第 1 行 + 列偏移 x" 的地址指令;
  • 举例:x=5(第 6 列),执行后 x=0x80+5=0x85,发送该指令后,光标定位到第 1 行第 6 列;
  • break:跳出 switch,避免执行后续分支。
cpp 复制代码
	lcd12864_write_cmd(x);

作用 :发送拼接好的地址指令,完成显示位置定位(只需传 X 即可确定起始位置

lcd12864_write_cmd是你之前定义的 "写指令函数",将拼接后的地址指令发送给 LCD12864,光标就会移动到指定的 (x 列、y 行) 位置。

cpp 复制代码
while(*str!='\0')
	{
		lcd12864_write_data(*str);
		str++;		
	}

作用:写入当前字符数据,显示在屏幕上

lcd12864_write_data是你之前定义的 "写数据函数"

对于带字库屏,传入字符的 ASCII 码(如 'A'=0x41)或汉字内码(如 "中"=0xD6D0),硬件会自动解析并显示对应的字符 / 汉字

str++ 等价于 str = str + 1,指针地址加 1,指向字符串的下一个字符

答疑:

调用函数时传入的是字符串(比如"A""中")这种 "文本形式",而非直接传0x410xD6D0这类编码值,但 LCD12864 带字库屏却能正确解析显示 ------ 本质是C 语言帮你完成了 "文本字面量" 到 "编码值" 的自动转换

① C 语言中 "字符 / 字符串字面量" 的本质 ------ 不是文本,是编码值的 "别名",编译器会自动把它们转换成对应的编码值(ASCII/GB2312)存储到内存中

函数中*str取到的不是 "文本",是内存中的编码值

lcd12864_show_string函数里,u8 *str字符串指针 ,指向的是字符串在内存中的 "首地址",*str取到的是内存中存储的编码值

**执行过程:**假设调用:lcd12864_show_string(2, 1, "ABC");

  1. y=1(≤3 且 > 0),无需修正;x=2,x&=0x0f后仍为 2;
  2. switch (y=1),执行x|=0x90 → x=0x90+2=0x92;
  3. 发送指令 0x92,定位到第 2 行第 3 列;
  4. 循环:
    • 第 1 次:*str='A' → 写入数据 'A',str 指向 'B';
    • 第 2 次:*str='B' → 写入数据 'B',str 指向 'C';
    • 第 3 次:*str='C' → 写入数据 'C',str 指向 '\0';
  5. 循环结束,最终在第 2 行第 3 列开始显示 "ABC"。
4.2 main.c
cpp 复制代码
/**************************************************************************************
实验名称:LCD12864液晶显示实验(带字库)
接线说明:	
实验现象:下载程序后,LCD12864上显示汉字字符信息
注意事项:																				  
***************************************************************************************/
#include "public.h"
#include "lcd12864.h"


/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	lcd12864_init();//LCD12864初始化

	lcd12864_show_string(0,0,"Hello World!");//第1行字符串显示
	lcd12864_show_string(0,1,"大家好!");//第2行字符串显示
	lcd12864_show_string(0,2,"欢迎使用51开发板");//第3行字符串显示
	lcd12864_show_string(0,3,"好好学习天天向上");//第4行字符串显示
	while(1)
	{
		
	}	
}
4.3 public

public.c

cpp 复制代码
#include "public.h"

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}

/*******************************************************************************
* 函 数 名       : delay_ms
* 函数功能		 : ms延时函数,ms=1时,大约延时1ms
* 输    入       : ms:ms延时时间
* 输    出    	 : 无
*******************************************************************************/
void delay_ms(u16 ms)
{
	u16 i,j;
	for(i=ms;i>0;i--)
		for(j=110;j>0;j--);
}

public.h

cpp 复制代码
#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;
typedef unsigned long u32;

void delay_10us(u16 ten_us);
void delay_ms(u16 ms);

#endif
相关推荐
辰哥单片机设计2 小时前
STM32项目分享:智能家用垃圾桶
stm32·单片机·嵌入式硬件
v先v关v住v获v取2 小时前
红薯杀秧机设计cad10张+三维图+设计说明书
科技·单片机·51单片机
就是蠢啊2 小时前
51单片机——LCD12864液晶显示(一)
单片机·嵌入式硬件·51单片机
某林21213 小时前
基于SLAM Toolbox的移动机器人激光建图算法原理与工程实现
stm32·嵌入式硬件·算法·slam
DTI070114 小时前
xilinx的vivado工具综合一直转圈圈,卡死后如何解决?
嵌入式硬件·fpga开发
点灯小铭15 小时前
基于单片机的去皮计价与超重报警电子秤设计与实现
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
章鱼哥嵌入式开发17 小时前
【 STM32 ADC电压采集与串口显示系统】
stm32·单片机·课程设计·嵌入式学习·实验室测量
HappyShengxiang19 小时前
TI发布全新Cortex-M33的MCU产品MSPM33C321A
单片机·嵌入式硬件·mcu·电赛
✧˖‹gσσ∂ иιghт›✧19 小时前
stm32 cube max+keil5的环境怎么printf重定向
stm32·单片机·嵌入式硬件