6.2C语言实现液晶显示
字符型液晶显示器用于显示数字、字母、图形符号。这类显示器均把 LCD 控制器、点
单片机的C 语言轻松入门
43
阵驱动器、字符存贮器等做在一块板上,再与液晶屏一起组成一个显示模块,因此,这类显
示器安装与使用都较简单。
图 6-2 是DSB-1A 实验板上字符型液晶的接口电路。要求编写程序从该显示器的第二行
第1 列开始显示“Hello World!”。
由于市面上常见的字符型液晶驱动器均由 HD44780 或其兼容芯片构成,因此,这类液
晶屏的驱动程序具有一定的通用性,这里给出用C 语言写的驱动程序。在设置字符的起始
行、列后,直接调用驱动程序提供的WriteString 函数即可将字符串显示在指定的位置,使
用非常简单。在熟悉了程序后,略作改动,可用于2002、2004 等型号的液晶屏。
程序如下:
/**************************************************
* Copyright 2003 pingfan's McuStudio
* All rights Reserved
*作者:周坚
*yj.c
*连线图:
* DB0---DPROT.0 DB4---DPROT.4 RS-------------P2.5
* DB1---DPROT.1 DB5---DPROT.5 RW-------------P2.6
* DB2---DPROT.2 DB6---DPROT.6 E--------------P2.7
* DB3---DPROT.3 DB7---DPROT.7 VLCD 接10K 可调电阻到GND*
*80C51 的晶振频率为12MHz
*液晶显示程序
***************************************************/
#include "reg51.h"
#include<absacc.h>
#include <intrins.h>
#define DPORT P0
#define uchar unsigned char
sbit RS = P2^5;
sbit RW = P2^6;
sbit E = P2^7;
DATA PORT
P0.0 DB0
P0.7 DB7
RS
R/W
E
P2.5
P2.6
P2.7
+5V
GND
VL
BLK
BLA
80C51
uchar Xpos; //列方向地址指针
uchar Ypos; //行方向地址指针
#define NoDisp 0
#define NoCur 1
#define CurNoFlash 2
#define CurFlash 3
/*延时程序
由 Delay 参数确定延迟时间
*/
void LcdWcn(uchar);
void LcdWc(uchar);
void WriteChar(uchar);
void LcdPos();
void LcdWd(uchar);
void LcdWdn(uchar);
void mDelay(unsigned int Delay)
{ unsigned int i;
for(;Delay>0;Delay--)
{ for(i=0;i<124;i++)
{;}
}
}
/*光标设置命令
Cur 为设定光标参数
*/
void SetCur(uchar Cur)
{ switch(Cur)
{ case 0x0:
{ LcdWc(0x08); //关显示
break;
}
case 0x1:
{ LcdWc(0x0c); //开显示但无光标
break;
}
case 0x2:
{ LcdWc(0x0e); //开显示有光标但不闪烁
break;
}
case 0x3:
{ LcdWc(0x0f); //开显示有光标且闪烁
break;
}
default: break;
}
}
/*清屏命令
*/
void ClrLcd()
{ LcdWc(0x01);
}
/*在指定的行与列显示
*/
void WriteChar(uchar c)
{ LcdPos();
LcdWd(c);
}
/*正常读写操作之前检测LCD 控制器
*/
void WaitIdle()
{ uchar tmp;
DPORT=0xff;
RS=0;
RW=1;
E=1;
_nop_();
for(;;)
{ tmp=DPORT;
tmp&=0x80;
if(tmp==0)
break;
}
E=0;
}
/*不检测忙的写字符子程序
*/
void LcdWdn(uchar c)
{
RS=1;
RW=0;
DPORT=c; //写入待写字符
E=1;
_nop_();
单片机的C 语言轻松入门
46
E=0;
}
/*带忙检测的写字符子程序
*/
void LcdWd(uchar c)
{ WaitIdle();
LcdWdn(c);
}
/*检测忙信号的送控制字子程序*/
void LcdWcn(uchar c)
{ RS=0;
RW=0;
DPORT=c;
E=1;
_nop_();
E=0;
}
/*检测忙信号的送控制字子程序*/
void LcdWc(uchar c)
{ WaitIdle();
LcdWcn(c);
}
void LcdPos()
{ uchar tmp;
Xpos&=0x0f; //16xx 型液晶的范围是0~15
Ypos&=0x01; //Y 的范围是0~1
tmp=Xpos;
if(Ypos==1)
{ tmp+=0x40;
}
tmp|=0x80;
LcdWc(tmp);
}
/*LCD 的复位程序
*/
void RstLcd()
{ mDelay(15); //延时15ms
LcdWcn(0x38);
mDelay(5);
LcdWcn(0x38);
mDelay(5);
LcdWcn(0x38);
LcdWc(0x38);
LcdWc(0x08);
LcdWc(0x01);
LcdWc(0x06);
LcdWc(0x0c);
}
void WriteString(char s[])
{ uchar pS=0;
for(;;)
{ WriteChar(s[pS]);
pS++;
if(s[pS]==0)
break;
if(++Xpos>=15) //每行最多显示16 个字符
break;
}
}
void main()
{ uchar s1[]="Hellow World!";
RstLcd(); //复位LCD
ClrLcd();
SetCur(CurFlash); //光标显示且闪烁
Xpos=2;
Ypos=1;
WriteString(s1);
for(;;)
{;}
}
程序分析:本程序中大量使用了函数,在此对函数的功能作一个简介。
C 语言程序是由一个个函数构成的,从函数定义的形式上划分,函数有三种形式:无参
数函数、有参数函和空函数。
无参数函数的定义形式为:
返回值类型识别符 函数名(){函数体语句}
如本例中的 void WaitIdle(){ …… }就是一个无参数函数
有参数函数的定义形式为:
返回值类型识别符 函数名(形式参数列表){函数体语句}
如本例中的 void LcdWdn(uchar c){ …… }就是一个有参数的函数
函数可以返回一个值,也可以什么值也返回,如果函数要返回一个值,在定义这个函数
时要定义好这个值的数据类型,这里所说的数据类型就是指前面课程中介绍到的int、char、
float 等类型,如果在定义函数时没有定义返回值的类型,系统默认为返回一个int 型的值。
如果明确地知道一个函数将没有返回值,可以将其定义为void 型,这样,如果在调用函数
时错误地使用了“变量名=函数名”的方式来调用函数,编译器就能发现这一错误并指出。
本例中就大量地应用到了void 型函数。
C 语言采用函数之间的参数传递方式,这使得一个函数能对不同的变量进行功能相同的
处理,使函数具有了通用性。定义函数时,写在函数名括号中的称之为形式参数,而在实际
调用函数时写在函数括号中的称之为实际参数。本例中:
void SetCur(uchar Cur)
{ …}
函数中Cur 就是一个形式参数,而在主函数中调用时写的:
SetCur(CurFlash);
其中CurFlash 就是一个用符号常量表示的实际参数,在执行该函数时该值被传递到函
数内部并执行。
每一个函数所调用的函数必须已被定义,否则就会出现语法错误,因此程序中一般要求
在程序的开头对程序中用到的函数进行统一的说明,然后再分别定义有关函数,本例中有:
void WriteChar(uchar);
…
void LcdWdn(uchar);
就是首先在程序的前方写一个有关函数的说明,而真正的函数定义则在程序放在后部。
但细心的读者可能发现有一些函数并未写其说明,而是直接在程序中定义了,如mDelay 函
数,这是为何呢?这是因为这些函数出现在程序的前面,在还没有任何函数调用它们之前它
们就被定义了,因此就不需要再单独写一个函数说明。读者可将mDelay 函数的定义移到程
序的后面位置,再次编译就会出错。当然,好的编程习惯是不论函数在何处被定义,总是在
写前面写一个函数说明。
有关单片机的 C 语言编程到此就告一个段落,虽然C 语言很多特性尚未介绍,但通过
上面有关内容的学习,我们已经可以使用C 语言进行一些实际的工程开发工作,大家可以
在工作中继续学习有关C 语言的知识。
单片机的C 语言轻松入门
43
阵驱动器、字符存贮器等做在一块板上,再与液晶屏一起组成一个显示模块,因此,这类显
示器安装与使用都较简单。
图 6-2 是DSB-1A 实验板上字符型液晶的接口电路。要求编写程序从该显示器的第二行
第1 列开始显示“Hello World!”。
由于市面上常见的字符型液晶驱动器均由 HD44780 或其兼容芯片构成,因此,这类液
晶屏的驱动程序具有一定的通用性,这里给出用C 语言写的驱动程序。在设置字符的起始
行、列后,直接调用驱动程序提供的WriteString 函数即可将字符串显示在指定的位置,使
用非常简单。在熟悉了程序后,略作改动,可用于2002、2004 等型号的液晶屏。
程序如下:
/**************************************************
* Copyright 2003 pingfan's McuStudio
* All rights Reserved
*作者:周坚
*yj.c
*连线图:
* DB0---DPROT.0 DB4---DPROT.4 RS-------------P2.5
* DB1---DPROT.1 DB5---DPROT.5 RW-------------P2.6
* DB2---DPROT.2 DB6---DPROT.6 E--------------P2.7
* DB3---DPROT.3 DB7---DPROT.7 VLCD 接10K 可调电阻到GND*
*80C51 的晶振频率为12MHz
*液晶显示程序
***************************************************/
#include "reg51.h"
#include<absacc.h>
#include <intrins.h>
#define DPORT P0
#define uchar unsigned char
sbit RS = P2^5;
sbit RW = P2^6;
sbit E = P2^7;
DATA PORT
P0.0 DB0
P0.7 DB7
RS
R/W
E
P2.5
P2.6
P2.7
+5V
GND
VL
BLK
BLA
80C51
uchar Xpos; //列方向地址指针
uchar Ypos; //行方向地址指针
#define NoDisp 0
#define NoCur 1
#define CurNoFlash 2
#define CurFlash 3
/*延时程序
由 Delay 参数确定延迟时间
*/
void LcdWcn(uchar);
void LcdWc(uchar);
void WriteChar(uchar);
void LcdPos();
void LcdWd(uchar);
void LcdWdn(uchar);
void mDelay(unsigned int Delay)
{ unsigned int i;
for(;Delay>0;Delay--)
{ for(i=0;i<124;i++)
{;}
}
}
/*光标设置命令
Cur 为设定光标参数
*/
void SetCur(uchar Cur)
{ switch(Cur)
{ case 0x0:
{ LcdWc(0x08); //关显示
break;
}
case 0x1:
{ LcdWc(0x0c); //开显示但无光标
break;
}
case 0x2:
{ LcdWc(0x0e); //开显示有光标但不闪烁
break;
}
case 0x3:
{ LcdWc(0x0f); //开显示有光标且闪烁
break;
}
default: break;
}
}
/*清屏命令
*/
void ClrLcd()
{ LcdWc(0x01);
}
/*在指定的行与列显示
*/
void WriteChar(uchar c)
{ LcdPos();
LcdWd(c);
}
/*正常读写操作之前检测LCD 控制器
*/
void WaitIdle()
{ uchar tmp;
DPORT=0xff;
RS=0;
RW=1;
E=1;
_nop_();
for(;;)
{ tmp=DPORT;
tmp&=0x80;
if(tmp==0)
break;
}
E=0;
}
/*不检测忙的写字符子程序
*/
void LcdWdn(uchar c)
{
RS=1;
RW=0;
DPORT=c; //写入待写字符
E=1;
_nop_();
单片机的C 语言轻松入门
46
E=0;
}
/*带忙检测的写字符子程序
*/
void LcdWd(uchar c)
{ WaitIdle();
LcdWdn(c);
}
/*检测忙信号的送控制字子程序*/
void LcdWcn(uchar c)
{ RS=0;
RW=0;
DPORT=c;
E=1;
_nop_();
E=0;
}
/*检测忙信号的送控制字子程序*/
void LcdWc(uchar c)
{ WaitIdle();
LcdWcn(c);
}
void LcdPos()
{ uchar tmp;
Xpos&=0x0f; //16xx 型液晶的范围是0~15
Ypos&=0x01; //Y 的范围是0~1
tmp=Xpos;
if(Ypos==1)
{ tmp+=0x40;
}
tmp|=0x80;
LcdWc(tmp);
}
/*LCD 的复位程序
*/
void RstLcd()
{ mDelay(15); //延时15ms
LcdWcn(0x38);
mDelay(5);
LcdWcn(0x38);
mDelay(5);
LcdWcn(0x38);
LcdWc(0x38);
LcdWc(0x08);
LcdWc(0x01);
LcdWc(0x06);
LcdWc(0x0c);
}
void WriteString(char s[])
{ uchar pS=0;
for(;;)
{ WriteChar(s[pS]);
pS++;
if(s[pS]==0)
break;
if(++Xpos>=15) //每行最多显示16 个字符
break;
}
}
void main()
{ uchar s1[]="Hellow World!";
RstLcd(); //复位LCD
ClrLcd();
SetCur(CurFlash); //光标显示且闪烁
Xpos=2;
Ypos=1;
WriteString(s1);
for(;;)
{;}
}
程序分析:本程序中大量使用了函数,在此对函数的功能作一个简介。
C 语言程序是由一个个函数构成的,从函数定义的形式上划分,函数有三种形式:无参
数函数、有参数函和空函数。
无参数函数的定义形式为:
返回值类型识别符 函数名(){函数体语句}
如本例中的 void WaitIdle(){ …… }就是一个无参数函数
有参数函数的定义形式为:
返回值类型识别符 函数名(形式参数列表){函数体语句}
如本例中的 void LcdWdn(uchar c){ …… }就是一个有参数的函数
函数可以返回一个值,也可以什么值也返回,如果函数要返回一个值,在定义这个函数
时要定义好这个值的数据类型,这里所说的数据类型就是指前面课程中介绍到的int、char、
float 等类型,如果在定义函数时没有定义返回值的类型,系统默认为返回一个int 型的值。
如果明确地知道一个函数将没有返回值,可以将其定义为void 型,这样,如果在调用函数
时错误地使用了“变量名=函数名”的方式来调用函数,编译器就能发现这一错误并指出。
本例中就大量地应用到了void 型函数。
C 语言采用函数之间的参数传递方式,这使得一个函数能对不同的变量进行功能相同的
处理,使函数具有了通用性。定义函数时,写在函数名括号中的称之为形式参数,而在实际
调用函数时写在函数括号中的称之为实际参数。本例中:
void SetCur(uchar Cur)
{ …}
函数中Cur 就是一个形式参数,而在主函数中调用时写的:
SetCur(CurFlash);
其中CurFlash 就是一个用符号常量表示的实际参数,在执行该函数时该值被传递到函
数内部并执行。
每一个函数所调用的函数必须已被定义,否则就会出现语法错误,因此程序中一般要求
在程序的开头对程序中用到的函数进行统一的说明,然后再分别定义有关函数,本例中有:
void WriteChar(uchar);
…
void LcdWdn(uchar);
就是首先在程序的前方写一个有关函数的说明,而真正的函数定义则在程序放在后部。
但细心的读者可能发现有一些函数并未写其说明,而是直接在程序中定义了,如mDelay 函
数,这是为何呢?这是因为这些函数出现在程序的前面,在还没有任何函数调用它们之前它
们就被定义了,因此就不需要再单独写一个函数说明。读者可将mDelay 函数的定义移到程
序的后面位置,再次编译就会出错。当然,好的编程习惯是不论函数在何处被定义,总是在
写前面写一个函数说明。
有关单片机的 C 语言编程到此就告一个段落,虽然C 语言很多特性尚未介绍,但通过
上面有关内容的学习,我们已经可以使用C 语言进行一些实际的工程开发工作,大家可以
在工作中继续学习有关C 语言的知识。