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 0101 即0x85
(但是按照逻辑,一共16位数据是需要 2^4 = 16,即4个bit就可以全部表示,为什么要用8个Bit? 因为上图中左侧的16X2是在显示屏上显示出来的,而右侧还有很多个格子没有显示出来,他们可以用作“移位”。)
2. 显示什么
根据例子,显示的应该是 ‘M’ , 根据手册查表可知:低4位应为1101,高4位应为0100
所以显示M应该输入 0100 1101 即 0x4D。(但是对照资料就可知,这其实就是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);
}