这里将我写的STC12C5A60S2单片机控制2004液晶屏的程序共享一下,是为了让前辈给予斧正。
最后一次更新:2014/05/14 15:30(终结篇)
(补充:以下代码只需要修改.h文件中含有 “选择” 字样的部分,就可以达到复用的效果,只需要注意中文注释部分)
程序中只需要关注中文注释部分
测试程序:
/*################main.c start################*/
#include
#include
#include "common.h"
#include "lcd2004.h"
void main(void)
{
UW16 i ;
UB8 author[]="Author:YangRui" ;
lcd2004Init();
for(i=0 ; i
/*################main.c end################*/
/*################lcd2004.h start################*/
#ifndef __LCD2004_H__
#define __LCD2004_H__
#include
#include "common.h"
sbit lcd2004_rs_bit = P2^6;/*根据硬件连接选择*/
sbit lcd2004_rw_bit = P2^5;/*根据硬件连接选择*/
sbit lcd2004_en_bit = P2^7;/*根据硬件连接选择*/
#define LCD2004_DATA_PORT P0/*根据硬件连接选择*/
/*default type:
px^0---DB0(LSB)
px^1---DB1
px^2---DB2
px^3---DB3
px^4---DB4
px^5---DB5
px^6---DB6
px^7---DB7(MSB)
*/
/*
RS Rw
0 0 ---->write command to lcd2004
0 1 ---->read lcd2004's status
1 0 ---->write data to lcd2004
1 1 ---->read lcd2004's data
*/
//lcd2004_rw_bit select
#define LCD2004_READ_OPERATION HIGH_LEVEL
#define LCD2004_WRITE_OPERATION LOW_LEVEL
//lcd2004_rs_bit select
#define LCD2004_DATA_OPERATION HIGH_LEVEL
#define LCD2004_COMMAND_OPERATION LOW_LEVEL
//lcd2004_en_bit select
#define LCD2004_ENABLE HIGH_LEVEL
#define LCD2004_DISABLE LOW_LEVEL
/*display:16(column)x2(row),5x7 lattice,8bit data interface*/
#define LCD2004_DEFAULT_DISPALY_MODE 0x38
/*
====================================================
lcd diaplay on/off and cursor display on/off configure
====================================================
/*
0 0 0 0 1 D C B
| | |------cursor blink : 1-->enable ; 0-->disalbe
| |----------cursor display: 1-->enable ; 0-->disable
|-------------lcd2004 dispaly: 1-->enable ; 0-->disalbe
*/
#define LCD2004_DISPLAY_ENABLE 0X04
#define LCD2004_CURSOR_DISPLAY_ENABLE 0X02
#define LCD2004_CURSOR_BLINK_ENABLE 0X01
#define LCD2004_DISPLAY_DISABLE (0X04 & (~(0x01<<2)))
#define LCD2004_CURSOR_DISPLAY_DISABLE (0X02 & (~(0x01<<1)))
#define LCD2004_CURSOR_BLINK_DISABLE (0X01 & (~(0x01<<0)))
/*默认:lcd显示,光标显示,光标闪烁*/ /*根据实际需要选择*/
#define LCD2004_DEFAULT_DISPLAY_AND_CURSOR_MODE 0x08 |
LCD2004_DISPLAY_ENABLE |
LCD2004_CURSOR_DISPLAY_ENABLE|
LCD2004_CURSOR_BLINK_ENABLE
/*
====================================================
point and cursor address configure
====================================================
0 0 0 0 0 1 N S
| |----- screen move:1-->enable ; 0-->disable
|--------- N=1:读或者写一个字符后地址指针加1,且光标加1
N=0:度或者写一个字符后地址指针减1,且光标减1
当S=1时 :
若N=1,整屏幕左移
N=0,整屏幕右移
以达到光标不移动,而整个屏幕移动的效果
*/
#define LCD2004_POINT_AND_CURSOR_PLUS 0X02
#define LCD2004_SCREEN_MOVE_ENABLE 0x01
#define LCD2004_POINT_AND_CURSOR_MINUS (0X02 & (~(0X01<<1)))
#define LCD2004_SCREEN_MOVE_DISABLE (0x01 & (~(0X01<<0)))
/*默认:光标和指针加一,屏幕不移动*/
#define LCD2004_DEFAULT_POINT_AND_POINT_ADDRESS_MODE 0X04 |
LCD2004_POINT_AND_CURSOR_PLUS|
LCD2004_SCREEN_MOVE_DISABLE
#define LCD2004_ROW0 0
#define LCD2004_ROW1 1
#define LCD2004_ROW2 2
#define LCD2004_ROW3 3
#define LCD2004_MIN_ROW LCD2004_ROW0
#define LCD2440_MAX_ROW LCD2004_ROW3
#define LCD2004_ROW_LENGHT 0X14 //20
#define LCD2004_ROW0_ADDRESS_START (0X80)
#define LCD2004_ROW1_ADDRESS_START (0X80+0X40)
#define LCD2004_ROW2_ADDRESS_START (LCD2004_ROW0_ADDRESS_START+LCD2004_ROW_LENGHT)
#define LCD2004_ROW3_ADDRESS_START (LCD2004_ROW1_ADDRESS_START+LCD2004_ROW_LENGHT)
//instruction:
#define LCD2004_CLEAN_ALL_DISPALY 0x01/*清屏*/
#define LCD2004_CURSOR_RETURN_TO_ORIGIN 0x02/*光标归位*/
//写命令
extern void lcd2004WriteCommand(UB8 commandCode);
//写数据
extern void lcd2004WriteData(UB8 dataCode);
//lcd20041清屏
extern void lcd2004CleanAll(void) ;
//光标归位
extern void lcd2004CursorHoming(void);
//lcd2004初始化
extern void lcd2004Init(void) ;
//lcd2004地址写字节
extern void lcd2004AddressWriteByte(UB8 row,UB8 column,UB8 dataCode) ;
//lcd2004地址写字符串
extern void lcd2004AddressWriteString(UB8 row,UB8 column,UB8 *stringCode);
#endif /*__LCD2004_H__*/
/*################lcd2004.h end################*/
/*################lcd2004.c start################*/
/***************************************************************************
申明
经过测试J204A(也称2004A,每行20字符,显示4行)液晶屏和1602液晶屏的
使用方法几乎是一样的,不过也是有区别的,在1602液晶屏中,用来寄存待显
示字符代码的DDRAM共有80个字节,而J204A能够一次性显示的字符是80字
节,这里不是巧合,即J204A只是将LCD1602的DDRAM的80个字符全部显示出
来而已。
第一行首地址 :0x80
第二行首地址 :0x80+0x40
第三行首地址 :0x80+20
第四行首地址 :0x80+0x40+20
可以注意到,第一行和第三行、第二行和第四行的地址是分别连续的。 经过
实验验证,可以得出这样的结论:J204A是将LCD1602的DDRAM的0x00~0x13地址
的数据映射显示在J204A的第一行,LCD1602的DDRAM的0x14~0x27地址的数据映
射显示在第三行 ; LCD1602的DDRAM的0x40~0x53地址的数据映射显示在J204A
的第二行,将LCD1602的DDRAM的0x54~0x67地址的数据映射显示在J204A的第四行。
----------------------------------------------------------------------------
J204A的程序和LCD1602的程序很相似,这里的程序是由LCD1602程序修改而来。
----------------------------------------------------------------------------
***************************************************************************/
/***************************************************************************
Module :lcd2004.c
Purpose :Implementation of lcd2004 module.
Version :0.01 2014/02/13 12:00(OK)
Complier:Keil 8051 C complier V9.01
MCU :STC12C5A60S2
Author :yangrui
QQ :279729201
Email :yangrui90s@163.com
Modification:
=================
2014/04/31 20:09
Reson:
1.解决大工程中,因未安装LCD2004而导致的工程卡死在函数
lcd2004CheckBusy()的问题,将
do{
;
}while( (lcd2004ReadStatus()) & 0x80));
修改为
do{
i++;
}while( ((lcd2004ReadStatus()) & 0x80) && (i<10));
因为在未安装LCD2004时,通过"读状态"函数lcd2004ReadStatus()读回来的是0xff,
如果采用第一种算法,会导致程序"卡死"。
=================
=================
2014/02/27 08:25
Reson:
1.修改函数SB8 lcd2004AddressWriteByte(...)
{
if(column<0|| column > 19||
( (row != LCD2004_ROW0) &&(row != LCD2004_ROW1))&&
(row != LCD2004_ROW2) &&(row != LCD2004_ROW3))
)
{
return -1;
}
......
return 0 ;
}
为
void lcd2004AddressWriteByte(UB8 row,UB8 column,UB8 dataCode)
{
//省略if判断
//无返回值
}
这里省略判断是因为LCD2004和LCD1602的特殊关系,在LCD1602的DDRAM中
共有80个字符,每一行的显示缓存有40个字符长度,为了调用的安全性,
在lcd1602.c中相同功能的函数是有if判断,但是对于LCD2004,第三行和
第四行所显示的内容分别就是LCD1602的DDRAM的第一行的显示缓冲DDRAM的
后20个字符、第二行的显示缓冲DDRAM的后20个字符。这可以通过以下方法
验证:
首先,省略if判断,然后调用函数
lcd2004AddressWriteString(LCD2004_ROW2,0,"2014/02/27");
和调用函数
lcd2004AddressWriteString(LCD2004_ROW0,20,"2014/02/27");
效果是一样的
为了调用的灵活性,这里省略了if判断,并将函数改为无返回值的类型。
=================
=================
2014/02/24 23:44
Reson:
1.增加函数lcd2004AddressWriteString(...)
=================
***************************************************************************/
#include
#include
#include
#include "lcd2004.h"
/*外部接口函数在lcd2004.h中声明*/
/*****************内部函数******************/
static void delay1msForLcd2004(void) ;
static UB8 lcd2004ReadStatus(void) ;
static void lcd2004CheckBusy(void) ;
/**********************************************/
/******************************************************
Function :delay1msForLcd2004
Input :N/A
Output :N/A
Return :N/A
Description :N/A
Note :crystal frequency is 11.0592MHZ
******************************************************/
static void delay1msForLcd2004(void)
{
unsigned char i, j;
_nop_();
_nop_();
i = 12;
j = 168;
do
{
while (--j);
} while (--i);
}
/******************************************************
Function :lcd2004ReadStatus
Input :N/A
Output :N/A
Return :lcd2004 status value
Description :N/A
Note :N/A
******************************************************/
static UB8 lcd2004ReadStatus(void)
{
UB8 statusCode ;
lcd2004_en_bit = LCD2004_DISABLE ;
lcd2004_rs_bit = LCD2004_COMMAND_OPERATION ;
lcd2004_rw_bit = LCD2004_READ_OPERATION ;
LCD2004_DATA_PORT = 0xff ;
delay1msForLcd2004() ;
lcd2004_en_bit = LCD2004_ENABLE ;
delay1msForLcd2004() ;
statusCode = LCD2004_DATA_PORT ;
lcd2004_en_bit = LCD2004_DISABLE ;
return statusCode ;
}
/******************************************************
Function :lcd2004CheckBusy
Input :N/A
Output :N/A
Return :N/A
Description :check lcd2004 busy or free,if busy,wait !
Note :when LCD2004_DATA_PORT[7]==1,lcd2004 is busy,
LCD2004_DATA_PORT[7]==0,lcd2004 is free.
补充:这里的“10”是经过测试的,测试中,利用串口打印
可知,“清除屏幕显示”命令“lcd2004CleanAll()”用时最长,
此命令下i值为1,这里设置i为10,足够大了
******************************************************/
static void lcd2004CheckBusy(void)
{
UW16 i=0 ;
do{
i++;
}while( (lcd2004ReadStatus() & 0x80) && (i<10));
}
/******************************************************
Function :lcd2004WriteCommand
Input :command code
Output :N/A
Return :N/A
Description :write command to lcd2004
Note :N/A
******************************************************/
void lcd2004WriteCommand(UB8 commandCode)
{
lcd2004CheckBusy();
lcd2004_en_bit = LCD2004_DISABLE ;
lcd2004_rs_bit = LCD2004_COMMAND_OPERATION ;/*command*/
lcd2004_rw_bit = LCD2004_WRITE_OPERATION ; /*write*/
LCD2004_DATA_PORT = commandCode ;
//delay1msForLcd2004();
lcd2004_en_bit = LCD2004_ENABLE ;
//delay1msForLcd2004();
lcd2004_en_bit = LCD2004_DISABLE ;
}
/******************************************************
Function :lcd2004WriteData
Input :data code
Output :N/A
Return :void
Description :write data to lcd2004
Note :N/A
******************************************************/
void lcd2004WriteData(UB8 dataCode)
{
lcd2004CheckBusy() ;
lcd2004_en_bit = LCD2004_DISABLE ;
lcd2004_rs_bit = LCD2004_DATA_OPERATION ; /*data*/
lcd2004_rw_bit = LCD2004_WRITE_OPERATION ; /*write*/
LCD2004_DATA_PORT = dataCode;
//delay1msForLcd2004();
lcd2004_en_bit = LCD2004_ENABLE ;
//delay1msForLcd2004();
lcd2004_en_bit = LCD2004_DISABLE ;
}
/******************************************************
Function :lcd2004CleanAll
Input :N/A
Output :N/A
Return :N/A
Description :clean lcd2004 display
Note :清除屏幕显示,光标归位(左上角),地址计数器AC设为0
******************************************************/
void lcd2004CleanAll(void)
{
lcd2004WriteCommand(LCD2004_CLEAN_ALL_DISPALY);
}
/******************************************************
Function :lcd2004CursorHoming
Input :N/A
Output :N/A
Return :N/A
Description :curosr homing to the 0x80
Note :光标归位,当屏幕移动显示时,lcd2004显示所有数据后,
调用此函数,屏幕显示的所有东西都会归位。光标在第一
个位置(0x80)。
******************************************************/
void lcd2004CursorHoming(void)
{
lcd2004WriteCommand(LCD2004_CURSOR_RETURN_TO_ORIGIN);
}
/******************************************************
Function :lcd2004Init
Input :N/A
Output :N/A
Return :N/A
Description :N/A
Note :N/A
******************************************************/
void lcd2004Init(void)
{
lcd2004_en_bit = LCD2004_DISABLE ;
lcd2004CleanAll();
lcd2004WriteCommand(LCD2004_DEFAULT_DISPALY_MODE);
lcd2004WriteCommand(LCD2004_DEFAULT_DISPLAY_AND_CURSOR_MODE);
lcd2004WriteCommand(LCD2004_DEFAULT_POINT_AND_POINT_ADDRESS_MODE);
/*可忽略,在lcd2004CleanAll()中隐含了该功能*/
lcd2004WriteCommand(LCD2004_ROW0_ADDRESS_START);
lcd2004_en_bit = LCD2004_DISABLE ;
}
/******************************************************
Function :lcd2004AddressWriteByte
Input :行位置
列位置
数据
Output :N/A
Return :N/A
Description :N/A
Note :N/A
******************************************************/
void lcd2004AddressWriteByte(UB8 row,UB8 column,UB8 dataCode)
{
if(row == LCD2004_ROW0)
{
lcd2004WriteCommand(LCD2004_ROW0_ADDRESS_START+column) ;
}
else if(row == LCD2004_ROW1)
{
lcd2004WriteCommand(LCD2004_ROW1_ADDRESS_START+column) ;
}
else if(row == LCD2004_ROW2)
{
lcd2004WriteCommand(LCD2004_ROW2_ADDRESS_START+column) ;
}
else if(row == LCD2004_ROW3)
{
lcd2004WriteCommand(LCD2004_ROW3_ADDRESS_START+column) ;
}
lcd2004WriteData(dataCode);
}
/******************************************************
Function :lcd2004AddressWriteString
Input :首个字符显示的行,列地址,字符串
Output :N/A
Return :N/A
Description :N/A
Note :这里使用的是strlen,而不是sizeof
******************************************************/
void lcd2004AddressWriteString(UB8 row,UB8 column,UB8 *stringCode)
{
UB8 length = strlen(stringCode) ;
if(row == LCD2004_ROW0)
{
lcd2004WriteCommand(LCD2004_ROW0_ADDRESS_START+column) ;
}
else if(row == LCD2004_ROW1)
{
lcd2004WriteCommand(LCD2004_ROW1_ADDRESS_START+column) ;
}
if(row == LCD2004_ROW2)
{
lcd2004WriteCommand(LCD2004_ROW2_ADDRESS_START+column) ;
}
else if(row == LCD2004_ROW3)
{
lcd2004WriteCommand(LCD2004_ROW3_ADDRESS_START+column) ;
}
while(length--)
{
lcd2004WriteData(*stringCode);
stringCode++;
}
}
/*################lcd2004.c end################*/
补充:common.h
#ifndef __COMMON_H__
#define __COMMON_H__
typedef unsigned char UB8 ;
typedef unsigned short int UW16 ;
typedef unsigned long UL32 ;
typedef char SB8;
typedef short int SW16 ;
typedef long SL32 ;
#define HIGH_LEVEL 1
#define LOW_LEVEL 0
#endif /*__COMMON_H__*/