第80節(jié):調(diào)用液晶屏內(nèi)部字庫來顯示漢字或字符的坐標(biāo)體系和本質(zhì)
從業(yè)近十年!手把手教你單片機(jī)程序框架 第80講
開場白:
前面章節(jié)講的內(nèi)容全部都是用自構(gòu)字庫的,相當(dāng)于使用液晶屏的圖像模式。其實這個款12864液晶屏的驅(qū)動芯片是st7920,它內(nèi)部是自帶16x16字庫的,可以顯示16x16的漢字或者8x16的字符。這一節(jié)開始就跟大家講講這方面的內(nèi)容。要教會大家四個知識點(diǎn):
第一個:內(nèi)部字庫的真實坐標(biāo)體系的本質(zhì)。當(dāng)我們用內(nèi)部字庫的時候,它的坐標(biāo)體系跟前面講的自造字庫坐標(biāo)不一樣,不再是256x32的液晶屏。它還原成為128x64的液晶屏,橫坐標(biāo)x軸坐標(biāo)沒辦法精確到每個點(diǎn),只能以16個點(diǎn)(2個字節(jié))為一個單位,因此128個點(diǎn)的x軸坐標(biāo)范圍是0至8。而y軸的坐標(biāo)也是以16個點(diǎn)(2個字節(jié))為一個單位,因此64個點(diǎn)的x軸坐標(biāo)范圍是0至3。把12864液晶屏分成4行8列,每個數(shù)代表一個坐標(biāo)點(diǎn)。
第二個:在使用內(nèi)部字庫時,C51編譯器暗地里干了啥?如果使用液晶屏內(nèi)部自帶字庫,編程的時候只要在源代碼里直接寫入所需要的漢字或者字符,就可以自動調(diào)用相對應(yīng)的字庫了。但是細(xì)心的網(wǎng)友一定會問,為什么在源代碼上直接寫入某個漢字就可以調(diào)用到這個漢字的字庫?其實,表面上我們寫下具體的某個漢字或者字符,但是C51編譯器會自動對數(shù)組內(nèi)的漢字翻譯成 機(jī)內(nèi)碼(2字節(jié)),會自動對數(shù)組內(nèi)的字符翻譯成 ASCII碼(1字節(jié))。
第三個:12864的控制芯片st7920內(nèi)部有兩套驅(qū)動顯示指令方式,一種是前面章節(jié)講的自構(gòu)字庫模式,也是圖像模式。另外一種就是本節(jié)講的用內(nèi)部字庫模式。在切換模式的時候,發(fā)送命令字0x0c表示用內(nèi)部字庫模式,發(fā)送命令字0x36表示用自構(gòu)字庫模式。
第四個:12864整屏有4行8列,一共32個坐標(biāo)點(diǎn),每個坐標(biāo)點(diǎn)可以顯示一個16x16的漢字,但是在顯示8x16字符時候,必須一次顯示2個字符籌夠16x16的點(diǎn)陣。例如,只想達(dá)到顯示一個字符的時候,應(yīng)該在另外一個空位置上顯示空字符來填充。
具體內(nèi)容,請看源代碼講解。
(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。
(2)實現(xiàn)功能:
開機(jī)上電后,液晶屏第一行調(diào)用直接漢字書寫方式的數(shù)組來顯示(饅頭V5)的內(nèi)容。第四行調(diào)用機(jī)內(nèi)碼和ASCII碼的數(shù)組來顯示(饅頭V5)的內(nèi)容。
(3)源代碼講解如下:
#include "REG52.H"
sbit LCDCS_dr = P1^6; //片選線
sbit LCDSID_dr = P1^7; //串行數(shù)據(jù)線
sbit LCDCLK_dr = P3^2; //串行時鐘線
sbit LCDRST_dr = P3^4; //復(fù)位線
void SendByteToLcd(unsigned char ucData); //發(fā)送一個字節(jié)數(shù)據(jù)到液晶模塊
void SPIWrite(unsigned char ucWData, unsigned char ucWRS); //模擬SPI發(fā)送一個字節(jié)的命令或者數(shù)據(jù)給液晶模塊的底層驅(qū)動
void WriteCommand(unsigned char ucCommand); //發(fā)送一個字節(jié)的命令給液晶模塊
void LCDWriteData(unsigned char ucData); //發(fā)送一個字節(jié)的數(shù)據(jù)給液晶模塊
void LCDInit(void); //初始化 函數(shù)內(nèi)部包括液晶模塊的復(fù)位
void display_clear(void); // 清屏。4行8列的坐標(biāo)點(diǎn)全部顯示2個空字符相當(dāng)于清屏了。
void display_hz1616(unsigned int x,unsigned int y,const unsigned char *ucArray);
void display_double_zf816(unsigned int x,unsigned int y,const unsigned char *ucArray1,const unsigned char *ucArray2);
void delay_short(unsigned int uiDelayshort); //延時
/* 注釋一:內(nèi)部字庫的真實坐標(biāo)體系的本質(zhì)。
* 當(dāng)我們用內(nèi)部字庫的時候,它的坐標(biāo)體系跟前面講的自造字庫坐標(biāo)不一樣,不再是256x32的液晶屏。
* 它還原成為128x64的液晶屏,橫坐標(biāo)x軸坐標(biāo)沒辦法精確到每個點(diǎn),只能以16個點(diǎn)(2個字節(jié))為一個單位,
* 因此128個點(diǎn)的x軸坐標(biāo)范圍是0至8。而y軸的坐標(biāo)也是以16個點(diǎn)(2個字節(jié))為一個單位,因此64個點(diǎn)的x軸
* 坐標(biāo)范圍是0至3。以下是坐標(biāo)地址的位置編碼。把12864液晶屏分成4行8列,每個數(shù)代表一個坐標(biāo)點(diǎn),
* 用深究具體含義,液晶驅(qū)動芯片ST7920的手冊上有提到。
*/
code unsigned char ucAddrTable[]= //調(diào)用內(nèi)部字庫時,液晶屏的坐標(biāo)體系,位置編碼,是驅(qū)動內(nèi)容,讀者可以不用深究它的含義。
{
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
};
/* 注釋二:在使用內(nèi)部字庫時,C51編譯器暗地里干了啥?
* 如果使用液晶屏內(nèi)部自帶字庫,以下編程的時候只要在源代碼里直接寫入所需要的漢字或者字符,
* 就可以自動調(diào)用相對應(yīng)的字庫了。但是細(xì)心的網(wǎng)友一定會問,為什么在源代碼上直接寫入某個漢字
* 就可以調(diào)用到這個漢字的字庫?其實,表面上我們寫下具體的某個漢字或者字符,但是C51編譯器
* 會自動對數(shù)組內(nèi)的漢字翻譯成 機(jī)內(nèi)碼(2字節(jié)),會自動對數(shù)組內(nèi)的字符翻譯成 ASCII碼(1字節(jié))。
* 本節(jié)程序會做這個實驗來驗證它。以下兩種書寫方式不一樣,但本質(zhì)是一樣的。
*/
code unsigned char Hz1616_man[]="饅"; //對于數(shù)組內(nèi)的漢字,編譯會自動翻譯成 機(jī)內(nèi)碼(2字節(jié))
code unsigned char JN1616_man[]= //機(jī)內(nèi)碼 饅 網(wǎng)上有很多把漢字或者字符轉(zhuǎn)換成相關(guān)編碼的工具軟件
{
0xC2,
0xF8,
};
code unsigned char Hz1616_tou[]="頭"; //對于數(shù)組內(nèi)的漢字,編譯會自動翻譯成 機(jī)內(nèi)碼(2字節(jié))
code unsigned char JN1616_tou[]= //機(jī)內(nèi)碼 頭 網(wǎng)上有很多把漢字或者字符轉(zhuǎn)換成相關(guān)編碼的工具軟件
{
0xCD,
0xB7,
};
code unsigned char Zf816_V[]="V"; //對于數(shù)組內(nèi)的字符,編譯會自動翻譯成 ASCII碼(1字節(jié))
code unsigned char ASCII816_V[]= //ASCII碼 V 網(wǎng)上有很多把漢字或者字符轉(zhuǎn)換成相關(guān)編碼的工具軟件
{
0x56,
};
code unsigned char Zf816_5[]="5"; //對于數(shù)組內(nèi)的字符,編譯會自動翻譯成 ASCII碼(1字節(jié))
code unsigned char ASCII816_5[]= //ASCII碼 5 網(wǎng)上有很多把漢字或者字符轉(zhuǎn)換成相關(guān)編碼的工具軟件
{
0x35,
};
code unsigned char Zf816_nc[]=" "; //對于數(shù)組內(nèi)的字符,編譯會自動翻譯成 ASCII碼(1字節(jié))
code unsigned char ASCII816_nc[]= //ASCII碼 空字符 網(wǎng)上有很多把漢字或者字符轉(zhuǎn)換成相關(guān)編碼的工具軟件
{
0x20,
};
void main()
{
LCDInit(); //初始化12864 內(nèi)部包含液晶模塊的復(fù)位
/* 注釋三:
* 12864的控制芯片st7920內(nèi)部有兩套驅(qū)動顯示指令方式,一種是前面章節(jié)講的自構(gòu)字庫模式,也是圖像模式。
* 另外一種就是本節(jié)講的用內(nèi)部字庫模式。以下是切換模式的命令,命令字0x0c表示用內(nèi)部字庫模式。
* 命令字0x36表示用自構(gòu)字庫模式。
*/
WriteCommand(0x0C); //命令字0x0c表示用內(nèi)部字庫模式。命令字0x36表示用自構(gòu)字庫模式。
display_clear(); // 清屏。4行8列的坐標(biāo)點(diǎn)全部顯示2個空字符相當(dāng)于清屏了。
display_hz1616(0,0,Hz1616_man); //第一行,調(diào)用直接漢字書寫方式的數(shù)組來顯示(饅頭V5),
display_hz1616(1,0,Hz1616_tou);
display_double_zf816(2,0,Zf816_V,Zf816_5);
display_hz1616(0,3,JN1616_man); //第四行,調(diào)用機(jī)內(nèi)碼和ASCII碼的數(shù)組來顯示(饅頭V5),
display_hz1616(1,3,JN1616_tou);
display_double_zf816(2,3,ASCII816_V,Zf816_5);
while(1)
{
;
}
}
/* 注釋四:在一個坐標(biāo)點(diǎn)顯示1個內(nèi)部字庫漢字的函數(shù)
* 第1,2個參數(shù)x,y是坐標(biāo)體系。x的范圍是0至8,y的范圍是0至3.
* 第3個參數(shù)*ucArray是漢字機(jī)內(nèi)碼,是有2個字節(jié)的數(shù)組。
*/
void display_hz1616(unsigned int x,unsigned int y,const unsigned char *ucArray)
{
WriteCommand(0x30); //基本指令集
WriteCommand(ucAddrTable[8*y+x]); //起始位置
LCDWriteData(ucArray[0]);
LCDWriteData(ucArray[1]);
}
/* 注釋五:在一個坐標(biāo)點(diǎn)顯示2個內(nèi)部字庫字符的函數(shù)
* 注意,由于一個坐標(biāo)點(diǎn)是16x16點(diǎn)陣,而一個字符是8x16點(diǎn)陣的,所以務(wù)必要顯示2個字符籌夠1個坐標(biāo)點(diǎn)。
* 第1,2個參數(shù)x,y是坐標(biāo)體系。x的范圍是0至8,y的范圍是0至3.
* 第3個參數(shù)*ucArray1是左邊第1個字符ASCII碼,是有1個字節(jié)的數(shù)組。
* 第4個參數(shù)*ucArray2是右邊第2個字符ASCII碼,是有1個字節(jié)的數(shù)組。
*/
void display_double_zf816(unsigned int x,unsigned int y,const unsigned char *ucArray1,const unsigned char *ucArray2)
{
WriteCommand(0x30); //基本指令集
WriteCommand(ucAddrTable[8*y+x]); //起始位置
LCDWriteData(ucArray1[0]);
LCDWriteData(ucArray2[0]);
}
void display_clear(void) // 清屏。4行8列的坐標(biāo)點(diǎn)全部顯示2個空字符相當(dāng)于清屏了。
{
unsigned int i,j;
for(i=0;i<4;i++)
{
for(j=0;j<8;j++)
{
display_double_zf816(j,i,Zf816_nc,ASCII816_nc); //Zf816_nc與ASCII816_nc本質(zhì)是一樣的,只是書寫方式不一樣。
}
}
}
void SendByteToLcd(unsigned char ucData) //發(fā)送一個字節(jié)數(shù)據(jù)到液晶模塊
{
unsigned char i;
for ( i = 0; i < 8; i++ )
{
if ( (ucData << i) & 0x80 )
{
LCDSID_dr = 1;
}
else
{
LCDSID_dr = 0;
}
LCDCLK_dr = 0;
LCDCLK_dr = 1;
}
}
void SPIWrite(unsigned char ucWData, unsigned char ucWRS) //模擬SPI發(fā)送一個字節(jié)的命令或者數(shù)據(jù)給液晶模塊的底層驅(qū)動
{
SendByteToLcd( 0xf8 + (ucWRS << 1) );
SendByteToLcd( ucWData & 0xf0 );
SendByteToLcd( (ucWData << 4) & 0xf0);
}
void WriteCommand(unsigned char ucCommand) //發(fā)送一個字節(jié)的命令給液晶模塊
{
LCDCS_dr = 0;
LCDCS_dr = 1;
SPIWrite(ucCommand, 0);
delay_short(90);
}
void LCDWriteData(unsigned char ucData) //發(fā)送一個字節(jié)的數(shù)據(jù)給液晶模塊
{
LCDCS_dr = 0;
LCDCS_dr = 1;
SPIWrite(ucData, 1);
}
void LCDInit(void) //初始化 函數(shù)內(nèi)部包括液晶模塊的復(fù)位
{
LCDRST_dr = 1; //復(fù)位
LCDRST_dr = 0;
LCDRST_dr = 1;
}
void delay_short(unsigned int uiDelayShort) //延時函數(shù)
{
unsigned int i;
for(i=0;i
{
;
}
}
總結(jié)陳詞:
通過本節(jié)的實驗,我們發(fā)現(xiàn)漢字的識別本質(zhì)是機(jī)內(nèi)碼,字符的識別本質(zhì)是ASCII碼。不管是機(jī)內(nèi)碼還是ASCII碼,這些都是16進(jìn)制的數(shù)字,也就是我們手機(jī)平時接收和發(fā)送的信息本質(zhì)都是這些數(shù)字編碼,但是機(jī)內(nèi)碼是2個字節(jié),ASCII碼是1個字節(jié),如果在一串隨機(jī)的信息中,同時包含漢字和字符兩種數(shù)字信息,我們的程序又該如何能篩選和識別它們,會不會把機(jī)內(nèi)碼和ASCII碼搞混亂了?不會的。其實這兩種編碼都是有規(guī)律可以篩選識別的,欲知詳情,請聽下回分解-----液晶屏顯示串口發(fā)送過來的任意漢字和字符。