单片机控制2004A液晶屏之模块化编程(持续更新中)

2019-04-15 12:25发布

这里将我写的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__*/