A VR学习笔记十七、LCD12232液晶显示实验
-------基于LT_Mini_M16
17.1 LCD12232液晶显示实验
17.1.1、实例功能:
在前面我们已经学习了1602和12864液晶的基本知识,并且通过简单的实例实现了在1602和12864液晶上显示字符和汉字。今天我们再来学习另外一种比较常用的液晶12232.
本实例中我们选用深圳锦昌电子的DM12232B型液晶。
本实例分为三个功能模块,分别描述如下:
●单片机系统:利用A Tmega16单片机与DM12232B型液晶构成液晶显示电路。
●外围电路:DM12232B型液晶与单片机的连接电路。
●软件程序:编写软件,控制液晶显示字符。
通过本实例的学习,掌握以下内容:
●掌握DM12232B型液晶的基本原理和程序设计方法。
17.2、器件和原理
关于液晶的显示原理我们在前面的实例中已经做过介绍,在这里就不再多做说明。在本实例中我们重点介绍DM12232B型液晶的结构、指令及显示控制。
12232系列的LCD大部分都是使用SED1520驱动芯片,12232F用的是ST7920。它们的运行速度都是nS级的,所以一般我们发送数据的时候不用过多考虑等待问题。
许多LCD模块的引脚数都不一致,常见有16~20个不等。不过它们的功能是大同小异。我们所用的DM12232B型液晶是18引脚:VDD,VSS,VLCD,RET,E1,E2,R/W,A0,DB0,DB1,DB2,DB3,DB4,DB5,DB6,DB7,LED+,LED-。
17.2.1、DM12232B型液晶的管脚排列
DM12232B型液晶的管脚排列及说明如图1所示:
引脚功能简单说明:
1、VLCD为LCD电源,要求电压可调节,一般用20K的可调电阻取中间抽头电压供电;
2、RES,复位信号。这个大家都知道,一般应用中直接接到高平就行了;
3、E1,E2为控制器选择线,高电平时为选中;
4、R/W=0时为写选通,R/W=1时为读选通,一般我们只是向液晶发送数据,不读液晶内部的数据,所以该脚可以直接接地(低电平);
5、A0=1时表示所发的数据是显示数据,A0=0时表示所发数据是指令(Instruction);
6、DB0~DB7为数据线;
7、LED-,LED+为背光灯电源,一个接正,一个接地就行。
17.2.1、DM12232B型液晶的读写时序
DM12232B型液晶的读写时序如图2所示,
应用中主要有两种读写时序:写指令和写数据,分别描述如下:
写指令:
E选通—A0=0—读写使能(直接接地就不用设置了)—数据的发送—状态释放
写数据
E选通—A0=1—读写使能(直接接地就不用设置了)—数据的发送—状态释放
图1 DM12232B型液晶的管脚排列及说明
图2 DM12232B型液晶的读写时序
17.2.3、DM12232B型液晶的指令介绍
DM12232B型液晶的指令如表1所示
表1 DM12232B型液晶的指令
表1(续)DM12232B型液晶的指令
17.2.4、DM12232B型液晶的原理图
DM12232B型液晶的原理图如图3所示
17.2.5、DM12232B型液晶的地址表
DM12232B型液晶的地址表如图4所示SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
图3 DM12232B型液晶的原理图
图4 DM12232B型液晶的地址表
10.1.3、电路和连接
图5所示为DM12232B型液晶与单片机之间进行并口通讯的典型接法本实例是在LT_Mini_M16学习板的基础上做的扩展实验,具体连线如下。引脚连接
mcu lcd 引脚说明
Vcc VDD 电源电压
GND VSS 电源地
偏压信号,接可调电阻VEE(VLCD) LCD外接驱动负电压
PA1 RES 复位信号(低电平有效),低电平复位
PA6 E1 读写使能信号1
PA7 E2 读写使能信号2
PA5 R/W 读写选择信号
PA4 A0 H-显示数据。。L-显示指令数据(指令数据选择)
PB0 DB0 数据线
PB1 DB1
PB2 DB2
PB3 DB3
PB4 DB4
PB5 DB5
PB6 DB6
PB7 DB7
Vcc VLED+ LED(+5V)或EL背光源
GND VLED- LED(0V)或EL背光源
图5 12232B液晶与单片机的一种接口图
17.4、程序设计
1、程序功能
程序的功能是使用单片机控制12232B液晶显示字符,程序比较简单,直接看程序就能明白原理了。
2函数说明
本程序多个功能函数,分别是:
●DM12232B液晶处理相关函数:
extern void LCDPort_Init(void); //液晶端口初始化
extern void LCD_Init(void); //LCD初始化
extern void WriteCommand(unsigned char chip_select,unsigned char cmd);//写命令
extern void WriteData(unsigned char chip_select,unsigned char data);//写数据
extern void clear_lcd(void); //清屏
extern void tex_Write(unsigned char *pt); //写字符
extern void WriteCharacter(unsigned char *pt); //写汉字
extern void Drawing_Map(unsigned char *pt); //绘图
●延时相关函数:
void Delayus(unsigned int lus); //us延时函数
void Delayms(unsigned int lms); //ms延时函数
由于WINA VR自带函数库中的延时函数使用起来很不方便,并且晶振频率不同,延时时间也有区别,所以本实例中自己写了两个延时函数。
3、使用WINA VR开发环境,使用的是外部12M的晶振,所以需要将makefile文件中的时钟频率修改为12M。另外在程序烧录到单片机的时候,熔丝位也要选择为外部12M晶振(注意是晶振,不是外部振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。
4、程序代码
由于本程序代码比较长,所以在此只列出与控制液晶DM12232B相关的部分代码,完整程序放在附件中
//端口初始化
void LCDPort_Init()
{
//LCD数据端口设置
PORTB = 0xff; //
DDRB = 0xFF; //配置端口PB全部为输出口,LCD数据端口
//LCD控制端口设置
SET_RES;
SET_A0;
SET_RW;
SET_E1;
SET_E2;
DDRA = 0xff; //
Delayms(15);
}
//LCD初始化
void LCD_Init()
{
WriteCommand(0x01,0xe2); //rest 复位
WriteCommand(0x02,0xe2);
//WriteCommand(0x01,0xae); //close display 关显示
//WriteCommand(0x02,0xae);
//WriteCommand(0x01,0xa4); //static driver关静态驱动
//WriteCommand(0x02,0xa4);
WriteCommand(0x01,0xa9); //duty 1/32占空比1/32
WriteCommand(0x02,0xa9);
//WriteCommand(0x01,0xa0); //clockwise output ADC选择,顺时针还是逆时针读取RAM数据
//WriteCommand(0x02,0xa0);
//WriteCommand(0x01,0xee); //end 关闭读修改写,无论读或写操作后,列地址都加1 //WriteCommand(0x02,0xee);
//WriteCommand(0x01,0x00); //行地址设置,设置显示RAM的行地址(Y地址)//WriteCommand(0x02,0x00);
//WriteCommand(0x01,0xc0); //显示起始行设置。指定显示器从显示RAM中的那一行开始显示
//数据,(起始行=0)
//WriteCommand(0x02,0xc0);
WriteCommand(0x01,0xaf); //opend display 开显示
WriteCommand(0x02,0xaf);
}
//LCD写指令,
void WriteCommand(unsigned char chip_select,unsigned char cmd)
{
if(chip_select & 1) //判断对左页还是右页的操作
{
SET_E1; //如果是左页,E1使能
}
else if(chip_select & 2) //
{
SET_E2; //右页,E2使能
}
CLR_A0; //A0=0 写命令
CLR_RW; //RW=0 写操作
PORTB = cmd; //写命令数据到数据端口
if(chip_select & 1) //
{
CLR_E1; //关闭左右页使能
}
else if(chip_select & 2) //
{
CLR_E2; //
}
SET_A0; //
SET_RW; //
}
//写数据
void WriteData(unsigned char chip_select,unsigned char data)
{
if(chip_select & 1) //判断左右页
{
SET_E1; //
}
else if(chip_select & 2) //
{
SET_E2; //
}
SET_A0; //A0=1,写数据
CLR_RW; //RW=0,写操作
PORTB = data; //写数据到数据端口
if(chip_select & 1) //
{
CLR_E1; //结束使能
}
else if(chip_select & 2) //
{
CLR_E2; //
}
CLR_A0; //
SET_RW; //
}
//清屏
void clear_lcd(void)
{
unsigned char a,b,c;
for(a = 0xb8;a < 0xbc;a++) //清屏0-3页,指令分别是b8,b9,ba,bb(X地址){
b = 0; //
WriteCommand(0x01,a); //左,第0页开始
WriteCommand(0x02,a); //右,第0页开始
WriteCommand(0x02,b); //右,第0行开始(Y地址)
WriteCommand(0x01,b); // 左,第0行开始
for(c = 0;c < 61;c++) //总共122列,左右各61列
{
WriteData(0x01,0x00); //左,每列均填充0
WriteData(0x02,0x00); //右,每列均填充0
}
}
}
//写字符
void tex_Write(unsigned char *pt)
{
unsigned char a,b;
if(SEL_E1) //左选中?
{
WriteCommand(0x01,0xb8); //页设置,第0页(X地址)
WriteCommand(0x01,Add1); //第0行开始(Y)地址
for(a = 8;a < 16;a++) //
{
WriteData(0x01,*(pt + a)); //上半部分8-16,总高度16,}
WriteCommand(0x01,0xb9); //第一页
WriteCommand(0x01,Add1); //
for(b = 0;b < 8;b++) //
{
WriteData(0x01,*(pt + b)); //下半部分
}
}
else if(SEL_E1 == 0) //若为0,写右半边
{
WriteCommand(0x02,0xb8); //
WriteCommand(0x02,Add1); //
for(a = 8;a < 16;a++) //
{
WriteData(0x02,*(pt + a)); //
}
WriteCommand(0x02,0xb9); //
WriteCommand(0x02,Add1); //
for(b = 0;b < 8;b++) //
{
WriteData(0x02,*(pt + b)); //
}
}
if((Add1 + 8) < 61)
Add1 += 8; //如果不超过61列列地址+8
else //
{
Add1 = 0; //超过61列,则列地址置0,写右半边
WriteCommand(0x02,0xb8); //
WriteCommand(0x02,Add1); //
for(a = 12;a < 16;a++) //一个字符占8列,所以在61列之后还要写4列
WriteData(0x02,*(pt + a)); //
WriteCommand(0x02,0xb9); //
WriteCommand(0x02,Add1); //
for(b = 4;b < 8;b++) //
WriteData(0x02,*(pt + b)); //
Add1 += 4; //
SEL_E1 = 0; //
}
}
// 写汉字
void WriteCharacter(unsigned char *pt)
{
unsigned char a,b;
if(SEL_E2) //
{
WriteCommand(0x01,0xba); //
WriteCommand(0x01,Add2); //
for(a=16;a<32;a++) //
{
WriteData(0x01,*(pt+a)); //
}
WriteCommand(0x01,0xbb); //
WriteCommand(0x01,Add2); //
for(b=0;b<16;b++) //
{
WriteData(0x01,*(pt+b)); //
}
else if(SEL_E2==0) //
{
WriteCommand(0x02,0xba); //
WriteCommand(0x02,Add2); //
for(a=16;a<32;a++) //
{
WriteData(0x02,*(pt+a)); //
}
WriteCommand(0x02,0xbb); //
WriteCommand(0x02,Add2); //
for(b=0;b<16;b++) //
{
WriteData(0x02,*(pt+b)); //
}
}
if((Add2+16)<61)
Add2+=16; //
else
{
Add2=0; //
WriteCommand(0x02,0xba); //
WriteCommand(0x02,Add2); //
for(a=29;a<32;a++)
WriteData(0x02,*(pt+a)); //一个汉字16列,写完61列之后还要写3列
WriteCommand(0x02,0xbb); //
WriteCommand(0x02,Add2); //
for(b=13;b<16;b++)
WriteData(0x02,*(pt+b)); //
Add2+=3; //
SEL_E2=0; //
}
}
//绘图
void Drawing_Map(unsigned char *pt)
{
unsigned char half,seg,page;
unsigned char flag = 1;
for(page = 0xb8;page < 0xbc;page++)
for(half = 0;half < 2;half++)
{
flag = !flag;
if(flag)
{
WriteCommand(0x02,page);
WriteCommand(0x02,0x00);
}
else
{
WriteCommand(0x01,page);
WriteCommand(0x01,0x00);
}
for(seg = 0;seg < 61;seg++)
{
if(flag)
{
WriteData(0x02,*pt++);
}
else
{
WriteData(0x01,*pt++);
}
}
}
}
}