備注:這篇文章是對(duì)12864操作的具體介紹,僅限剛接觸12864的新手,大神請(qǐng)拍磚,文章寫(xiě)的較散,建議先參考12864手冊(cè)及控制驅(qū)動(dòng)器ST7920英文手冊(cè)有個(gè)初步理解之后再閱讀該篇文章,將會(huì)有更深的認(rèn)識(shí)。強(qiáng)烈建議閱讀ST7920英文手冊(cè),細(xì)節(jié)內(nèi)容里面有詳細(xì)介紹,中文的12864也是從中譯過(guò)來(lái)的。
本文分三個(gè)步驟介紹12864的內(nèi)部資源原理,指令集詳細(xì)講解,以及應(yīng)用例子。
對(duì)12864的所有操作概括起來(lái)有4種: 1)、讀忙狀態(tài)(同時(shí)讀出指針地址內(nèi)容),初始化之后每次對(duì)12864的讀寫(xiě)均要進(jìn)行忙檢測(cè)。 2)、寫(xiě)命令:所有的命令可以查看指令表,后續(xù)講解指令的詳細(xì)用法。寫(xiě)地址也是寫(xiě)指令。 3)、寫(xiě)數(shù)據(jù):操作對(duì)象有DDRAM、CGRAM、GDRAM。 4)、讀數(shù)據(jù):操作對(duì)象也是DDRAM、CGRAM、GDRAM。
對(duì)12864的學(xué)習(xí)首相要了解其內(nèi)部資源,知道了它里面有哪些東西,你就可以更加方便的使用它。
先介紹幾個(gè)英文的名字: DDRAM:(Data Display Ram),數(shù)據(jù)顯示RAM,往里面寫(xiě)啥,屏幕就會(huì)顯示啥。 CGROM:(Character Generation ROM),字符發(fā)生ROM。里面存儲(chǔ)了中文漢字的字模,也稱作中文字庫(kù),編碼方式有GB2312(中文簡(jiǎn)體)和BIG5(中文繁體)。筆者使用的是育松電子的QC12864B,講解以此為例。 CGRAM:(Character Generation RAM),字符發(fā)生RAM,,12864內(nèi)部提供了64×2B的CGRAM,可用于用戶自定義4個(gè)16×16字符,每個(gè)字符占用32個(gè)字節(jié)。 GDRAM:(Graphic Display RAM):圖形顯示RAM,這一塊區(qū)域用于繪圖,往里面寫(xiě)啥,屏幕就會(huì)顯示啥,它與DDRAM的區(qū)別在于,往DDRAM中寫(xiě)的數(shù)據(jù)是字符的編碼,字符的顯示先是在CGROM中找到字模,然后映射到屏幕上,而往GDRAM中寫(xiě)的數(shù)據(jù)時(shí)圖形的點(diǎn)陣信息,每個(gè)點(diǎn)用1bit來(lái)保存其顯示與否。 HCGROM:(Half height Character Generation ROM):半寬字符發(fā)生器,就是字母與數(shù)字,也就是ASCII碼。 至于ICON RAM(IRAM):貌似市場(chǎng)上的12864沒(méi)有該項(xiàng)功能,筆者也沒(méi)有找到它的應(yīng)用資料,所以不作介紹。
下面就圍繞著上面列舉的這列資源展開(kāi)對(duì)12864的講解: DDRAM: 筆者使用的這塊12864內(nèi)部有4行×32字節(jié)的DDRAM空間。但是某一時(shí)刻,屏幕只能顯示2行×32字節(jié)的空間,那么剩余的這些空間呢?它們可以用于緩存,在實(shí)現(xiàn)卷屏顯示時(shí)這些空間就派上用場(chǎng)了。 DDRAM結(jié)構(gòu)如下所示: 80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、8EH、8FH 90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、9EH、9FH A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、ADH、AEH、AFH B0H、B1H、B2H、B3H、B4H、B5H、B6H、B7H、B8H、B9H、BAH、BBH、BCH、BDH、BEH、BFH
地址與屏幕顯示對(duì)應(yīng)關(guān)系如下: 第一行:80H、81H、82H、83H、84H、85H、86H、87H 第二行:90H、91H、92H、93H、94H、95H、96H、97H 第三行:88H、89H、8AH、8BH、8CH、8DH、8EH、8FH 第四行:98H、99H、9AH、9BH、9CH、9DH、9EH、9FH
說(shuō)明:紅色部分的數(shù)據(jù)歸上半屏顯示,綠色部分的數(shù)據(jù)歸下半屏顯示。一般我們用于顯示字符使用的是上面兩行的空間,也就是80H~8FH,90H~9FH,每個(gè)地址的空間是2個(gè)字節(jié),也就是1個(gè)字,所以可以用于存儲(chǔ)字符編碼的空間總共是128字節(jié)。因?yàn)槊總(gè)漢字的編碼是2個(gè)字節(jié),所以每個(gè)地址需要使用2個(gè)字節(jié)來(lái)存儲(chǔ)一個(gè)漢字。當(dāng)然如果將2個(gè)字節(jié)拆開(kāi)來(lái)使用也可以,那就是顯示2個(gè)半寬字符。 DDRAM內(nèi)部存儲(chǔ)的數(shù)據(jù)是字符的編碼,可以寫(xiě)入的編碼有ASCII碼、GB2312碼、BIG5碼。筆者使用的12864字庫(kù)貌似不太全,字符“數(shù)”都無(wú)法顯示,而是顯示其他字符。如果顯示長(zhǎng)篇漢字文章就不太適合吧。 DDRAM數(shù)據(jù)讀寫(xiě): 所有的數(shù)據(jù)讀寫(xiě)都是先送地址,然后進(jìn)行讀寫(xiě)。對(duì)DDRAM寫(xiě)數(shù)據(jù)時(shí),確保在基本指令集下(使用指令0x30開(kāi)啟),然后寫(xiě)入地址,之后連續(xù)寫(xiě)入兩個(gè)字節(jié)的數(shù)據(jù)。讀數(shù)據(jù)時(shí),在基本指令集下先寫(xiě)地址,然后假讀一次,之后再連續(xù)讀2個(gè)字節(jié)的數(shù)據(jù),讀完之后地址指針自動(dòng)加一,跳到下一個(gè)字,若需要讀下一個(gè)字的內(nèi)容,只需再執(zhí)行連續(xù)讀2個(gè)字節(jié)的數(shù)據(jù)。這里的假讀需要注意,不光是讀CGRAM需要假讀,讀其他的GDRAM、DDRAM都需要先假讀一次,之后的讀才是真讀,假讀就是讀一次數(shù)據(jù),但不存儲(chǔ)該數(shù)據(jù),也就是說(shuō)送地址之后第一次讀的數(shù)據(jù)時(shí)錯(cuò)誤的,之后的數(shù)據(jù)才是正確的。(dummy為假讀) 關(guān)于編碼在DDRAM中的存儲(chǔ)需要說(shuō)明事項(xiàng)如下: 1)、每次對(duì)DDRAM的操作單位是一個(gè)字,也就是2個(gè)字節(jié),當(dāng)往DDRAM寫(xiě)入數(shù)據(jù)時(shí),首先寫(xiě)地址,然后連續(xù)送入2個(gè)字節(jié)的數(shù)據(jù),先送高字節(jié)數(shù)據(jù),再送低字節(jié)數(shù)據(jù)。讀數(shù)據(jù)時(shí)也是如此,先寫(xiě)地址,然后讀出高字節(jié)數(shù)據(jù),再讀出低字節(jié)數(shù)據(jù)(讀數(shù)據(jù)時(shí)注意先假讀一次)。 2)、顯示ASCII碼半寬字符時(shí),往每個(gè)地址送入2個(gè)字節(jié)的ASCII編碼,對(duì)應(yīng)屏幕上的位置就會(huì)顯示2個(gè)半寬字符,左邊的為高字節(jié)字符,右邊的為低字節(jié)字符。 3)、顯示漢字時(shí),漢字編碼的2個(gè)字節(jié)必須存儲(chǔ)在同一地址空間中,不能分開(kāi)放在2個(gè)地址存放,否則顯示的就不是你想要的字符。每個(gè)字中的2個(gè)字節(jié)自動(dòng)結(jié)合查找字模并顯示字符。所以,如果我們往一個(gè)地址中寫(xiě)入的是一個(gè)漢字的2字節(jié)編碼就會(huì)正確顯示該字符,編碼高字節(jié)存放在前一地址低字節(jié),編碼低字節(jié)存放在后一地址高字節(jié),顯然他們就不會(huì)結(jié)合查找字模,而是與各地址相應(yīng)字節(jié)結(jié)合查找字模。 4)、因?yàn)榭刂破鱏T7920提供了4個(gè)自定義字符,所以這4個(gè)自定義字符也是可以顯示出來(lái)的,同樣這4個(gè)自定義字符也是采用編碼的方式,但是這4個(gè)字符的編碼是固定的,分別是0000H,0002H,0004H,0006H。如下圖所示: 
上圖只是把2個(gè)字符的CGRAM空間畫(huà)出來(lái),后續(xù)還有2個(gè)字符?梢钥吹矫總(gè)字符都有16行16列,每一行使用2個(gè)字節(jié),因此一個(gè)字符占用的空間是32字節(jié),地址是6位的,4個(gè)字符的地址分別是:00H~0FH、10H~1FH、20H~2FH、30H~3FH。編碼使用2個(gè)字節(jié),可以看到有2個(gè)位是任意的,說(shuō)明其實(shí)這4個(gè)字符的編碼可以有多個(gè),只是我們常用前面列舉的4個(gè)編碼。
CGRAM: (數(shù)據(jù)讀寫(xiě)) CGRAM的結(jié)構(gòu)就是上面所示了,這里再補(bǔ)充一些讀寫(xiě)CGRAM的內(nèi)容,讀寫(xiě)之前先寫(xiě)地址,寫(xiě)CGRAM的指令為0x40+地址。但是我們寫(xiě)地址時(shí)只需要寫(xiě)第一行的地址,例如第一個(gè)字符就是0x40+00H,然后連續(xù)寫(xiě)入2個(gè)字節(jié)的數(shù)據(jù),之后地址指針會(huì)自動(dòng)加一,跳到下一行的地址,然后再寫(xiě)入2個(gè)字節(jié)的數(shù)據(jù)。其實(shí)編程實(shí)現(xiàn)就是寫(xiě)入地址,然后連續(xù)寫(xiě)入32個(gè)字節(jié)的數(shù)據(jù)。讀數(shù)據(jù)也是先寫(xiě)首地址,然后假讀一次,接著連續(xù)讀32個(gè)字節(jié)的數(shù)據(jù)。
GDRAM:(繪圖顯示RAM) 繪圖RAM的空間結(jié)構(gòu)如下圖所示: 這些都是點(diǎn)陣,繪圖RAM就是給這些點(diǎn)陣置1或置0,可以看到其實(shí)它本來(lái)是32行×256列的,但是分成了上下兩屏顯示,每個(gè)點(diǎn)對(duì)應(yīng)了屏幕上的一個(gè)點(diǎn)。要使用繪圖功能需要開(kāi)啟擴(kuò)展指令。然后寫(xiě)地址,再讀寫(xiě)數(shù)據(jù)。 GDRAM的讀寫(xiě): 首先說(shuō)明對(duì)GDRAM的操作基本單位是一個(gè)字,也就是2個(gè)字節(jié),就是說(shuō)讀寫(xiě)GDRAM時(shí)一次最少寫(xiě)2個(gè)字節(jié),一次最少讀2個(gè)字節(jié)。 寫(xiě)數(shù)據(jù):先開(kāi)啟擴(kuò)展指令集(0x36),然后送地址,這里的地址與DDRAM中的略有不同,DDRAM中的地址只有一個(gè),那就是字地址。而GDRAM中的地址有2個(gè),分別是字地址(列地址/水平地址X)和位地址(行地址/垂直地址Y),上圖中的垂直地址就是00H~31H,水平地址就是00H~15H,寫(xiě)地址時(shí)先寫(xiě)垂直地址(行地址)再寫(xiě)水平地址(列地址),也就是連續(xù)寫(xiě)入兩個(gè)地址,然后再連續(xù)寫(xiě)入2個(gè)字節(jié)的數(shù)據(jù)。如圖中所示,左邊為高字節(jié)右邊為低字節(jié)。為1的點(diǎn)被描黑,為0的點(diǎn)則顯示空白。這里列舉個(gè)寫(xiě)地址的例子:寫(xiě)GDRAM地址指令是0x80+地址。被加上的地址就是上面列舉的X和Y,假設(shè)我們要寫(xiě)第一行的2個(gè)字節(jié),那么寫(xiě)入地址就是0x00H(寫(xiě)行地址)然后寫(xiě)0x80H(列地址),之后才連續(xù)寫(xiě)入2個(gè)字節(jié)的數(shù)據(jù)(先高字節(jié)后低字節(jié))。再如寫(xiě)屏幕右下角的2個(gè)字節(jié),先寫(xiě)行地址0x9F(0x80+32),再寫(xiě)列地址0x8F(0x80+15),然后連續(xù)寫(xiě)入2個(gè)字節(jié)的數(shù)據(jù)。編程中寫(xiě)地址函數(shù)中直接用參數(shù)(0x+32),而不必自己相加。 讀數(shù)據(jù):先開(kāi)啟擴(kuò)展指令集,然后寫(xiě)行地址、寫(xiě)列地址,假讀一次,再連續(xù)讀2字節(jié)的數(shù)據(jù)(先高字節(jié)后低字節(jié))。
讀寫(xiě)時(shí)序: 讀寫(xiě)時(shí)序圖如下:(上圖為寫(xiě),下圖為讀) 時(shí)序圖中的信號(hào)引腳就是12864主要的引腳,分別是: RS:命令/數(shù)據(jù)寄存器選擇端 WR:讀寫(xiě)控制端
E:使能端 DB7~DB0:數(shù)據(jù)端
所有對(duì)12864的操作都是圍繞著幾根引腳展開(kāi)的。包括寫(xiě)命令、寫(xiě)數(shù)據(jù)、讀數(shù)據(jù)、讀狀態(tài)就是通過(guò)這些引腳的高低電平搭配來(lái)實(shí)現(xiàn)的。
根據(jù)時(shí)序圖可以編寫(xiě)相應(yīng)的寫(xiě)命令函數(shù)、寫(xiě)數(shù)據(jù)函數(shù)、讀數(shù)據(jù)函數(shù)、讀狀態(tài)函數(shù)。需要的注意的是有效數(shù)據(jù)出現(xiàn)的那段時(shí)間Tc必須合適,不能太短,否則會(huì)造成讀寫(xiě)失敗。
給出幾個(gè)函數(shù)示例: //忙檢測(cè),若忙則等待,最長(zhǎng)等待時(shí)間為60ms
void busychk_12864(void){ unsigned int timeout = 0;
E_12864 = 0;
RS_12864 = 0;
RW_12864 = 1;
E_12864 = 1;
while((IO_12864 & 0x80)&& ++timeout != 0); //忙狀態(tài)檢測(cè),等待超時(shí)時(shí)間為60ms
E_12864 = 0; }
//寫(xiě)命令子程序
void wrtcom_12864(unsigned char com){ busychk_12864();
E_12864 = 0;
RS_12864 = 0;
RW_12864 = 0;
IO_12864 = com;
E_12864 = 1;
delay_12864(50); //50us使能延時(shí)!!!注意這里,如果是較快的CPU應(yīng)該延時(shí)久一些
E_12864 = 0;
}
//讀數(shù)據(jù)子程序 unsigned char reddat_12864(void){
unsigned char temp; busychk_12864();
E_12864 = 0;
IO_12864 = 0xff;//IO口置高電平,讀引腳
RS_12864 = 1;
RW_12864 = 1;
E_12864 = 1;
delay_12864(50); //使能延時(shí)!!!注意這里,如果是較快的CPU應(yīng)該延時(shí)久一些
temp = IO_12864;
return temp;
}
//寫(xiě)數(shù)據(jù)子程序
void wrtdat_12864(unsigned char dat){ busychk_12864();
E_12864 = 0;
RS_12864 = 1;
RW_12864 = 0;
E_12864 = 1;
IO_12864 = dat;
delay_12864(50); //使能延時(shí)!!!注意這里,如果是較快的CPU應(yīng)該延時(shí)久一些
E_12864 = 0;
}
其中,忙檢測(cè)是必要的,當(dāng)BF=1時(shí),表示內(nèi)部正在進(jìn)行有關(guān)的操作,即處于忙狀態(tài)。在BF變回0之前ST7920不會(huì)接受任何指令。MCU必須檢測(cè)BF以確定ST7920內(nèi)部操作是否完成,然后才能再發(fā)送指令。也可以用延時(shí)來(lái)替代忙檢測(cè),但是需要延時(shí)足夠的時(shí)間。盲檢測(cè)實(shí)際就是讀內(nèi)部的狀態(tài)寄存器,該寄存器最高位(D7)為忙標(biāo)志BF,剩余7位為地址指針的內(nèi)容,所以進(jìn)行盲檢測(cè)實(shí)際上也把地址指針中的地址讀出來(lái)了。
指令集: 指令集分為基本指令集和擴(kuò)展指令集,使用相應(yīng)的指令集必須先寫(xiě)相應(yīng)指令表明后續(xù)指令均為該類指令。如使用基本指令集時(shí),寫(xiě)指令(0x30),需要使用擴(kuò)展指令集時(shí)寫(xiě)指令(0x34)切換到擴(kuò)展指令集。
一)基本指令集(RE=0):(使用擴(kuò)展指令集先寫(xiě)指令0x30,這使得RE=0)
清屏指令(0x01):往DDRAM寫(xiě)滿0x20,指針地址寫(xiě)0x00。表現(xiàn)在屏幕就是顯示空白。 回車(chē)指令(0x02/0x03):地址指針內(nèi)容寫(xiě)0x00. 進(jìn)入模式:0 0 0 0 0 1 I/D S:設(shè)置讀寫(xiě)數(shù)據(jù)之后光標(biāo)、顯示移位的方向。內(nèi)部有2個(gè)可編程位,I/D表示讀寫(xiě)一個(gè)字符后數(shù)據(jù)指針是加一還是減一。I/D=1指針加一,I/D=0指針減一。S=1開(kāi)啟整屏移動(dòng)。 S I/D= H H,屏幕每次左移一個(gè)字符。 S I/D= H L ,屏幕每次右移一個(gè)字符。 但是平時(shí)不開(kāi)啟屏幕移動(dòng),這里說(shuō)明一個(gè)概念,就是屏幕移動(dòng),實(shí)際試驗(yàn)中若開(kāi)啟了屏幕移動(dòng)你會(huì)發(fā)生顯示是灰常怪異的,說(shuō)明如下:由于DDRAM的結(jié)構(gòu)是下方表所示: 上半屏 下半屏 80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、8EH、8FH 90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、9EH、9FH A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、ADH、AEH、AFH B0H、B1H、B2H、B3H、B4H、B5H、B6H、B7H、B8H、B9H、BAH、BBH、BCH、BDH、BEH、BFH 在未開(kāi)啟屏移時(shí),屏幕是以表格第一列作為參考起點(diǎn),然后前8列歸上半屏顯示,后8列歸下半屏顯示。如果此時(shí)向左屏移一個(gè)字符,那么DDRAM內(nèi)容與顯示映射關(guān)系變?yōu)椋?/font> 80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、8EH、8FH 90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、9EH、9FH A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、ADH、AEH、AFH B0H、B1H、B2H、B3H、B4H、B5H、B6H、B7H、B8H、B9H、BAH、BBH、BCH、BDH、BEH、BFH 可以看到實(shí)際上原來(lái)第三第四行開(kāi)始的字符跑到了第一行第二行的末尾,整個(gè)DDRAM的結(jié)構(gòu)就是一種循環(huán)的結(jié)構(gòu),發(fā)生屏移時(shí)DDRAM與顯示映射關(guān)系不斷在改變。但是這不太符合我們的閱讀習(xí)慣,所以如果需要使用該項(xiàng)功能還需編程校正之。 顯示、光標(biāo)、閃爍開(kāi)關(guān):0 0 0 0 0 0 1 D C B: D=1: 顯示開(kāi)(Display) C=1: 光標(biāo)開(kāi)(Cursor) B=1: 光標(biāo)位置閃爍開(kāi)(Blink)。為0則為關(guān)。 光標(biāo)顯示移位控制:0 0 0 1 S/C R/L X X

說(shuō)明:LL:這時(shí)僅僅是將地址指針AC的值減1。在屏幕上表現(xiàn)是光標(biāo)左移一個(gè)字符。 LH:這時(shí)僅僅是將地址指針AC的值加1。在屏幕上表現(xiàn)是光標(biāo)右移一個(gè)字符。 HL:AC指針不變,向左屏移一個(gè)字符。這是DDRAM結(jié)構(gòu)循環(huán)左移,80H接在8FH后面,90H接在9FH后面。這與上面講的屏移是一樣的。 HH:AC指針不變,向右屏移一個(gè)字符。這是DDRAM結(jié)構(gòu)循環(huán)右移,80H接在8FH后面,90H接在9FH后面。 功能設(shè)置:0 0 1 DL X RE X X:(切換基本指令集與擴(kuò)展指令集) DL=1表示8為接口,DL=0表示4為接口。 RE=1表示開(kāi)啟擴(kuò)展指令,RE=0表示使用基本指令。 開(kāi)啟基本指令則設(shè)置為0x30,開(kāi)啟擴(kuò)展指令則設(shè)置為0x34。 CGRAM地址設(shè)置:0x40+地址。地址范圍是00H~3FH。前提是SR=0,即允許設(shè)置IRAM和CGRAM地址。。 DDRAM地址設(shè)置:只有字地址。如下表所示。(注意DDRAM地址有4行×16字)如下所示: 80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、8EH、8FH 90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、9EH、9FH A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、ADH、AEH、AFH B0H、B1H、B2H、B3H、B4H、B5H、B6H、B7H、B8H、B9H、BAH、BBH、BCH、BDH、BEH、BFH 所以某一時(shí)刻只能顯示其中的2行。只有卷動(dòng)顯示才能將另兩行的數(shù)據(jù)顯示出來(lái)。 讀忙標(biāo)志(地址):同時(shí)忙標(biāo)志和地址讀出來(lái)。忙狀態(tài)時(shí),ST7920不會(huì)接受任何指令。按照時(shí)序圖將RS置0,RW置1,然后讀取狀態(tài)寄存器。 寫(xiě)RAM(DDRAM/CGRAM/GDRAM):寫(xiě)了控制邏輯(函數(shù)wrtcom_12864(地址);)之后,直接送數(shù)據(jù)(wrtdat_12864)。寫(xiě)完后地址指針根據(jù)進(jìn)入模式中的設(shè)置加一或減一。寫(xiě)數(shù)據(jù)前先寫(xiě)地址,而寫(xiě)地址本身是一個(gè)寫(xiě)地址命令,然后再寫(xiě)數(shù)據(jù)。 讀RAM(DDRAM/CGRAM/GDRAM):記得先假讀一次,后面的才是真讀,假讀之后不需要再假讀了,除非重設(shè)了地址。
二)擴(kuò)展指令集(RE=1):(使用擴(kuò)展指令集先寫(xiě)指令0x34,這使得RE=1)
待機(jī)模式:0x01,不影響DDRAM,所以跟清屏指令不同,任何指令可以結(jié)束待機(jī)模式。 卷動(dòng)地址/IRAM地址允許設(shè)置:0 0 0 0 0 0 1 SR: SR=1:允許設(shè)置垂直卷動(dòng)地址。SR=0:允許設(shè)置IRAM和CGRAM地址。 設(shè)置卷動(dòng)/IRAM地址:0x40+地址。(卷動(dòng)地址為行地址,即縱向地址). 這里講解卷動(dòng),卷動(dòng)就是上下滾屏,實(shí)現(xiàn)屏幕的垂直滾動(dòng)。 卷動(dòng)地址:地址范圍為0x00~0x63,共64行卷動(dòng)地址其實(shí)就是垂直地址。每一個(gè)地址代表著DDRAM中的一行的像素點(diǎn)。卷動(dòng)一次就是把該行所有點(diǎn)移到上半屏和下半屏幕最上方。 80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、8EH、8FH 90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、9EH、9FH A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、ADH、AEH、AFH B0H、B1H、B2H、B3H、B4H、B5H、B6H、B7H、B8H、B9H、BAH、BBH、BCH、BDH、BEH、BFH 還是DDRAM的結(jié)構(gòu)圖,需要注意的是卷屏是分上半屏卷動(dòng)和下半屏卷動(dòng),兩屏之間沒(méi)有關(guān)系,也就是DDRAM中左邊紅色部分在上半屏滾動(dòng),右邊綠色部分在下半屏滾動(dòng)。 B0H、B1H、B2H、B3H、B4H、B5H、B6H、B7H 的下一行是 80H、81H、82H、83H、84H、85H、86H、87H 也就是說(shuō)左邊是一個(gè)上下相接的循環(huán)結(jié)構(gòu)。同理右邊也是上下相接的循環(huán)結(jié)構(gòu)。左邊內(nèi)存中的字符上下滾動(dòng)。右邊內(nèi)存中的字符上下滾動(dòng),兩者木有關(guān)系。 要開(kāi)啟卷動(dòng),首先開(kāi)啟擴(kuò)展指令集,然后允許卷動(dòng)地址設(shè)置,再設(shè)置卷動(dòng)地址。 wrtcom_12864(0x34); //打開(kāi)擴(kuò)展指令
wrtcom_12864(0x03); //允許輸入卷動(dòng)地址
wrtcom_12864(0x40 + 地址 //設(shè)置卷動(dòng)地址
wrtcom_12864(0x30); //回到基本指令 要實(shí)現(xiàn)全屏滾動(dòng),就必須使用循環(huán)不斷地修改卷動(dòng)地址。從00~63如此循環(huán),但遺憾的是這也不符合我們的閱讀習(xí)慣,后續(xù)的應(yīng)用的中將講解全屏滾動(dòng)的實(shí)現(xiàn)方法。這里只是把卷動(dòng)原理講清楚。 反白顯示:0 0 0 0 0 1 R1 R0: R1、R0初始化的值為00。選擇1~4任一行反白顯示并可決定是否反白。 如何開(kāi)啟反白顯示:首先開(kāi)啟擴(kuò)展指令(0x34),然后設(shè)置選中某一行設(shè)置反白顯示(0x04+R1R0)。00為第一行,01為第二行,10為第三行,11為第四行。需要說(shuō)明的是,這里的行是指DDRAM所有內(nèi)存的行,而不是顯示的行,屏幕只顯示2行。 所以如果我們開(kāi)啟第3第4行的反白顯示,不卷動(dòng)我們是看不到效果的。 同時(shí),如果我們開(kāi)啟第1行反白顯示,那么在屏幕中第1行第3行都會(huì)反白顯示,第2行則對(duì)應(yīng)屏幕第2第4行,這一點(diǎn)需要注意。 如何關(guān)閉反白顯示:只需在此寫(xiě)一次地址即可關(guān)閉,也就說(shuō),第一次寫(xiě)第一開(kāi)啟反白,第二次寫(xiě)相同的地址關(guān)閉反白顯示。 wrtcom_12864(0x34); //反白顯示試驗(yàn)
wrtcom_12864(0x04); //開(kāi)啟反白顯示
delay_12864(60000); //延時(shí)
delay_12864(60000); //延時(shí) wrtcom_12864(0x04); //關(guān)閉反白顯示
wrtcom_12864(0x30); //開(kāi)啟基本指令集 擴(kuò)展功能設(shè)置:0x36設(shè)置繪圖顯示開(kāi)。 當(dāng)GDRAM寫(xiě)完了之后,寫(xiě)0x36則屏幕顯示你所繪制的圖形。 0 0 0 0 1 DL x RE G x (RE=1擴(kuò)展指令,G=1開(kāi)繪圖顯示,DL=1表示8為接口) 設(shè)置GDRAM地址:繪圖時(shí),需要將GDRAM的地址寫(xiě)入地址指針中,然后才能寫(xiě)入數(shù)據(jù)。連續(xù)寫(xiě)入兩個(gè)字節(jié),第一個(gè)為行地址(Y),第二個(gè)為列地址(X)。 需要注意的是:寫(xiě)了數(shù)據(jù)之后,地址指針會(huì)自動(dòng)加一(以字為單位),當(dāng)?shù)竭_(dá)該行的行尾時(shí),指針下一次加一會(huì)使得地址指針跳回該行行首,也就說(shuō)如果地址值為8FH時(shí),下一次它就是80H(以第一行為例)。指針地址在本行之間循環(huán)。 指令介紹完 再講下初始化過(guò)程,根據(jù)ST7920的手冊(cè)提供的初始化步驟就可以了。 

初始化函數(shù)如下://延時(shí)子程序
void delay_12864(unsigned int del){
unsigned int i;
for(i = 0; i < del; i++){; }
}
//初始化12864子函數(shù)
void initial_12864(void){ delay_12864(40000);
RST_12864 = 1;
RST_12864 = 0; //復(fù)位
delay_12864(500);
RST_12864 = 1; wrtcom_12864(0x30); //設(shè)置為基本指令集動(dòng)作
delay_12864(100);
wrtcom_12864(0x30); //設(shè)置為基本指令集動(dòng)作
delay_12864(37);
wrtcom_12864(0x08); //設(shè)置顯示、光標(biāo)、閃爍全關(guān)。
delay_12864(100);
wrtcom_12864(0x01); //清屏,并且DDRAM數(shù)據(jù)指針清零
delay_12864(100000);
wrtcom_12864(0x06); //進(jìn)入模式設(shè)置
}
應(yīng)用部分: 這里講解12864的幾個(gè)典型應(yīng)用: 1)、自編字符創(chuàng)建以及顯示 2)、GDRAM的繪制及顯示 3)、全屏卷動(dòng)的實(shí)現(xiàn)方法
1)、自編字符創(chuàng)建以及顯示 先明確的要點(diǎn),12864具有4個(gè)自編字符,每個(gè)字符的編碼為0000H、0002H、0004H、0006H,4個(gè)自定義字符的CGRAM地址分別為00H~0FH、10H~1FH、20H~2FH、30H~3FH。 我們以第3個(gè)字符為例: 在這里先把整個(gè)源文件的宏定義以及各子函數(shù)貼出: #include< reg52.h> #define IO_12864 P0 sbit RS_12864 = P2^5;
sbit RW_12864 = P2^6;
sbit E_12864 = P2^7;
sbit RST_12864 = P2^2; //忙檢測(cè),若忙則等待,最長(zhǎng)等待時(shí)間為60ms
void busychk_12864(void){ unsigned int timeout = 0;
E_12864 = 0;
RS_12864 = 0;
RW_12864 = 1;
E_12864 = 1;
while((IO_12864 & 0x80)&& ++timeout != 0); //忙狀態(tài)檢測(cè),等待超時(shí)時(shí)間為60ms
E_12864 = 0; }
//寫(xiě)命令子程序
void wrtcom_12864(unsigned char com){ busychk_12864();
E_12864 = 0;
RS_12864 = 0;
RW_12864 = 0;
IO_12864 = com;
E_12864 = 1;
delay_12864(50); //使能延時(shí)!!!注意這里,如果是較快的CPU應(yīng)該延時(shí)久一些
E_12864 = 0;
}
//讀數(shù)據(jù)子程序
unsigned char reddat_12864(void){
unsigned char temp; busychk_12864();
E_12864 = 0;
IO_12864 = 0xff; //IO口置高電平,讀引腳
RS_12864 = 1;
RW_12864 = 1;
E_12864 = 1;
delay_12864(50); //使能延時(shí)!!!注意這里,如果是較快的CPU應(yīng)該延時(shí)久一些
temp = IO_12864;
return temp;
}
//寫(xiě)數(shù)據(jù)子程序
void wrtdat_12864(unsigned char dat){ busychk_12864();
E_12864 = 0;
RS_12864 = 1;
RW_12864 = 0;
E_12864 = 1;
IO_12864 = dat;
delay_12864(50); //使能延時(shí)!!!注意這里,如果是較快的CPU應(yīng)該延時(shí)久一些
E_12864 = 0;
}
//初始化12864子函數(shù)
void initial_12864(void){ delay_12864(40000);
RST_12864 = 1;
RST_12864 = 0; //復(fù)位
delay_12864(500);
RST_12864 = 1; wrtcom_12864(0x30); //設(shè)置為基本指令集動(dòng)作
delay_12864(100);
wrtcom_12864(0x30); //設(shè)置為基本指令集動(dòng)作
delay_12864(37);
wrtcom_12864(0x08); //設(shè)置顯示、光標(biāo)、閃爍全關(guān)。
delay_12864(100);
wrtcom_12864(0x01); //清屏,并且DDRAM數(shù)據(jù)指針清零
delay_12864(100000);
wrtcom_12864(0x06); //進(jìn)入模式設(shè)置 wrtcom_12864(0x0c); //開(kāi)顯示
}
以上函數(shù)定義在main()函數(shù)之前,我們?cè)谥骱瘮?shù)中編寫(xiě)程序: void main(){ unsigned char i,*addr; unsigned char defchar[] = {0x08,0x10,0x08,0x10,0x08,0x10,0x7F,0xFE,0x20,0x04,0x12,0x48,0x08,0x10,0x05,0xA0,0x02,0x40,0x01,0x80,0x01,0x80,0x07,0xE0,0x09,0x90,0x11,0x88,0x11,0x88,0x11,0x88}; //自定義字符,這里是筆者畫(huà)的一個(gè)小機(jī)器人。
delay_12864(100); //啟動(dòng)延時(shí) initial_12864(); //初始化12864 addr = defchar; wrtcom_12864(0x40+0x20); //寫(xiě)CGRAM首行地址 for(i = 0; i< 32; i++){ wrtdat_12864(*addr++); } wrtcom_12864(0x80); //在第一行第一個(gè)字符出顯示自定義字符 wrtdat_12864(0x00); //寫(xiě)第三個(gè)自定義字符編碼的高字節(jié) wrtdat_12864(0x04); //寫(xiě)第三個(gè)自定義字符編碼的低字節(jié)
while(1);
} 運(yùn)行程序就可以看到第一個(gè)字符處出現(xiàn)一個(gè)小機(jī)器人了。
2)、GDRAM的繪制及顯示 先明確的要點(diǎn),GDRAM是32行×16字。寫(xiě)數(shù)據(jù)之前必須先送行地址,然后送列地址。讀寫(xiě)的基本操作單元是字(2個(gè)字節(jié))。讀寫(xiě)完一個(gè)字后地址指針在本行自動(dòng)加一,到達(dá)行末則返回行首地址(地址循環(huán))。 我們這里先以一個(gè)畫(huà)點(diǎn)函數(shù)函數(shù)為例,然后再根據(jù)畫(huà)點(diǎn)函數(shù)寫(xiě)一個(gè)繪制矩形的函數(shù): 先建一個(gè)坐標(biāo)左上角為(0,0),右下角為(63,127)。 畫(huà)點(diǎn)原理:由于GDRAM的讀寫(xiě)基本操作單元是字,那么我們需要畫(huà)一個(gè)點(diǎn)但是又不改變其他點(diǎn)的內(nèi)容,那么需要把該點(diǎn)所處的字中的2個(gè)字節(jié)均讀出,然后再單獨(dú)修改我們需要畫(huà)的那個(gè)點(diǎn)(其他位保持不變),最后把該字再寫(xiě)回去。 因此,涉及的操作有先讀GDRAM,再寫(xiě)GDRAM,再顯示GDRAM。 在寫(xiě)主函數(shù)之前先寫(xiě)幾個(gè)子函數(shù),說(shuō)明其作用: void clnGDR_12864(void) //清空GDRAM
void drawdot_12864(unsigned char y,unsigned char x,unsigned char type) //畫(huà)點(diǎn)子函數(shù)
為什么要清空GDRAM呢,因?yàn)橹噶罴袥](méi)有GDRAM清空指令,而我們往里寫(xiě)了什么它就會(huì)一直保存著,所以我們畫(huà)點(diǎn)之前先清空GDRAM,其實(shí)清空GDRAM就是不斷往里寫(xiě)0x00。
//清空GDRAM,總共就是寫(xiě)1KB的0x00。
void clnGDR_12864(void){ unsigned char j,k; wrtcom_12864(0x34); //在寫(xiě)GDRAM的地址之前一定要打開(kāi)擴(kuò)充指令集 //否則地址寫(xiě)不進(jìn)去!!
for( j = 0 ; j < 32 ; j++ )
{
wrtcom_12864(0x80 + j) ; //寫(xiě)Y 坐標(biāo)
wrtcom_12864(0x80) ; //寫(xiě)X 坐標(biāo)
for( k = 0 ; k < 32 ; k++ ) //寫(xiě)一整行數(shù)據(jù)
{
wrtdat_12864( 0x00 );
}
}
}
//畫(huà)點(diǎn)函數(shù),左上角為參考點(diǎn)(0,0)
//右下角為(63,127),點(diǎn)坐標(biāo)形式為(行坐標(biāo),列坐標(biāo))
//參數(shù)type用于設(shè)置畫(huà)黑點(diǎn)、白點(diǎn)或取反(黑變白,白變黑)
//type = 0為白色,1 為黑色,2為取反
void drawdot_12864(unsigned char y,unsigned char x,unsigned char type){
unsigned char X,Y,k; //X存儲(chǔ)行地址,Y存儲(chǔ)列地址 //k存儲(chǔ)點(diǎn)在字中的位置從左至右為0~15
unsigned char DH,DL; //存放讀出數(shù)據(jù)的高字節(jié)和低字節(jié)
if(y >= 0&& y <= 63&& x >= 0&& x <= 127) {
if(y < 32){ //算法:確定所畫(huà)點(diǎn)的地址行與列地址
X = 0x80 + (x>> 4);
Y = 0x80 + y;
}else{
X = 0x88 + (x>> 4);
Y = 0x80 + (y - 32);
}
wrtcom_12864(0x34); //開(kāi)啟擴(kuò)展指令,關(guān)閉繪圖顯示
wrtcom_12864(Y); //寫(xiě)入所確定的點(diǎn)的行位地址
wrtcom_12864(X); //寫(xiě)入所確定的點(diǎn)的列字地址
DH = reddat_12864();//假讀
DH = reddat_12864(); //讀高字節(jié)
DL = reddat_12864(); //讀低字節(jié)
k = x % 16; //余數(shù)為點(diǎn)在字中的位置
//畫(huà)點(diǎn)
switch(type){ //畫(huà)點(diǎn)類型,1黑或0白或2取反
case 0:
if(k < 8){ //點(diǎn)在高字節(jié)
DH& = ~(0x01 << (7 - k)); //修改該點(diǎn)同時(shí)保持其他位不變
}else{ //點(diǎn)在低字節(jié)
DL& = ~(0x01 << (7 - (k % 8))); //修改該點(diǎn)同時(shí)保持其他位不變
}
break;
case 1:
if(k< 8){
DH |= (0x01 << (7 - k)); //修改該點(diǎn)同時(shí)保持其他位不變
}else{
DL |= (0x01 << (7 - (k % 8))); //修改該點(diǎn)同時(shí)保持其他位不變
}
break;
case 2:
if(k< 8){
DH ^= (0x01 << (7 - k)); //修改該點(diǎn)同時(shí)保持其他位不變
}else{
DL ^= (0x01 << (7 - (k % 8))); //修改該點(diǎn)同時(shí)保持其他位不變
}
break;
default:
break;
}
wrtcom_12864(Y); //寫(xiě)行位地址
wrtcom_12864(X); //寫(xiě)列字地址
wrtdat_12864(DH); //將高字節(jié)數(shù)據(jù)寫(xiě)回
wrtdat_12864(DL); //將低字節(jié)數(shù)據(jù)寫(xiě)回
wrtcom_12864(0x30); //轉(zhuǎn)回普通指令
}
}
下面編寫(xiě)主函數(shù),這就簡(jiǎn)單了,如下:
void main(void){ delay_12864(1000);
initial_12864(); clnGDR_12864(); //清空GDRAM
drawdot_12864(20,50,1); //畫(huà)點(diǎn)
wrtcom_12864(0x36); //開(kāi)繪圖顯示 while(1);
}
程序運(yùn)行后相應(yīng)位置出現(xiàn)了一個(gè)黑點(diǎn),手機(jī)壞了,拍不了照,不然就貼下照片。 然后根據(jù)畫(huà)點(diǎn)函數(shù),擴(kuò)展一個(gè)畫(huà)矩形的函數(shù)吧:
//畫(huà)矩形子函數(shù),參數(shù)為(點(diǎn)1行坐標(biāo),點(diǎn)1列坐標(biāo), //點(diǎn)2行坐標(biāo),點(diǎn)2列坐標(biāo),線條顏色(0為白,1為黑,2對(duì)原色取反))
void drawrec_12864(unsigned char y1,unsigned char x1,unsigned char y2,unsigned char x2,unsigned char type){
unsigned char largex,largey,smallx,smally; //將兩點(diǎn)橫縱坐標(biāo)按大小存儲(chǔ)
unsigned char i; if(x1 > x2){
largex = x1;
smallx = x2;
}else{
largex = x2;
smallx = x1;
}
if(y1 > y2){
largey = y1;
smally = y2;
}else{
largey = y2;
smally = y1;
}
//以下繪制4條矩形邊框 for(i = smallx; i < largex; i++){
drawdot_12864(largey,i,type);
}
for(i = largey; i > smally; i--){
drawdot_12864(i,largex,type);
}
for(i = largex; i > smallx; i--){
drawdot_12864(smally,i,type);
}
for(i = smally; i < largey; i++){
drawdot_12864(i,smallx,type);
}
wrtcom_12864(0x30); //返回普通指令
}
主函數(shù)為: void main(void){ delay_12864(1000);
initial_12864(); clnGDR_12864(); //清空GDRAM
drawrec_12864(20,50,30,120,1); //畫(huà)矩形 wrtcom_12864(0x36); //開(kāi)繪圖顯示 while(1);
}
關(guān)于GDRAM的操作就到這吧,下面講解下12864全屏卷動(dòng)的實(shí)現(xiàn)方法。
3)、12864全屏卷動(dòng)的實(shí)現(xiàn)方法 首先需要明確的要點(diǎn): DDRAM的結(jié)構(gòu)如下: 80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、8EH、8FH 90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、9EH、9FH A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、ADH、AEH、AFH B0H、B1H、B2H、B3H、B4H、B5H、B6H、B7H、B8H、B9H、BAH、BBH、BCH、BDH、BEH、BFH 卷屏是分上下屏個(gè)各自卷動(dòng)的,上半屏卷動(dòng)左邊紅色區(qū)域的內(nèi)容,下半屏卷動(dòng)右邊綠色區(qū)域的內(nèi)容。
為了實(shí)現(xiàn)全屏卷動(dòng)顯示,必須使用拼接的方法實(shí)現(xiàn)。 筆者花了幾個(gè)小時(shí)研究了下算法,然后第二天實(shí)現(xiàn)了,F(xiàn)講述如下: 細(xì)心觀察DDRAM的結(jié)構(gòu)發(fā)現(xiàn),如果在卷動(dòng)過(guò)程中,在同一時(shí)刻屏幕顯示的內(nèi)容最多涉及3行DDRAM的內(nèi)容,而另一行是沒(méi)有顯示的,那么這一行就是用來(lái)緩存的數(shù)據(jù)的。 當(dāng)屏幕顯示如下2行時(shí)開(kāi)始卷動(dòng)(一): 80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、8EH、8FH 90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、9EH、9FH 則屏幕同時(shí)出現(xiàn)以下3行DDRAM內(nèi)容(二): 80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、8EH、8FH 90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、9EH、9FH A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、ADH、AEH、AFH 需要注意的是,左邊是上半屏顯示,右邊是下半屏顯示。 在程序的開(kāi)始處往DDRAM對(duì)應(yīng)區(qū)域填寫(xiě)如下內(nèi)容: 第一行字符 第三行字符--> 開(kāi)始顯示 第二行字符 第四行字符 第三行字符 第五行字符--> 即將顯示 第四行字符 第六行字符 這樣在開(kāi)始卷動(dòng)之后,就可以實(shí)現(xiàn)拼接的效果了。當(dāng)卷動(dòng)了16次之后,也就是第一行字符已經(jīng)移出屏幕,屏幕顯示的DDRAM如下: 第一行字符 第三行字符 第二行字符 第四行字符 第三行字符 第五行字符 第四行字符 第六行字符 此時(shí),屏幕接著滾動(dòng),顯示內(nèi)容涉及3行的DDRAM,如下: 第一行字符 第三行字符--> 已顯示完畢 第二行字符 第四行字符 第三行字符 第五行字符 第四行字符 第六行字符--> 即將顯示 第一行DDRAM是空余的,下次就該往第一行寫(xiě)數(shù)據(jù),寫(xiě)完后DDRAM內(nèi)容如下: 第五行字符 第七行字符 第二行字符 第四行字符 第三行字符 第五行字符 第四行字符 第六行字符 經(jīng)過(guò)又一次的16次卷屏之后屏幕顯示內(nèi)容如下: 第五行字符 第七行字符--> 即將顯示 第二行字符 第四行字符--> 顯示完畢 第三行字符 第五行字符 第四行字符 第六行字符 然后接下來(lái)又卷動(dòng)16次,筆者的算法是,在每一次卷動(dòng)后寫(xiě)一個(gè)字到顯示完畢的那一行中,卷完16次,顯示完畢的那一行也就寫(xiě)完了。然后接下來(lái)的16次卷動(dòng)又寫(xiě)剛剛顯示完畢的那一行,而剛被寫(xiě)完的那一行將在后面16次卷動(dòng)中顯示。 原理就是如此,然后從中提取出規(guī)律,設(shè)計(jì)出算法,并編程實(shí)現(xiàn): 下面是程序?qū)崿F(xiàn): void main(void){ unsigned char code ser[] = {"一一一一一一一一二二二二二二二二叁叁叁叁叁叁叁叁四四四四四四四四中國(guó)中國(guó)中國(guó)中國(guó)"}; //這是要顯示的字符串 //沒(méi)有檢測(cè)換行符功能,只能顯示一長(zhǎng)串的漢字或一串ASCII碼字符。
unsigned char i,addr,flag,hang,over,*ptdat; //addr用于存儲(chǔ)寫(xiě)入地址 //flag存儲(chǔ)卷動(dòng)地址,名字沒(méi)取好! //hang存儲(chǔ)下一行要寫(xiě)入數(shù)據(jù)的行號(hào)(1~4) //over記錄寫(xiě)入的空字符數(shù) //ptdat存儲(chǔ)字符串的指針 delay_12864(1000);
initial_12864(); ptdat = ser;
over = 0; //寫(xiě)入空字符數(shù) //這里先把前面DDRAM中的前3行的字符數(shù)據(jù)寫(xiě)入 //如果字符不足<=4行,那么不卷動(dòng),之后字符>4行才卷動(dòng) //一直到末行顯示完畢則停止卷動(dòng) wrtcom_12864(0x80); //寫(xiě)屏幕第一行字符
for(i = 0; i < 16; i++){
if(*ptdat != ''){
wrtdat_12864(*ptdat++);
}else{
wrtdat_12864(0x20);
over++;
}
}
wrtcom_12864(0x90); //寫(xiě)屏幕第二行字符 for(i = 0; i< 16; i++){
if(*ptdat != ''){
wrtdat_12864(*ptdat++);
}else{
wrtdat_12864(0x20);
over++;
}
}
wrtcom_12864(0x88);//寫(xiě)屏幕第三行字符
for(i = 0; i < 16; i++){
if(*ptdat != ''){
wrtdat_12864(*ptdat++);
}else{
wrtdat_12864(0x20);
over++;
}
}
wrtcom_12864(0x98);//寫(xiě)屏幕第四行字符
for(i = 0; i < 16; i++){
if(*ptdat != ''){
wrtdat_12864(*ptdat++);
}else{
wrtdat_12864(0x20);
over++;
}
} ptdat = ptdat - 32;
wrtcom_12864(0xa0); //寫(xiě)DDRAM第3行數(shù)據(jù)
for(i = 0; i < 16; i++){
if(*ptdat != ''){
wrtdat_12864(*ptdat++);
}else{
wrtdat_12864(0x20);
over++;
}
} ptdat = ptdat + 16;
for(i = 0; i < 16; i++){
if(*ptdat != ''){
wrtdat_12864(*ptdat++);
}else{
wrtdat_12864(0x20);
over++;
}
} //前面的代碼是往DDRAM中寫(xiě)如下內(nèi)容: //第一行字符 第三行字符 //第二行字符 第四行字符 //第三行字符 第五行字符 //如果寫(xiě)第5行時(shí)全為空,說(shuō)明字符剛好4行,不卷動(dòng)。 //此時(shí)第5行寫(xiě)入16個(gè)0x20空字符,over用于記錄空字符個(gè)數(shù)。 //如果不足4行,則前面也將會(huì)寫(xiě)入空字符,此時(shí)寫(xiě)完了3行DDRAM后 //over的值必大于16,而只要over>15,就不卷動(dòng) wrtcom_12864(0x0c); //開(kāi)顯示
if(over> 15){;} //顯示字符不足4行,不卷動(dòng)
else //顯示字符大于4行,開(kāi)啟卷動(dòng)
{ hang = 4; //接下來(lái)要寫(xiě)DDRAM第4行數(shù)據(jù)
flag = 0x01; //初始卷動(dòng)地址為1
while(1){
switch(hang){ //設(shè)置寫(xiě)入DDRAM的地址
case 1: addr = 0x80; break; //往地址變量中寫(xiě)第一行首地址
case 2: addr = 0x90; break; //往地址變量中寫(xiě)第二行首地址
case 3: addr = 0xa0; break; //往地址變量中寫(xiě)第三行首地址
case 4: addr = 0xb0; break; //往地址變量中寫(xiě)第四行首地址 }
switch(hang){ //指出下一次要寫(xiě)的行地址
case 1: hang = 2; break;//第1行寫(xiě)完了,下一行要寫(xiě)第2行
case 2: hang = 3; break;//第2行寫(xiě)完了,下一行要寫(xiě)第3行
case 3: hang = 4; break;//第3行寫(xiě)完了,下一行要寫(xiě)第4行
case 4: hang = 1; break;//第4行寫(xiě)完了,下一行要寫(xiě)第1行
}
//后續(xù)代碼為往每一行寫(xiě)數(shù)據(jù),卷動(dòng)一次寫(xiě)一個(gè)字。
ptdat = ptdat - 32;
for(i = 0; i< 8; i++){ //寫(xiě)一行中的前8個(gè)字符
wrtcom_12864(0x34); //打開(kāi)擴(kuò)展指令
wrtcom_12864(0x03); //允許輸入卷動(dòng)地址
wrtcom_12864(0x40 + flag++); //設(shè)置卷動(dòng)地址
wrtcom_12864(0x30); //回到基本指令
wrtcom_12864(addr + i);
delay_12864(20000);
if(*ptdat != ''){
wrtdat_12864(*ptdat++); //寫(xiě)入高字節(jié)
}else{
wrtdat_12864(0x20); //字符串結(jié)束則寫(xiě)入空字符
}
if(*ptdat != ''){
wrtdat_12864(*ptdat++); //寫(xiě)入低字節(jié)
}else{
wrtdat_12864(0x20); //字符串結(jié)束則寫(xiě)入空字符
}
}
ptdat = ptdat + 16;
for(i = 8; i< 16; i++){ //寫(xiě)一行中的后8個(gè)字符
wrtcom_12864(0x34); //打開(kāi)擴(kuò)展指令
wrtcom_12864(0x03); //允許輸入卷動(dòng)地址
if(flag == 64){flag = 0;}
wrtcom_12864(0x40 + flag); //設(shè)置卷動(dòng)地址
flag++;
wrtcom_12864(0x30); //回到基本指令
wrtcom_12864(addr + i);
delay_12864(20000);
if(*ptdat != ''){
wrtdat_12864(*ptdat++); //寫(xiě)入高字節(jié)
}else{
over++; //寫(xiě)完最后一行字符,需要再卷動(dòng)16次才能顯示出來(lái)。
wrtdat_12864(0x20); //字符串結(jié)束則寫(xiě)入空字符
}
if(*ptdat != ''){
wrtdat_12864(*ptdat++); //寫(xiě)入低字節(jié)
}else{
wrtdat_12864(0x20); //字符串結(jié)束則寫(xiě)入空字符
}
}
if(over< 8){;}
else {break;}
}
} while(1);
} 到這里所有的內(nèi)容都介紹完了。 筆者愚鈍,花了一個(gè)星期的時(shí)間琢磨它,本文就算是給自己的一個(gè)交代吧! |