1. 程式人生 > >LCD-中文顯示-超級菜鳥版

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
      {
       Set_Rs;
       GPIOx->ODR =CharColor;  //DataToWrite(CharColor);
        Clr_nWr;
        Set_nWr;
      }    
    }
    ChinesePointer++;
  }
  Set_Cs; 
}

還有個問題,就是陣列的儲存方式,暫存器儲存方式,液晶的掃描方式,怎一個亂字了得~~

取出漢字字型檔向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");  
getchar(); 
return 0; 
}  

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);
}
  }
}


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

//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);
}
else
{
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
 Lcd_SetCursor(xStart+xLong-1+x_offset,yStart+yLong-1+y_offset);
    
#elif ID_AM==010
 Lcd_SetCursor(xStart+x_offset,yStart+yLong-1+y_offset);
    
#elif ID_AM==011
 Lcd_SetCursor(xStart+x_offset,yStart+yLong-1+y_offset);
    
#elif ID_AM==100
 Lcd_SetCursor(xStart+xLong-1+x_offset,yStart+y_offset);    
    
#elif ID_AM==101
 Lcd_SetCursor(xStart+xLong-1+x_offset,yStart+y_offset);    
    
#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有什麼關係呢?

液晶定時變暗,然後關閉,看屏的時候再用按鍵喚醒,跟手機螢幕一樣,能麼?

好像觸控式螢幕這塊兒也挺龐大的~

問題多多啊,菜鳥的道路很艱辛,但也很充實,菜並快樂著~