ruan18278816371 发表于 2014-5-2 23:17:17

老生常谈一个令我不解的12864液晶显示汉字!!!

以前都说KEIL环境下对一些汉字的显示有BUG,需要打补丁。补丁早已打过我遇到的不是这个问题,为了好说明问题我还是上传液晶部分的代码,结合代码讲述吧。
void KeyProcess(byte value)
{
        lcd_init();//初始化LCD
        P0=0xff;//先将液晶数据口拉高
        if(value==0)//不论长按键或短按键按下都执行
        {        
               int i=0;
               byte code table[] = {"正在采集数据"};
               lcd_pos(1,0);//显示在第二行第一个字符开始
               for(i;i<12;i++)
               //while(table!='\0')这里注释掉用上面的for循环确保循环次数
               {
                       printf("i=%d",i);//串口查看循环次数
                       write_dat(table);//写要显示的数据
                       //i++;
               }
        }
       else if(value==1)
        {
               int i=0;
               byte codetable3[] = {"温度为:"};
               byte codetable4[] = {"湿度为:"};
               lcd_pos(2,0);//设置显示在第三行
               while(table3!='\0')
               {         
                       write_dat(table3);
                       i++;
               }
               i=0;
               lcd_pos(3,0);
               while(table4!='\0')
               {         
                       write_dat(table4);
                       i++;
               }
        }
       else if(value==2)
        {
               int i=0;
               byte codetable1[] = {"    系统关闭"};//前后有两个空格,主要是为了使汉字显示在液晶中间,美观
               byte codetable2[] = {"欢迎再次使用 "};//前后一个空格
               lcd_pos(1,0);//设置显示在第二行
               while(table1!='\0')
               {         
                       write_dat(table1);
                       i++;
               }
               i=0;
               lcd_pos(2,0);//设置显示在第三行
               while(table2!='\0')
               {         
                       write_dat(table2);
                       i++;
               }
        }
        else        //都没有按键按下
                mmi();//保持在界面初始化,这个函数在液晶上第一行显示“欢迎使用”第二行显示“环境监测系统”第三行显示“很高兴能为您服务”
}
这个服务程序是通过按下按键来切换相应的界面,液晶初始化是没有问题的,界面初始化也没问题,在条件value==2下的液晶显示汉字部分也没有问题,问题就出现在条件在value==0和条件value==1要显示的部分,只要我写入要显示的汉字超过三个就显示就会出错(出现乱码或者数据丢失),我用printf函数在串口上追踪查看,发现循环数据的次数是够的,可在液晶上显示的就是乱码或其他不是我写入的数据。我实在不解啊!!找了好久的原因都没找出来。这时也许有人会说可能你value==0和条件value==1成立的条件有问题,好,我把value==2条件下的汉字显示放到value==0和条件value==1中,按下对应的按键显示是正常的,我想更不是硬件问题,要是硬件问题就不可能界面初始化成功了。万能的论坛,各位大牛求解啊!!!

lryxr2507 发表于 2014-5-2 23:29:34

注意字符(含空格),一定要保持是两个,因为一个汉字是占两个字符,比如: 1 ,如果后面需要汉字,一定要在1后面空一格,才能在下一个地址写汉字.

lryxr2507 发表于 2014-5-2 23:33:08

你拷贝这段进去就知道了:

                byte codetable1[] = {"    系统关闭    "};//前后有两个空格,主要是为了使汉字显示在液晶中间,美观
               byte codetable2[] = {"   欢迎再次使用"};//前后一个空格

ruan18278816371 发表于 2014-5-2 23:44:01

lryxr2507 发表于 2014-5-2 23:29
注意字符(含空格),一定要保持是两个,因为一个汉字是占两个字符,比如: 1 ,如果后面需要汉字,一定要在1后面空 ...

是的,这我知道,我这里没犯这个错误啊。“正在采集数据”这儿是没有空格,我这里在液晶上显示还是有乱码的

wmm20031015 发表于 2014-5-3 00:16:21

串口输出一下写到屏的里的数据是不是对的{:lol:}

fog_wizard 发表于 2014-5-3 07:43:45

是不是7920的液晶主控,把数据从屏上读取下来,看看和写进入的是否一致。

ZHW0117 发表于 2014-5-3 08:47:38

强烈收藏啊!一定要搞明白

lu0718 发表于 2014-5-6 09:14:15

LZ如何打的补丁?

颜靖峰 发表于 2014-5-7 20:54:21

#include <intrins.h>
#include "STC15F2K60S2.H"
#include "12864.h"
#define uintunsigned int
#define uchar unsigned char
sbit LCM_RS= P4^5;   //模式位,为0输入指令,为1输入数据
sbit LCM_RW= P2^7;   //读写位,为0读,为1写
sbit LCM_EN= P2^6;   //使能位,高脉冲      
#define Lcd_Bus P0   //LCM12864数据总线,P0.0--P0.7对应连接DB0--DB7



/*==========================12864液晶显示屏并口驱动程序=============================*/
void chk_busy()
{
   LCM_RS=0; LCM_RW=1; LCM_EN=1;
   Lcd_Bus=0xff;
   while((Lcd_Bus&0x80)==0x80);
   LCM_EN=0;
}
//================写指令到LCD=============================
void write_com(uchar cmdcode)
{
        chk_busy();
        LCM_RS=0; LCM_RW=0; LCM_EN=1;
        Lcd_Bus=cmdcode;
        LCM_EN=0;
}
//=================写数据到LCD==============================
void write_data(uchar Dispdata)
{       
        chk_busy();       //检查忙位
        LCM_RS=1; LCM_RW=0; LCM_EN=1;
        Lcd_Bus=Dispdata;
        LCM_EN=0;
}
void lcd_pos(uchar X,uchar Y)//设定显示位置
{                        
   ucharpos;
   if (X==1)
   {X=0x80;}
   else if (X==2)
   {X=0x90;}
   else if (X==3)
   {X=0x88;}
   else if (X==4)
   {X=0x98;}
   pos = X+Y ;
   write_com(pos);   //显示地址
}
//==========向LCM发送一个字符串,长度64字符之内============
void lcm_w_word(uchar *s)
{
        while(*s>0) { write_data(*s); s++; }//应用:lcm_w_word("您好!");
}
/**************在X(行)Y(列)显示字符串*******************
void LCD_Display_String( uchar x,uchar y,uchar *str )
{
    lcd_pos(x,y);         //先确定起始行和列
        while (*str!='\0')
      {
      write_data(*str);
          str++;
          }
}
void LCD_Display_Array( uchar x,uchar y,uchar *Array,uchar Lenth )
{
        lcd_pos(x,y);                           //先确定起始行和列
        while(Lenth--)
                {
                        write_data(*Array);
                        Array++;
                }
}*/
/******************************************************************************/
void lcm_w_test(bit i,unsigned char word)
{//写指令或数据(被调用层)
        if(i == 0){
                write_com(word);//写指令(0,指令)
        }else{
                write_data(word);//写数据(1,数据)
        }
}
//===========清屏函数=====================================
void lcm_clr(void)
{
        write_com(0x30);       
        write_com(0x01);
}

//=========================================================

void lcm_clr2(void)
{//清屏上面3行(用空格填满要清显示的地方,因为液晶屏是静态显示的,所以这办法常用)
        lcm_w_test(0,0x80);//第一行
        lcm_w_word("                ");
          //标尺("1234567812345678"应该能够显示满一行)
        lcm_w_test(0,0x90);//第二行
        lcm_w_word("                ");
          //标尺("1234567812345678"应该能够显示满一行)
        lcm_w_test(0,0x88);//第一行
        lcm_w_word("                ");
          //标尺("1234567812345678"应该能够显示满一行)
        lcm_w_test(0,0x98);//第一行
        lcm_w_word("                ");
}

//==================初始化LCD屏===============================
void lcm_init()
{
        write_com(0x30);//选择8bit数据流
        write_com(0x0c);//开显示(无游标、不反白)
        lcm_clr();      //清除显示,并且设定地址指针为00H
        write_com(0x06);//光标右移,DDRAM位址计数器(AC)加1,不整屏移动
        lcm_clr2();
}
/*-------------------使用绘图的方法让一个16*16的汉字符反白---------------------------
        形式参数:uchar x,uchar y,uchar wide,uchar bkcor
        行参说明:坐标水平位置,坐标垂直位置,反白行数,要反白还是清除(1:反白,0:清除)
-----------------------------------------------------------------------------------
void write1616GDRAM(uchar x,uchar y,uchar sign,uchar *bmp)       
{
        uchar i,j,basex;
        write_com(0x36);      //扩展指令,绘图模式命令,开显示也可以绘.(关图片显示0x34)
        if(y==1||y==2)          //第一第二行
        {
                basex=0x80;           //上半屏
                y=(y-1)*16;          //垂直位址从0X80开始.
        }
        if(y==3||y==4)        //第三第四行
        {
                basex=0x88;       //下半屏
                y=(y-3)*16;       //垂直位址都是从0X80开始的,不管上下半屏。
        }
        for(i=0;i<16;i++)        //
        {                                                                                                                       
                write_com(0x80+y+i);//写入垂直位址。
                write_com(basex+x-1); //再写入水平位址(上半屏第一字为0X80,……第七字为0X87)
                                                           //下半屏第一字为0X88,……第七字为0X8F;
                for(j=0;j<2;j++)   //再写入两个8位元的数据,AC会自动增一,接着写数据
                {
                        if(sign==1)
                                write_data(~(*bmp++));
                        else
                                write_data(*bmp++);
                }
        }       
        write_com(0x36);//写完数据,开图片显示   
}*/
/**********************************************************
//函数功能:使用绘图的方法让一行反白
//形式参数:uchar row,uchar bkcor
//行参说明:反白行数,要反白还是清除(1:反白,0:清除反白)
//返回参数:无
//使用说明:无
//**********************************************************/   

void setrowbkcolor(uchar row,uchar bkcor)       
{
        uchar i,j,basex,basey,color;
        if(bkcor==1)        color=0xff;          //全写入0XFF,反白。
        if(bkcor==0)        color=0x00;       //全写入0X00,消白。
        write_com(0x36);      //扩展指令,写图片时,关图片显示
        if(row==1||row==2)          //第一第二行
        {
                basex=0x80;           //上半屏
        }
        if(row==3||row==4)        //第三第四行
        {
                basex=0x88;       //下半屏
                row=row-2;       //垂直位址都是从0X80开始的,不管上下半屏。
        }
        basey=0x80+(row-1)*16;        //从哪一行的首行点阵开始
        for(i=0;i<16;i++)        //一行有16行点阵
        {                                                                                                                       
                write_com(basey+i);//写入垂直位址。
                write_com(basex); //水平位址(上半屏第一字为0X80)//下半屏第一字为0X88;
                                                             
                for(j=0;j<16;j++)   //再写入两个8位元的数据,AC会自动增一,接着写数据
                        write_data(color);
        }       
        write_com(0x36);//写完数据,开图片显示   
}
/*=====================================================================================
        函数功能:显示16X32图形,适用于st7920型液晶
        形式参数:uchar x,uchar y,uchar *bmp
        行参说明:横坐标X列,纵坐标Y行,要显示的图形BMP
=====================================================================================*/         
void write1632GDRAM(uchar x,uchar y,uchar *bmp)       
{
        uchar i,j,basex,basey;
        switch(y)       //由y纵坐标定是上半屏还是下半屏
        {
          case 1: basex=0x80; break;//上半屏
          case 2: basex=0x80; break;//先上半屏,下面再下半屏。
          case 3: basex=0x88; break;//下半屏
          default:   return;   //别的则返回
        }
        basey=basex+x-1;
        write_com(0x36);
        if(y==1||y==3)        //如为第一第三行,则直接是在同一半屏,直接绘完32行点陈数据。
        {
                  for(i=0;i<32;i++)       //写入32行点阵
                  {                                                                                                               
                          write_com(0x80+i);        //先写入垂直位址,选上下32行的哪一行,
                                                                        //不管上下半屏,首行都为0X80
                          write_com(basey);        //再写入水平位址(选上下半屏)
                          for(j=0;j<2;j++)         //2个8位元的数据,即16BIT宽度
                                  write_data(*bmp++);   
                  }       
        }
        if(y==2)//从第二行开始则画图将上下半屏都有,所以先画完上半屏16行,再画下半屏16行。
        {                                       
                  for(i=0;i<16;i++)       //写入上半屏16行点阵
                  {                                                                                                               
                          write_com(0x90+i);//先写入垂直位址,选上下32行的哪一行,不管上下半屏,
                                                                        //首行都为0X80,第二行为0X90。
                          write_com(basey);        //(选上半屏)再写入水平位址
                          for(j=0;j<2;j++)         //2个8位元的数据,即16BIT宽度
                                  write_data(*bmp++);   
                  }
                  for(i=0;i<16;i++)       //写入下半屏16行点阵
                  {                                                                                                               
                          write_com(0x80+i);//先写入垂直位址,选上下32行的哪一行,不管上下半屏,首行都为0X80
                          write_com(basey+8);   //(选下半屏)再写入水平位址
                          for(j=0;j<2;j++)         //2个8位元的数据,即16BIT宽度
                                  write_data(*bmp++);   
                  }       
        }
        write_com(0x36);//写完数据,开图片显示   
}
/*=====================================================================================      
        函数名称: init_12864_GDRAM()
        功能描述: 在程写GDRAM时序初始化12864
=====================================================================================*/
void init_12864_GDRAM()       
{
        write_com(0x30);   //基本指令操作(扩充指令操作为:0x34)
        write_com(0x0C);   //整体显示ON,游标OFF,游标位置OFF
        write_com(0x06);       //光标右移,DDRAM位址计数器(AC)加1,不整屏移动
        lcm_clr();         //清屏 (清DDRAM)
}

/*======================================================================================
        函数名称:Clean_12864_GDRAM(void)                                               
        函数功能:清屏函数
        使用说明:GDRAM填满0
=======================================================================================*/
void Clean_12864_GDRAM(void)
{
    uchar x,y;
    write_com(0x36);
    init_12864_GDRAM();                //设置扩展指令集,按手册说明,仅设置了绘图位,
    write_com(0x36);        //需要两次,本次设置扩展指令集。
    for (y=0;y<32;y++)
    {
      write_com(0x80+y);//设置y=1000+00xx,y+1则往下一行
      write_com(0x80);        //设置x=1000 0000
      for (x=0;x<16;x++)
      {
            write_data(0x00);   //高字节数据
            write_data(0x00);        //低字节数据
      }
    }
}
/*------------------显示图片------------------------*/

void Disp_Img(unsigned char code *img)
{unsigned int j=0;
   unsigned char x,y,i;
       for(i=0;i<9;i+=8)
       for(y=0;y<32;y++)//原来 为 y<26 ,上下两个半屏不能正常对接显示,导致显示的图片中间有空隙         
         for(x=0;x<8;x++)
         {write_com(0x36);//功能设置---8BIT控制界面,扩充指令集      
            write_com(y+0x80);      //行地址
            write_com(x+0x80+i);   //列地址
            write_com(0x30);
            write_data(img); //写数据还要回到基本指令集   
            write_data(img);
         }   
         
}

lsx007 发表于 2014-5-7 22:22:45

本帖最后由 lsx007 于 2014-5-7 22:27 编辑

看下反汇编代码,看汉字的编码对不对,如果不对那么就是keil的补丁没弄好,如果正确那么说明keil这边没有问题,再调试液晶。
我觉得还需注意一个地方,程序的中的汉字字符串很占系统的内存,编译代码后查看程序各个段的大小

win100 发表于 2014-5-7 22:34:07

注意延时
页: [1]
查看完整版本: 老生常谈一个令我不解的12864液晶显示汉字!!!