显示模块LCD1602

LCD1602

是一种工业字符型液晶,能够同时显示16x02即32字符 (16列两行)

LCD1602需要有16根线接到单片机:

VSS -> GND //电源负

VDD -> 5V //电源正

VO -> GND //对比度

RS -> P1.0 //命令选择

RW -> P1.1 //读/写

E -> P1.4 //使能

A -> 5V //背光正

K -> GND //背光负

D0~D7 -> P0.0~P0.7 //数据

要使用这个屏幕,要搞清两件事情,例如我要在第一行第五列打印一个 'M'

1. 在哪里显示

根据手册,显示的位置应该是‘ 04 ’(16进制),即”0000 0101', 也就是说,显示的位置可以用8位2进制数来表示,这8位数据对应的就是外部接线的 D0~D7 口。

但是,不能直接就写入 0000 0101,因为LCD1602规定:在写入显示地址时,必须使得最高位D7恒为高电平1,所以写入的应该是 1000 01010x85

(但是按照逻辑,一共16位数据是需要 2^4 = 16,即4个bit就可以全部表示,为什么要用8个Bit? 因为上图中左侧的16X2是在显示屏上显示出来的,而右侧还有很多个格子没有显示出来,他们可以用作“移位”。)

2. 显示什么

 根据例子,显示的应该是 ‘M’ , 根据手册查表可知:低4位应为1101,高4位应为0100

所以显示M应该输入 0100 11010x4D。(但是对照资料就可知,这其实就是ASCII码,所以代码中直接写字符就可以

在搞清了这两件事情之后,就会有疑问,C51发送给LCD的指令,不管是位置还是内容都是8个bit, LCD是如何识别哪个是位置哪个是内容的呢?

这时候就需要RS:

数据就是 ‘M’ , 指令就是 显示位置

编写‘检测忙信号’的函数

查阅手册:

 

 知道了忙信号是什么,接下来就要读忙信号,这就涉及到了‘读操作’:

RS: 0代表写地址,1代表写内容(但前面提到RS需要低电平)

R/W:前端和后端过程中高低都可,当中必须置1,所以干脆全程置1

D0-D7:根据内容的ASCII码

E:先低;再高;再低,其中低的时间必须大于tR; 高的时间必须大于tPW;再低的时间必须大于tF

void ifBusy() //根据手册,读操作读取并判定忙信号
{
	char tmp;
	
	RS = 0;
	RW = 1;
	E = 0;
	_nop_(); //2微秒左右			
	E = 1;
	_nop_();
	
	tmp = databuffer; //根据时序图,E为1时开始读
	if((tmp & 0x80) == 0x80){//BF(D7)位为高电平,代表忙
		flag_busy = 1;
	}else{
		flag_busy = 0;
	}
	
	E = 0;
	_nop_();
	
	
}

void detectBusy()
{
	databuffer = 0x80; //一定要先将databuffer置为忙
	
	ifBusy(); //先检查一次是否忙并获得flag_busy的初值
	while(flag_busy == 1){ //然后如果flag_busy不变回0,就一直检查是否busy
		ifBusy();
	}
}

编写‘初始化’的函数

void Init()
{
	Delay5ms();
	Delay5ms();
	Delay5ms();
	write(0,0x38);
	Delay5ms();
	
	detectBusy();
	write(0,0x38);
	detectBusy();
	write(0,0x08);
	detectBusy();
	write(0,0x01);
	detectBusy();
	write(0,0x06);
	detectBusy();
	write(0,0x0C);
	
}

编写‘写操作’的函数

根据写操作的时序图:

 

RS: 0代表写地址,1代表写内容

R/W:前端和后端过程中高低都可,当中必须置0,所以干脆全程置0

D0-D7:根据内容的ASCII码

E:先低;再高;再低,其中低的时间必须大于tR; 高的时间必须大于tPW;再低的时间必须大于tF

void write(char flag_w, char cmd) //write(1,cmd)代表写内容;write(0,cmd)代表写指令
{
	RW = 0;//全程置0
	
	if(flag_w == 1){
		RS = 1; //写内容
		E = 0;
		_nop_(); //2微秒左右	
		
		databuffer = cmd; //根据时序图,E为0时开始传内容
		
		E = 1;
		_nop_();
		E = 0;
		_nop_();
	}
	
	if(flag_w == 0){
		RS = 0; //写指令
		E = 0;
		_nop_(); //2微秒左右
		
		databuffer = cmd; //根据时序图,E为0时开始传内容
		
		E = 1;
		_nop_();		
		E = 0;
		_nop_();
	}
	
}

显示单个字符

有了以上的函数,就可以开始组合成真正的项目了,如果目标是在第一行第五列打印一个 'M':

#include "reg52.h"
#include "intrins.h" //这个库加了,delay函数里面的nop()才不会报错
#include <string.h>

#define databuffer P0 //定义8位数据线D0-D7
 
sfr AUXR = 0x8E; //配置了这句话,才可以在UART的初始化里写AUXR寄存器,原因见STC89系列的手册
sbit D5 = P3^7;

sbit RS = P1^0;
sbit RW = P1^1;
sbit E = P1^4;
sbit D7 = P0^7;
char flag_w;
char flag_busy = 0;


void Delay5ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 9;
	j = 244;
	do
	{
		while (--j);
	} while (--i);
}


void write(char flag_w, char cmd) //write(1,cmd)代表写内容;write(0,cmd)代表写指令
{
	RW = 0;//全程置0
	
	if(flag_w == 1){
		RS = 1; //写内容
		E = 0;
		_nop_(); //2微秒左右	
		
		databuffer = cmd; //根据时序图,E为0时开始传内容
		
		E = 1;
		_nop_();
		E = 0;
		_nop_();
	}
	
	if(flag_w == 0){
		RS = 0; //写指令
		E = 0;
		_nop_(); //2微秒左右
		
		databuffer = cmd; //根据时序图,E为0时开始传内容
		
		E = 1;
		_nop_();		
		E = 0;
		_nop_();
	}
	
}

void ifBusy() //根据手册,读操作读取并判定忙信号
{
	char tmp;
	
	RS = 0;
	RW = 1;
	E = 0;
	_nop_(); //2微秒左右			
	E = 1;
	_nop_();
	
	tmp = databuffer; //根据时序图,E为1时开始读
	if((tmp & 0x80) == 0x80){//BF(D7)位为高电平,代表忙
		flag_busy = 1;
	}else{
		flag_busy = 0;
	}
	
	E = 0;
	_nop_();
	
	
}

void detectBusy()
{
	databuffer = 0x80; //一定要先将databuffer置为忙
	
	ifBusy(); //先检查一次是否忙并获得flag_busy的初值
	while(flag_busy == 1){ //然后如果flag_busy不变回0,就一直检查是否busy
		ifBusy();
	}
}

void Init()
{
	Delay5ms();
	Delay5ms();
	Delay5ms();
	write(0,0x38);
	Delay5ms();
	
	detectBusy();
	write(0,0x38);
	detectBusy();
	write(0,0x08);
	detectBusy();
	write(0,0x01);
	detectBusy();
	write(0,0x06);
	detectBusy();
	write(0,0x0C);
	
}

void showStr(char *msg)
{
	while(msg != '\0'){
		detectBusy();
		write(0,0x85);
		detectBusy();
		write(1,msg);
		msg++;
	}
}

void main()
{
	Init();
	
	detectBusy();
	write(0,0x85);
	detectBusy();
	write(1,'M');
	
	/*while(1){
		showStr("mjm");
	}*/
	
}

试验结果:

显示字符串

其实了解了如何显示字符,字符串就很简单了,值得一提的是,只要给出一个初始位置并不断写内容,光标会自行移动,不需要再对光标进行++操作

和以上代码的唯一不同的就是增加了显示字符串的函数并修改了main函数

void showStr(char *msg, char hang, char lie)
{
	char pos;
	
	if(hang == 0){//如果第一行
			pos = 0x80 + 0x00 + lie; 
		}else if(hang == 1){//如果第二行
			pos = 0x80 + 0x40 + lie;
		}
		detectBusy();
		write(0,pos); //只要定下开始的位置,之后光标会自行移动
	
	while(*msg != '\0'){
		detectBusy();
		write(1,*msg);
		msg++;
	}
		
}

void main()
{
	Init();
	
	showStr("m.j.m", 0, 5);
	showStr("SHUAI", 1, 3);	
	
}

试验结果:

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值