tearsman520 发表于 2011-10-22 21:56:16

初始化LCD12864时遇到的有趣问题

最近在玩LCD12864,M051驱动,为了节省硬件引脚开支,采用4线驱动。
硬件连接:P2.7-P2.4 → D7-D4;
          P2.1 → RS;
          P2.2 → RW;
          P2.3 → E。

之前有做过LCD1602的4线模式,而1602和12864的大部分指令集是兼容的,当初写1602的库时,是直接从网上拿的他人的初始化代码,没有深入研究。我基本没对原始库做任何改动,初始化12864就成功了,也显示了出来,改了一下显示位置的函数,整屏显示都没问题了,今天突然想研究下初始化,于是拿出DATASHEET细细看了一番。按照datasheet的初始化流程怎么也不成功,忒奇怪,后来好不容易搞定。

原因是:我在写4BIT的数据发送程序时,先发高四位,在发低四位,而4BIT模式下,初始化只要求发高4位,我加了个标志位,就搞定了。贴上代码:
void LCD_Send_Data(unsigned char Data_Type, unsigned char Data, unsigned char ChkBF){
    if(ChkBF)LCD_Check_Busy();
    switch (Data_Type) {
    caseDT_Cmd:
      LCD_Set_Status(OP_Write_Cmd);
      break;
    caseDT_Data:
      LCD_Set_Status(OP_Write_Data);
      break;
    }

    LCD_Bus_Port &= 0x0F;
    LCD_Bus_Port |= (Data & 0xF0);         /* High 4bits */
    LCD_Positive_Pulse();

    System_Delay_Ms(15);

if(!Init_Flag) {
      LCD_Bus_Port &= 0x0F;
      LCD_Bus_Port |= ((Data & 0x0F) << 4);/* Low 4 bits */
      LCD_Positive_Pulse();
    }

    if(ChkBF)LCD_Check_Busy();
}

以上是发送数据的代码,改好后的初始化代码如下:

void LCD_Init(void){
System_Delay_Ms(800);
    Init_Flag = 1;
LCD_Send_Data(DT_Cmd, 0x20, 0);   /* 4BIT传送模式,只发高四位 */
    Init_Flag = 0;
    System_Delay_Ms(15);
LCD_Send_Data(DT_Cmd, 0x02, 0);   /* 复位AC位置 */
    System_Delay_Ms(15);
    LCD_Send_Data(DT_Cmd, 0x0C, 1);          /* 显示开启,无游标,无反白 */
    LCD_Send_Data(DT_Cmd, 0x06, 1);          /* 游标移动方向为正 */
    LCD_Send_Data(DT_Cmd, 0x01, 1);          /* 清除显示屏 */
    System_Delay_Ms(100);
}

上面的这段代码可以正常工作。如果不加入对标志位的控制,把第一条初始化指令改为0x03,则也能完成初始化!只是有点不太稳定。代码如下。

void LCD_Init(void){
System_Delay_Ms(800);
    Init_Flag = 0;
LCD_Send_Data(DT_Cmd, 0x03, 0);
//    Init_Flag = 0;
LCD_Send_Data(DT_Cmd, 0x20, 0);/* 4BIT模式了 */
LCD_Send_Data(DT_Cmd, 0x00, 0);
    System_Delay_Ms(15);
LCD_Send_Data(DT_Cmd, 0x0C, 1);
LCD_Send_Data(DT_Cmd, 0x06, 1);
    LCD_Send_Data(DT_Cmd, 0x01, 1);
    System_Delay_Ms(100);
}

我好奇了,于是继续更改初始化代码。

void LCD_Init(void){
System_Delay_Ms(800);
    Init_Flag = 0;
LCD_Send_Data(DT_Cmd, 0x03, 0);/* 如果直接去掉这条指令,初始化就不成功 */
System_Delay_Ms(15);
//    Init_Flag = 0;
LCD_Send_Data(DT_Cmd, 0x02, 0);/* 设置4BIT模式 */
// LCD_Send_Data(DT_Cmd, 0x00, 0);
    System_Delay_Ms(15);
LCD_Send_Data(DT_Cmd, 0x0C, 1);
LCD_Send_Data(DT_Cmd, 0x06, 1);
    LCD_Send_Data(DT_Cmd, 0x01, 1);
//    LCD_Send_Data(DT_Cmd, 0x06, 1);
    System_Delay_Ms(100);
}

这段代码还是可以工作!!而且很稳定。但是如果把首先发的0x03这条命令去掉,就不行了,先发0x02再发0x20还是不行。这是第一个奇怪的问题。

第二个奇怪的问题就是:对于进入点设定指令0x06(游标右移),必须放在清屏指令0x01之前,否则初始化也不成功!

贴上ST7920的初始化流程图和相关命令解释:

http://cache.amobbs.com/bbs_upload782111/files_46/ourdev_687498IDQ45A.JPG
(原文件名:LCD12864初始化流程图.JPG)

http://cache.amobbs.com/bbs_upload782111/files_46/ourdev_687499CSLVVV.JPG
(原文件名:进入点设定.JPG)

kingwaykingway 发表于 2011-10-22 22:29:23

不错不错。。赞一个。。。

tearsman520 发表于 2011-10-22 22:37:10

回复【1楼】kingwaykingway
-----------------------------------------------------------------------

啊……但是那两个问题我到现在还没搞懂……

machao 发表于 2011-10-23 14:44:33

不好,参考一下51的代码。

#include<reg52.h>
#include<string.h>
#define uchar unsigned char
#define uintunsigned int
#define DATA 1
#define COMMAND 0
sbit rs=P1^0;
sbit rw=P1^1;
sbit en=P1^2;
sbit psb=P1^3;
sbit rst=P1^4;

void delayUs(uint z);
void init();
void writeByte(uchar flag, uchar content);
void writeBytes(uchar address,uchar *buffer);

void main()
{
init();
writeBytes(0x80,"我爱祖国");
while(1);
}
void delayUs(uint z)
{
while(z--);
}
void init()
{
rst=0;
delayUs(40);
rst=1;
delayUs(40);
psb=1;
delayUs(8);
writeByte(COMMAND,0x20);//基本指令集 4bit数据
delayUs(8);
writeByte(COMMAND,0x01);
delayUs(8);
writeByte(COMMAND,0x06);
delayUs(8);
writeByte(COMMAND,0x0c);
delayUs(8);
}
void writeByte(uchar flag,uchar content)
{
delayUs(1000);
en=0;
delayUs(5);
if(flag)
rs=1;
else
rs=0;
rw=0;
en=1;
P0=content&0xf0;//数据高4位放到P0口高四位上

delayUs(10);
en=0;
if(flag)
rs=1;
else
rs=0;
delayUs(5);
rw=0;
en=1;
P0=(content<<4)&0xf0;//数据低4位放到P0口高四位上
en=0;
}
void writeBytes(uchar address,uchar *buffer)
{
uchar i;
i=strlen(buffer);
writeByte(COMMAND,address);
for(;i>0;i--)
writeByte(DATA,*(buffer++));
}

tearsman520 发表于 2011-10-23 15:09:21

回复【3楼】machao
-----------------------------------------------------------------------

我按照这样的风格修改了代码,0x20依然不行。

#include "LCD12864.h"

extern void System_Delay_Ms(unsigned int Delay_Time);

static unsigned char Init_Flag;

void LCD_Delay(unsigned int Delay){
    while(Delay) {
      Delay--;
    }
}

void LCD_Positive_Pulse(void){
    LCD_E = 1;
    LCD_Delay(300);
    LCD_E = 0;
}

void LCD_Set_Status(unsigned char Operation_Type){
    switch (Operation_Type) {
    case OP_Read_Status:
      LCD_RS = 0;
      LCD_RW = 1;
      break;
    case OP_Write_Cmd:
      LCD_RS = 0;
      LCD_RW = 0;
      break;
    case OP_Read_Data:
      LCD_RS = 1;
      LCD_RW = 1;
      break;
    case OP_Write_Data:
      LCD_RS = 1;
      LCD_RW = 0;
      break;
    }
}

void LCD_Check_Busy(void){
    unsigned char Busy_Flag;
        unsigned char Data_Tmp;
    do {
      LCD_Set_Status(OP_Read_Status);
//      LCD_Delay(40);                   /* Address set up time */
      LCD_E = 1;
                LCD_Delay(1000);
      Data_Tmp = LCD_Bus_Pin;
                Data_Tmp &= 0xF0;                   /* 获得高四位 */               
                LCD_E = 0;

                LCD_E = 1;
                LCD_Delay(1000);
                Busy_Flag = LCD_Bus_Pin;
                Busy_Flag >>= 4;
                Busy_Flag = Busy_Flag | Data_Tmp;      
                LCD_E = 0;
    } while (Busy_Flag & 0x80);
}

void LCD_Send_Data(unsigned char Data_Type, unsigned char Data, unsigned char ChkBF){
    if(ChkBF)LCD_Check_Busy();
//    switch (Data_Type) {
//    caseDT_Cmd:
//      LCD_Set_Status(OP_Write_Cmd);
//      break;
//    caseDT_Data:
//      LCD_Set_Status(OP_Write_Data);
//      break;
//    }
        if(Data_Type == DT_Cmd)LCD_RS = 0;
        else LCD_RS = 1;
        LCD_RW = 0;
        LCD_Bus_Port &= 0x0F;
        LCD_E = 1;
    LCD_Bus_Port |= (Data & 0xF0);         /* High 4bits */
//        LCD_Delay(1000);
        LCD_E = 0;
//    LCD_Positive_Pulse();

    System_Delay_Ms(15);

        if(!Init_Flag) {
          if(Data_Type == DT_Cmd)LCD_RS = 0;
          else LCD_RS = 1;
          LCD_RW = 0;
          LCD_Bus_Port &= 0x0F;
          LCD_E = 1;
      LCD_Bus_Port |= ((Data & 0x0F) << 4);/* Low 4 bits */
//                LCD_Delay(1000);
                LCD_E = 0;
//      LCD_Positive_Pulse();
    }

    if(ChkBF)LCD_Check_Busy();
}

void LCD_Clear(void){
    LCD_Send_Data(DT_Cmd, 0x01, 1);
    System_Delay_Ms(20);
}

void LCD_Putchar(unsigned char c){
    LCD_Send_Data(DT_Data, c, 1);
    System_Delay_Ms(2);
}

void LCD_Putstring(unsigned char* Str){
    while(*Str) {
      LCD_Putchar(*Str);
      Str ++;
    }
}

void LCD_Gotopos(unsigned char Row, unsigned char Column){
    unsigned char LCD_Addr;
        if(Row <= 2) {
                LCD_Addr = 0x80 + (Row - 1) * 0x10 + (((Column - 1)) % 8);
        }
        else
                LCD_Addr = 0x88 + (Row - 3) * 0x10 + (((Column - 1)) % 8);
        LCD_Send_Data(DT_Cmd, LCD_Addr, 1);
}

void LCD_Putnumber(unsigned int Number, unsigned char Width){
    unsigned char Num_Temp[]={0};
    unsigned char i, i_tmp;
    for(i = 0; i < Width; i++) {
      Num_Temp = Number % 10;
      Number /= 10;
    }
    for(i = Width; i > 0; i--) {
      if(Num_Temp == 0 && i != 1) LCD_Putchar('0');
      else{
            i_tmp = i;
            break;
      }
    }
    for(i = i_tmp; i > 0; i--) {
      LCD_Putchar(0x30 + Num_Temp);
    }
}

void LCD_Init(void){
        System_Delay_Ms(800);
        LCD_RESET = 0;
        System_Delay_Ms(1);
        LCD_RESET = 1;

        LCD_Send_Data(DT_Cmd, 0x20, 0);
        System_Delay_Ms(15);
//    Init_Flag = 0;
//        LCD_Send_Data(DT_Cmd, 0x02, 0);
//        LCD_Send_Data(DT_Cmd, 0x00, 0);
    System_Delay_Ms(15);
        LCD_Send_Data(DT_Cmd, 0x01, 1);
          System_Delay_Ms(15);

        LCD_Send_Data(DT_Cmd, 0x06, 1);
    System_Delay_Ms(15);

    LCD_Send_Data(DT_Cmd, 0x0c, 1);
    System_Delay_Ms(15);

//    LCD_Send_Data(DT_Cmd, 0x06, 1);
    System_Delay_Ms(100);
}

tearsman520 发表于 2011-10-23 15:38:32

直接移植!还是不行,只有0x02能通过初始化。

#include "LCD12864.h"

extern void System_Delay_Ms(unsigned int Delay_Time);

void delayUs(uint z)   
{
while(z--);
}
void init(void)
{
System_Delay_Ms(400);
rst=0;
System_Delay_Ms(1);
rst=1;
System_Delay_Ms(15);
// psb=1;
System_Delay_Ms(15);
writeByte(CMD,0x02);//基本指令集 4bit数据
System_Delay_Ms(15);
//writeByte(CMD,0x02);//基本指令集 4bit数据
System_Delay_Ms(15);
writeByte(CMD,0x01);
System_Delay_Ms(15);
writeByte(CMD,0x06);
System_Delay_Ms(15);
writeByte(CMD,0x0c);
System_Delay_Ms(15);
}
void writeByte(uchar flag,uchar content)
{
System_Delay_Ms(15);
en=0;
System_Delay_Ms(15);
if(flag)
rs=1;
else
rs=0;
rw=0;
P2_DOUT &= 0x0f;
en=1;
P2_DOUT |= content & 0xf0;//数据高4位放到P0口高四位上

System_Delay_Ms(1);
en=0;
if(flag)
rs=1;
else
rs=0;
System_Delay_Ms(15);
rw=0;
P2_DOUT &= 0x0f;
en=1;
P2_DOUT |= (content<<4) & 0xf0;//数据低4位放到P0口高四位上
en=0;
}
void writeBytes(uchar address,uchar *buffer)
{
uchar i;
i=strlen(buffer);
writeByte(CMD,address);
for(;i>0;i--)
writeByte(DATA,*(buffer++));
}

tearsman520 发表于 2011-10-23 15:41:26

4BIT时序图。

http://cache.amobbs.com/bbs_upload782111/files_46/ourdev_687817NAHPZ3.JPG
(原文件名:4BIT时序.JPG)
页: [1]
查看完整版本: 初始化LCD12864时遇到的有趣问题