天天看点

LCD-中文显示-超级菜鸟版

      液晶是什么?字模是什么?GBK是什么?gram是什么?HZK16是什么?bin是什么?汉字内码是什么?UNICODE又是什么?..

      液晶真是小东西大学问,我是地道的门外汉,一块液晶,一个开发板,显示一个中文字,连个例也没有,好在有网络,从一篇文章里遇到一堆听都没听过的专业词汇,然后在从查找这些词汇的过程中遇到更多的词汇,真是张见识了。

      感觉接触一个新事物,难度不在它本身的技术有多复杂,再高深的技术都是靠千千万万个逻辑组建起来的,只要静下心来理,总是能理出点头绪的。倒是一点概念都没有的时候最惶惑。而这个阶段的超级菜鸟也是举步维艰的,很多资料帖子都看不懂(不是似懂非懂,是一点都不懂~~),而一般人又没时间给超级菜鸟把一个个基础的概念解释清楚,更郁闷的是,市面上,海一般的教程资料又不一定合用。最终求助无门,要么望而怯步,放弃,要么硬啃。好在只要是有逻辑的东西,都是能啃的,虽然不好消化,但是绝对比别人告诉自己来得深刻,而且能培养探索新事物的能力。(当然,有时时间紧迫,不容许慢慢消化,还是赶紧求助好心高人的好。)

      据说,液晶带中文字库能显示中文,那不带中文字库怎么显示啊?字库又长什么样呢?

static u8 GBK16[32];

unsigned char *Read_One_GBK16(unsigned char *ch)

{

   unsigned int  temp1;

   unsigned char temp2;

   unsigned char *p;

   p=&GBK16[0];

   temp1=*ch;

   temp2=*(ch+1); //有个快典网http://bm.kdd.cc/ 能查,查到“歌”字GBK码为:0XB8E8,正好两个字节。

// if(temp1<0x81||temp2<0x40)return 1;//不合法的汉字

   temp1-=0xa0;                                     //计算汉字区码 //不懂

   temp2-=0xa0;                                 //计算汉字位码

   temp1=((INT32U)(94*(temp1-1)+(temp2-1)))*32;     //计算汉字在字库中的偏移地址

 //  sector_offset = temp1/(512/32);//算出要读哪个扇区  933

 // byte_offset = (temp1%(512/32))*32;//算出要读扇区的哪个字节 //更不懂了

   f_open(&fii,"sys/HZK16.bin", FA_OPEN_EXISTING | FA_READ); //f_open打开了一个“HZK16.bin”的文件啥东东~

   f_lseek(&fii,temp1);

   f_read(&fii, GBK16, 32, &br);

   f_close(&fii); 

//   GBK_Buffer=buffer;

   return p;

}

“Read_One_GBK16”--“读一个GBK”同类的还有unsigned char *Read_One_GBK32(unsigned char *ch),

unsigned char *Read_One_GBK12(unsigned char *ch),

WCHAR *GBKTOUnicode(unsigned char *ch,啥是GBK啊?

百科说:“GBK: 汉字国标扩展码,基本上采用了原来GB2312-80所有的汉字及码位,并涵盖了原Unicode中所有的汉字20902,总共收录了883个符号, 21003个汉字及提供了1894个造字码位。 Microsoft简体版中文Windows 95就是以GBK为内码,又由于GBK同时也涵盖了Unicode所有CJK汉字,所以也可以和Unicode做一一对应。”

( 是不是可以理解为GBK是汉字在库中的一个编号?)

bin:百科说:“ bin (binary)其中文意思既是:二进制,二进制文件,其用途依系统或应用而定 。

  也就是说,一般来讲是机器代码,汇编语言编译后的结果,(DOS下汇编语言编译后与.com文件相类似),用debug、WINHEX,U_EDIT等软件打开(通常不一定能看得懂是些什么除非精通汇编语言)

  所有的文件, 无论后缀名是什么, 一律分为两种格式. text 和 binary.

  一种文件格式binary的缩写。一个后缀名为.bin的文件, 只是想表明它是binary格式.,但并不表明它与某种应用程序有必然的联系性”

(下了一个WINHEX,的确能打开^^,原来每个字对应的就是一堆数组,用网上下载的字模生成工具,生成也是一样的)

网友说:“以指定的方式建立字库 字库分3类

(1) 常用可见ASCII字 0-9, A-Z及a-z,各种符号*#?()等 数字字库0-9 ,这是XY长度为6*8点的

(2) 半角字符 0-9,A-Z 也是我们常用的数字和英文字符显示方式,占半个汉字大小,是8*16点。半角字符,就是指占汉字一一半大小

(3) 汉字字库常用HZK16(6763个汉字) 显然这是16*16的, 标准的全角字符

一般应用不建议采用第(1)项来显示数字与字母,一是字体太小,二是不便于和汉字混排,不好对齐。”

那这样就明了了,没有字库的话只要用工具(比如PCtoLCD2002.exe)把具体的字的“字模”(就是点点排成的数组),给函数,就不用上面的函数读库里的GBK了。(工具要选好,不然摸半天都摸不出来)

 },/*"歌"字,16*16*/

u8 song[]= {0x01,0x00,0x5D,0x78,0x55,0x48,0x55,0x48,0x5D,0x7A,0x41,0x01,0x7F,0xFE,0x45,0x02,

0x09,0x04,0x30,0x18,0xD7,0xE0,0x10,0x10,0x14,0x0C,0x18,0x07,0x10,0x02,0x00,0x00}

void Lcd_WriteChinese(u8 x,u8 y,u16 x_offset,u16 y_offset,u16 CharColor,u16 CharBackColor,u8 *ChineseCode,u8 mode)

{

  u8 ByteCounter,BitCounter;   

  u8 *ChinesePointer;

  Lcd_SetBox(x*16,y*16,16,16,x_offset,y_offset);        

  Lcd_WR_Start();

  ChinesePointer=Read_One_GBK16(ChineseCode); //这里用改为:ChinesePointer=&song[0];就可以用了。

  for(ByteCounter=0; ByteCounter<32; ByteCounter++)

//这里连续写入了32个字节,那扫描应该是横扫了,而不是下面说的先上半个后下半个,生成字模的时候要注意。

  {

    for(BitCounter=0;BitCounter<8;BitCounter++)

    {

      if((*ChinesePointer & (0x80 >> BitCounter)) == 0x00)

      {

       Set_Rs;

  if(!mode)

  {

      GPIOx->ODR = CharBackColor;//DataToWrite(CharBackColor);

        Clr_nWr;

        Set_nWr;

  }

      }

      else

       GPIOx->ODR =CharColor;  //DataToWrite(CharColor);

      }    

    }

    ChinesePointer++;

  }

  Set_Cs; 

}

还有个问题,就是数组的存储方式,寄存器存储方式,液晶的扫描方式,怎一个乱字了得~~

以下来自http://www.ourdev.cn的huasoft网友的帖子:

取出汉字字库向LCD写屏的方式

液晶NOKIA5110的X,Y概念及写屏方式:

液晶5110由84点*48点组成。 可以看到,最多显示的半角字符是10*6个, 最多显示的汉字是5*3个

液晶5110的规格书上是这样描述它的写入坐标概念的,首先,每次写入命令是写一个竖着的8个bit即一个字节,这是它的一个最基本的写入元单元。(写入时先写高位,这一点对掌握整体概念不重要,先不讨论)。以这样的元单元为计数,屏幕整个被分成了84*6 个这样的元单元。

NOKIA5110的LCD的XY坐标概念

写入一个汉字“一”,字模如下

/*--  文字:  一  --*/

/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/

0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xC0,0x80,0x00,

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

写入过程的函数一般是这样的:

LCD_set_XY(row*8, page);// 列,页 

for(i=0; i<16;i++) 

LCD_write_byte(pgm_read_byte(hanzi+c*32+i),1); 

    LCD_set_XY(row*8, page+1);// 列,页 

for(i=16; i<32;i++) 

LCD_write_byte(pgm_read_byte(hanzi+c*32+i),1);

写入示意图如下:

NOKIA5110LCD写入汉字一的过程

 好了,现在问题来了,HZK16和 我们在5110LCD上用的字库是不同的组织方式,一个先行后列, 一个先上半部后下半部。如何转换?

 (我就显示一个字,直接用工具改字模更方便~这里学习下前辈改扫描方式的程序)

-----------------------------------------------------------

为了让NOKIA5110直接用hzk16字库形式而不需要转换, 我们直接把hzk16转成适合Nokia5110的扫描方式,

关键代码段

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

//2.将标准hzk16.bin的16*16的先行后列字模转换成NOKIA5110屏用的先刷汉字上半部分16列,再刷下半部分16列的方式(见文档)

//add hzk16fornokia5110.bin write routine here

FILE *HZK1 = 0, *HZK2 = 0;

unsigned long offset1 = 0, offset2 =0;

if((HZK1=fopen("hzk16.bin", "rb")) == NULL)  

{  

printf("Can't Open hzk16.bin/n");  

getchar(); 

return 0; 

}  

if((HZK2=fopen("hzk16fornokia5110.bin", "wb")) == NULL)  

printf("Can't Open hzk16fornokia5110.bin/n");  

offset1=0;

unsigned char mat1[2][16],mat2[2][16];

//int i,j,k,m;

while( !feof(HZK1) )

fseek(HZK1, offset1, SEEK_SET);

fread(mat1, 32, 1, HZK1); 

//mat1-->mat2

  for(i=0;i<2;i++)

  {

for(j=0;j<8;j++)

  for(k=0;k<8;k++) //bit

//mat2[0][j].bit[k]= mat1[0][j*2].bit[7-k];

if( bit_isset(mat1[i][k*2],(7-j))==1 )

bit_set(mat2[i][j],k);

else

bit_clr(mat2[i][j],k);

  }

//mat2[0][j+8].bit[k]= mat1[0][j*2+1].bit[7-k];

if( bit_isset(mat1[i][k*2+1],(7-j))==1)

bit_set(mat2[i][j+8],k);

bit_clr(mat2[i][j+8],k);

  }//for(i=0;i<2;i++)

//write mat2

printf("write offset: %ld/n",offset1);

fseek(HZK2, offset1, SEEK_SET);

fwrite(mat2,32,1,HZK2);

offset1+=32;

}//while

printf("I find the feof()!=0!/n");

fclose(HZK1); 

fclose(HZK2);

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

 在函数里Lcd_WriteChinese用到Lcd_SetBox(x*16,y*16,16,16,x_offset,y_offset) ,内容如下,        

 void Lcd_SetBox(u8 xStart,u16 yStart,u8 xLong,u16 yLong,u16 x_offset,u16 y_offset)

#if ID_AM==000   

 Lcd_SetCursor(xStart+xLong-1+x_offset,yStart+yLong-1+y_offset);

#elif ID_AM==001

#elif ID_AM==010

 Lcd_SetCursor(xStart+x_offset,yStart+yLong-1+y_offset);

#elif ID_AM==011

#elif ID_AM==100

 Lcd_SetCursor(xStart+xLong-1+x_offset,yStart+y_offset);    

#elif ID_AM==101

#elif ID_AM==110

 Lcd_SetCursor(xStart+x_offset,yStart+y_offset);

#elif ID_AM==111

 Lcd_SetCursor(xStart+x_offset,yStart+y_offset); 

#endif

 LCD_WR_REG(0x0050,xStart+x_offset);//水平 GRAM起始位置

 LCD_WR_REG(0x0051,xStart+xLong-1+x_offset);//水平GRAM终止位置

 LCD_WR_REG(0x0052,yStart+y_offset);//垂直GRAM起始位置

 LCD_WR_REG(0x0053,yStart+yLong-1+y_offset);//垂直GRAM终止位置

void Lcd_SetCursor(u8 x,u16 y)

 LCD_WR_REG(0x20,x);

 LCD_WR_REG(0x21,y);   

函数名:Lcd块选函数

功能:选定Lcd上指定的矩形区域

应该是控制显示区域和定位的吧,现在汉字已经可以显示了,但是,是从右到左,而且x轴y轴混了,改了一下也没成功,咋整啊~~

好像位图显示也是镜像的,什么原因呢?~~

可以把字库和位图放在sd卡里,液晶从里面读不?跟gram有什么关系啊?(gram是什么~)

ILI9325是什么?跟用软件从屏中读出来的DeviceCode有什么关系呢?

液晶定时变暗,然后关闭,看屏的时候再用按键唤醒,跟手机屏幕一样,能么?

好像触摸屏这块儿也挺庞大的~

问题多多啊,菜鸟的道路很艰辛,但也很充实,菜并快乐着~

继续阅读