环境
软件
- uVision V4.02
- ISIS Professional 7.8
芯片
- AT89C51
- LM016L(LCD)
仿真图
实现效果
实现功能有四个
- 水平滚动显示字符串
- 带光标显示随机算术式
- 全码表字符显示
- CGRAM自定义字符显示
相关代码及资源
https://github.com/duganlx/DSP
操作小记
LCD1602相关函数编写
指令表
指令说明
上述表中,1为高电平、0为低电平
- 指令1:清显示:,光标复位到地址00H位置
- 指令2:光标返回:,光标返回到地址00H
- 指令3:光标和显示模式设置
- I/D:光标移动方向,1:右移,0:左移
- S:屏幕上所有文字是否左移动或右移:1:有效,0:无效
- 指令4:显示开关控制
- D:控制整体显示的开关,1:开显示,0:关显示
- C:控制光标的开关,1:有光标,0:无光标
- B:控制光标是否闪烁,1:闪烁,0:不闪烁
- 指令5:光标或显示移位S/C:1:移动显示的文字,0:移动光标
- 指令6:功能设置命令
- DL:1:4位总线,0:8位总线(有些模块反过来,比如这里所用的就是)
- N:0:单行显示,1:双行显示
- F:0: 5∗7的点阵字符,1: 5∗10的点阵字符
- 指令7:字符发生器RAM地址设置
- 指令8:DDRAM地址设置
- 指令9:读忙信号和光标地址
- BF:1:忙,此时模块不能接收命令或数据,0:不忙
- 指令10:写数据
- 指令11:读数据
其他说明
- 液晶显示模块是一个慢显示器件,所以在执行每条指令之前一定要确认模块的忙标志为低电平(不忙),否则该指令失效
- 当显示字符时要先输入显示字符地址,以下是内部显示地址
所以若要写入字符到第二行第一个(40H),那么实际上写入的命令数据为C0H:0100_0000(40H)+1000_0000(80H)=1100_0000(C0H)
基本操作时序
说明下,RS为寄存器选择,高电平时选择寄存器,低电平时选择指令寄存器。RW为读写信号线,高电平时进行读操作,低电平时进行写操作。EN为使能端,当EN端由高电平跳变成低电平时,液晶模块执行命令。L表示低电平,H表示高电平。
- 读状态
- 输入:
RS=L; RW=H; E=H
- 输出:
D0~7=状态字
- 写指令
- 输入:
RS=L; RW=L; E=下降沿脉冲; D0~7=指令码
- 输出:
null
- 读数据
- 输入:
RS=H; RW=H; E=H
- 输出:
D0~7=数据
- 写数据
- 输入:
RS=H; RW=L; E=下降沿脉冲; D0~7=数据
- 输出:
null
时序图
时序参数
字符代码与字符图形对应关系
使用样例
比如要输出A,查询表可知对应代码为
0100_0001
,而后显示时模块把地址41H
中的点阵字符图形显示出来,我们就可以看到A
头文件
#include<reg51.h>
#include<intrins.h> //使用_nop_();
#include<string.h>
宏定义
// RS为寄存器选择,高电平时选择寄存器,低电平时选择指令寄存器
sbit RS = P2^0;
// RW为读写信号线,高电平时进行读操作,低电平时进行写操作
sbit RW = P2^1;
// EN为使能端,当EN端由高电平跳变成低电平时,液晶模块执行命令
sbit EN = P2^2;
初始化函数
/** * LCD初始化 * * @return */
void init()
{
/* 0011_1000 置功能 DL=1 --> 8位总线 N=0 --> 单行显示 F=0 --> 显示5x7的点阵字符 */
write_cmd(0x38);
/* 0000_0001 清显示 指令码01H,光标复位到地址00H位置 */
write_cmd(0x01);
/* 0000_0110 置输入模式 I/D=1 --> 光标右移 S=0 --> 屏幕上所有文字左移或右移 */
write_cmd(0x06);
/* 0000_1100 显示开/关控制 D=1 --> 开显示 C=0 --> 无光标 B=0 --> 光标不闪烁 */
write_cmd(0x0C);
}
检测BF(busy flag)位状态
/* * 检测BF(busy flag)位状态 * * @return */
void test_BF()
{
unsigned char LCD_status;
do{
P0 = 0xFF; // LCD1602读取状态数据,必须有一个上拉电平
EN = 0; RS = 0; RW = 1; // RS为0时,P0的数据为命令
EN=1;// 让RS和RW设置有效
LCD_status = P0;
_nop_(); _nop_();
EN = 0;
}while(LCD_status&0x80); // 1000_0000 忙碌状态
}
写数据
/* * 写数据(一位一位的写) * * @param data8 八位数据 * @return */
void write_data(unsigned char data8)
{
test_BF();
EN = 0; RS = 1; RW = 0;
P0 = data8;
EN = 1; _nop_(); EN = 0;
}
写命令
/* * 写命令 * * @param cmd8 八位命令 * @return */
void write_cmd(unsigned char cmd8)
{
test_BF();
EN = 0; RS = 0; RW = 0;
P0 = cmd8;
EN = 1; _nop_(); EN = 0;
}
写字符串
/** * 写字符串 * * @param r row * @param c column * @param str 字符串 * @return */
void write_str(int r, int c, char *str)
{
int i=0;
unsigned char Addressx[] = {0x80, 0xC0};
unsigned char StartAdd = (Addressx[r] | c);//按位或
write_cmd(StartAdd);
for(i = 0; i < 16; i++){
if(str[i]==0) break;
write_data(str[i]);
}
// 如果不够16位,用空格填充
for(;i < 16; i++){
write_data(' ');
}
}
主函数编写
头文件
#include<reg51.h>
#include<intrins.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
宏定义
sbit switch1 = P3^0;
sbit switch2 = P3^1;
sbit switch3 = P3^2;
sbit switch4 = P3^3;
unsigned char *strCode=" I Love You!";
引入外部函数
extern void delay_ms(int ms);
extern void init();
extern void write_cmd(unsigned char cmd8);
extern void write_str(int r, int c, char *str);
extern void write_data(unsigned char data8);
功能1:水平滚动文字
void h_scroll_words()
{
int i;
init();
write_str(0,0, " Example-1 ");
while(1)
{
/* fun1 */
for(i = 0; i<strlen(strCode); i++)
{
write_str(1, 0, strCode + i);
delay_ms(100);
}
if(switch1 != 0) break;
}
}
功能2:随机数相加
void random_words()
{
int a,b,i;
unsigned char tempStr[17];
init();
write_cmd(0x0F); //0000_1111:显示开/关控制
write_str(0,0, " Example-2 ");
while(1)
{
//srand(TH0);
a = rand()%10;
b = rand()%10;
sprintf(tempStr, "%d+%d=%d", a, b, a+b);
write_cmd(0xC0|0x05); //1100_0101
for(i = 0; i < 11; i++)
{
if(tempStr[i]) write_data(tempStr[i]);
else write_data(' ');
delay_ms(150);
}
write_str(1, 0, " ");
delay_ms(150);
if(switch2 != 0) break;
}
}
功能3:全码显示
void all_str_code()
{
int i,j;
init();
write_cmd(0x0F);//0000_1111:显示开/关控制
write_str(0,0, " Example-3 ");
while(1)
{
write_cmd(0xC0);
for(i = 0x20; i <= 0xFF; i++)
{
if(switch3) return;
if(i >= 0x80 && i < 0xa0) continue;
if((++j) == 16)
{
write_str(1, 0, " ");
j = 0;
//如果命令让它从第二行写,就重置到第二行第一个地址
write_cmd(0xC0);
}
//if(i == 0xFF) i = 0x20;
write_data(i);
delay_ms(50);
}
}
}
功能4:自定义字符显示
void character_str_code()
{
int i = 0;
unsigned char CC[] = {0x1F,0x11,0x1F,0x11,0x1F,0x11,0x1F,0x00};
init();
write_cmd(0x0F); // 0000_1111:显示开关控制
write_str(0, 0, " Example-4 ");
write_cmd(0x40); //0100_0000:置字符发生存贮器地址
for(i = 0; i < 8; i++)
{
// 通过上面write_LCD_CMD(0x40); 把数组写到CGRAM
write_data(CC[i]);
}
while(1)
{
write_cmd(0xC0);
for(i = 0; i < 16; i++)
{
if(switch4) return;
write_data(0);
delay_ms(50);
}
//当满行显示后清屏
write_str(1, 0, " ");
delay_ms(150);
}
}
主函数
void main()
{
while(1)
{
if(switch1 == 0)
{
h_scroll_words();
}
else if(switch2 == 0)
{
random_words();
}
else if(switch3 == 0)
{
all_str_code();
}
else if(switch4 == 0)
{
character_str_code();
}
}
}