LCD1602可以說是大多數單片機工程師了解液晶顯示的入門級模組,其顯示原理與其它很多顯示模組也是相似的,今天我們就來詳細討論一下它的指令集。所謂模組的指令集,本質上是該模組使用的控制芯片的指令集,本文主要討論基於HD44780芯片的LCD1602。
其實在博客園的博文中,我已經整理了一篇關於LCD1602的文章(原作者已不可考),從技術角度來講,這篇文章寫得還是不錯的,該討論的地方都涉及到了,對LCD1602顯示有興趣的讀者可以去閱讀一下。但是大家可能都知道,曾幾何時,某人曾經對兄弟誇了海口:要說寫技術文章,我誰也不服(此劇情純屬虛構,如有雷同,實屬巧合,哈哈~~)!所以我決定百尺竿頭更進一步,使用另一種方式來探討它們,Let's Go Go Go, Fire in Hole,不好意思,走神了。
為了本文的完整性,我們簡單介紹一下HD44780中DDRAM的組織方式,如下圖所示:
DDRAM(Display DataRAM)即“顯示數據隨機存取存儲器”,一般我們簡稱為“顯存”,簡單的說,你往顯存中寫入什么,屏幕上就會顯示什么。顯示位置(Display position)表示DDRAM地址對應的屏幕位置。例如,顯示位置1(屏幕左上角)對應DDRAM地址0x00(當然,這只是默認情況下)。
HD44780包含80個顯存地址(第一行0x00~0x27、第二行0x40~0x67),也就意味可以顯示最多80個字符。但是,我們使用的LCD1602只能顯示2行,每行16個字符。換句話說,默認情況下,它只使用到了32個DDRAM地址(第一行0x00~0xF、第二行0x40~0x4F),相應的DDRAM地址與屏幕對應關系如下圖所示(注意:兩行DDRAM地址不是連續的)。
例如,你想在LCD左上角顯示內容,就應該把數據寫入到DDRAM地址0x00中,如果想在LCD右下角顯示內容,應該把相應的數據寫入到DDRAM地址0x4F中。總之,液晶顯示屏上的內容與DDRAM地址是一一對應的。
當然,我們往DDRAM中寫入數據是字模的地址(而不是字模本身),具體屏幕上顯示什么取決於字模地址中對應的字模。HD44780芯片已經預定義了一些字模,它們保存在一個稱為CGROM(Character Generator ROM,字符生成只讀存儲器)的地方,簡單的說,CGROM就是一個字庫,相應的字模如下圖所示(當然,還有一個CGRAM,它允許用戶自定義字模,后續有機會再討論)。
使用CGROM中預定義的字模非常簡單,只需要把字模對應的地址寫入到DDRAM即可。例如,你要顯示小寫字母“j”,我們就應該寫入0b0110_1010(0x6A),其中0110表示高4位地址(Upper 4 Bits),1010表示低4位地址(Lower 4 Bits)。
好的,我們已經知道如何確定字模地址,那怎么來定位DDRAM地址呢?總不能亂寫一通吧!HD44780內部有一個地址計數器(AddressCount, AC),它保存的就是DDRAM(或CGRAM)的地址,我們通過指令就可以控制具體訪問的DDRAM地址。
咱們來看看HD44780數據手冊定義的指令集,后續的單條指令截圖都可以從這里查到,如下圖所示:
首先我們來看看“功能設置(Function Set)”指令,它用來設置與硬件相關的配置,功能設置不當可能會導致數據傳輸或顯示不正常,所以“功能設置”指令通常也是發送給LCD1602的第一條指令,其中包含DL、N、F三個配置位,如下圖所示:
DL位(Data Length)選擇數據總線的寬度為4位(DL=0)還是8位(DL=1),一般我們常用的還是8位總線。
N位(Number)表示一行(N=0)還是兩行(N=1)顯示(它們對應的DDRAM地址是不一樣的,有興趣可以參考數據手冊查閱一行顯示的DDRAM組織方式),LCD1602當然應該設置為2行顯示。
F位(Font)表示顯示的字體為5x8點陣(F=0)還是5x10(F=1),兩行顯示下只能顯示5x8點陣,下圖左側給出了兩種字體的區別(包含光標位置,右側是光標閃爍時的狀態轉換)
我們決定使用8位數據總線給LCD1602發送數據,相應的指令應該為“0b11_1000(0x38)”。
然后我們可以使用顯示開關控制(Display on/offcontrol)指令,之所以緊趕慢趕地先討論它,是因為只要正確使用該指令,就可以打開顯示並開啟(閃爍)光標。在實際硬件調試時,能打開光標就意味着硬件的連接正常,且單片機往模組中寫入的數據被正常接收(即寫數據的時序正確),至此顯示模組的驅動過程基本上已經完成了一半,接下來就是逐個指令調試的過程了。
顯示開關控制指令可以設置D、C、B三位,HD44780數據手冊對該指令的詳細描述如下:
D位(Display)是顯示開關控制位,當D=1時顯示開啟,當D=0時顯示關閉。當然,該位狀態並不影響DDRAM中已經寫入的數據,如果你反復開啟與關閉顯示,DDRAM中的內容仍然是不變的,因為本質上它只是控制液晶屏驅動時序是否生成。
C位(Cursor)表示是否顯示光標,當C=1時顯示,當C=0時不顯示光標。B位(Blink)表示光標是否閃爍,當B=1時表示閃爍,當B=0時不閃爍。
假設我們需要打開顯示,且開啟光標閃爍,需要給HD44780的指令是什么呢?我們把顯示開關控制(Display on/off control)指令重新剪貼如下:
打開顯示需要設置D=1,顯示光標需要設置C=1,而讓光標閃爍需要設置B=1,也就是說,我們需要給HD44780發送指令“0xF”。我們來看看VisualCom軟件平台相應的仿真效果,如下圖所示
VisualCom軟件平台中“內存”與“寄存器”窗口中的數據取決於具體的調入元器件類型,一般的顯示控制芯片(這里是HD44780)都會有,而像LED、數碼管、按鍵之類器件則無。VisualCom軟件平台有一個非常實用的功能,就是預置數據,它可以給當前選擇的器件預置一些數據或命令,當你運行仿真之后,器件會先執行預置數據。
我們先調入“LCD1602”仿真器件,右擊后在彈出的快捷菜單中選擇“預置數據”項即可打開如下圖所示對話框。
對話框中有一個表格,你可以插入想要的數據行,“類型”欄中可以是數據或命令;“附加欄”是擴展項,對於HD44780是無效的;“十進制”與“十六進制”表示插入的具體數據或命令,兩欄的數據完全一樣,你往十進制寫入15,十六進制中就會顯示F,反之亦然,這可以方便不同的進制數據輸入的場合;“自定義備注”欄可以為你插入的行做注釋。
為了插入數據或命令行,你可以選擇左下角的“插入數據”或“插入命令”按鈕插入數據行,它們會在表格的結尾插入,你也可以右擊后選擇相應的選項,它可以在你點擊的某行上方插入一行。當然,也可以對數據行進行移動、刪除等操作,此處不再贅述
我們先插入一條“功能設置指令”(0x38),再插入了一條“顯示開關控制指令”(0xF),然后點擊“單步運行”按鈕,就可以看到液晶顯示模組的左上角出現了一個閃爍的光標。同時我們注意到,“寄存器窗口”中的B、C、D位均為1。光標的位置由地址計數器(AC)決定,由於AC=0,所以光標出現在左上角。
執行“0xF”指令后還顯示了兩行字符“join weixin mp dzzzzcn”,我們可以查看“內存窗口”中對應的字模地址。例如,0x01地址中數據為0x6A,也就是小寫字母“j”的字模地址,其它依此類推。我們買到的真實LCD1602模組當然不會顯示這兩行字,然而即便如此,我們通常也會習慣使用一條清屏指令(Clear Display)清除屏幕的顯示內容,因為上電之后DDRAM中的數據可能是亂碼,相應的指令如下所示:
清屏,顧名思義,一旦執行該指令,屏幕上不會有任何內容顯示。實際上,清屏指令把所有DDRAM地址中的數據都設置為0x20。
有些人可能會想:為什么不全部設置為0x00呢?因為前面已經提過,DDRAM中的數據代表的是字模地址,而不是字模本身。我們看看前面的CGROM字模表,0x20(0b0010_0000)恰好對應一個空白的字模(實際上,0x20就是空格的ASCII碼),這樣也就可以清除屏幕顯示。
我們在之前的預置數據后再添加了一條指令“0x1”,單步運行后的效果如下圖所示:
可以看到,“內存窗口”中的有DDRAM地址的數據全部都修改為0x20,但是原來光標狀態還是不變的,該顯示的顯示,該閃爍的還是閃爍,因為光標不屬於顯示內容,只不過其位置總會回到左上角(如果原來沒在左上角的話),數據手冊對此已經明確說明,如下圖所示:
這段說明中還提到了I/D位(Increment/Decrement)與S位(Shift),這就要涉及到另一條“進入模式設置(Entry mode set)”,如下圖所示。
前面我們不是說過,想往哪個DDRAM地址寫數據就需要先設置AC嗎?現在我們要寫一行16個字符,如果每寫一個字符都要使用指令設置一下DDRAM地址,是不是很麻煩?HD44780允許我們往(從)DDRAM地址寫(或讀)數據后,將AC自動加1(I/D=1)或減1(I/D=0),這樣如果你要寫一行字符,只需要確定首地址就可以了,是不是很方便?由於光標的位置取決於AC,如果光標顯示已經打開,當你往DDRAM連續寫入字符時,光標位置也會隨之移動。
現在我們要從左上角開始寫入字符串“dzzzzcn”,首先從CGROM字模表中找到小寫字母“d”、“z”、“c”、“n”對應的字模地址分別為“0x64”、“0x7A”、“0x63”、“0x6E”、,然后在VisualCom軟件中再接着添加7個預置數據,即“0x64”、“0x7A”、“0x7A”、“0x7A”、“0x7A”、“0x63”、“0x6E”,如下圖所示:
運行后的效果如下圖所示:
有人可能會想問:沒看到你使用DDRAM地址設置指令呀?也沒有使用“進入模式設置”指令設置地址增加模式,怎么它就會從左上角(0x00)開始寫起呢?因為我們已經使用的清屏指令,它會把AC清零,並且它還會將I/D位設置為1(地址增加模式)。
S位用來控制移屏功能的開啟與否。移屏是個神馬東西?其實跟卷屏差不多,只不過卷屏通常是顯示內容上下卷動,而HD44780只有兩行,它的移屏方向要么左(I/D=1)要么向右(I/D=0),你想要開啟它,只要將S位設置為1即可,數據手冊說明如下:
注意該段說明中有句話:如果S=1,看起來光標沒有移動但屏幕移動了。這是什么意思呢?我們仿真一下不就知道了,將預置數據修改如下:
我們只是在寫數據前插入了一條“進入模式設置”指令(0x5),它打開了移屏功能,並且將I/D設置為0。也就是說,每往DDRAM寫一個數據,地址會自減1,而且會往右移屏。
我們來看看相應的仿真效果,如下圖所示。
運行結果似乎有點耐人尋味?怎么會是這樣的呢?其實並沒有錯,因為我們設置了地址自減模式,而清屏后的AC=0,那么你再寫一個數據,它減到哪里去了?就跳到第一行DDRAM的尾地址0x27(而不是0xF)。我們前面已經提過,LCD1602只使用了32個DDRAM地址(第一行0x00~0xF、第二行0x40~0x4F),所以在正常情況下,當你往DDRAM地址0x27寫入字模地址時,屏幕上是不會顯示相應的字模,對不對?
但是由於你開啟了右移屏指令,當你寫第一個字母d的時候,AC指向了DDRAM地址0x27,同時往右移了一次屏,所以此時屏幕最左側對應的DDRAM地址就是0x27(第一行)與0x67(第二行),而不再是原來的0x0與0x40,更不是正常情況下LCD1602最右側對應的DDRAM地址0xF與0x4F。
當你全部寫完“dzzzzcn”字符串后,光標所在的位置(AC)就是0x21。也就是說,你寫7個字符的同時也往右移了7次屏。看到沒有,光標原來的位置並沒有改變就是這個意思。
如果我們把地址模式改為自加模式(“進入模式指令”為0x7),寫入同樣的數據會出現什么結果呢?自加模式下為左移屏,每寫一次數據左移一次,所以最后的結果應該是:從顯示屏上來看,相當於什么都沒寫。我們來看看仿真的結果,如下圖所示:
好像跟清屏后的效果一樣呀!但是請注意,現在的AC值為0x7,從“內存窗口”可以看到,DDRAM地址0x00~0x07中還是寫入了“dzzzzcn”的字模,只不過由於寫數據的同時往左移了7次屏,所以看起來好像什么都沒寫一樣。
好的,剩下的指令我們后續有機會再來討論吧,么么噠~~