晨輝教你輕松學(xué)51--------液晶篇(1602)
1602字符液晶在實際的產(chǎn)品中運用的也比較多了,前幾天留意了一下,發(fā)現(xiàn)宿舍門前的自動售水機就是采用的1602液晶進行顯示的。而且對于單片機的學(xué)習(xí)而言,掌握1602的用法是每一個學(xué)習(xí)者必然要經(jīng)歷的過程。在此,我將使用1602過程中遇到的問題以及感受記錄下來,希望能夠給初學(xué)者帶來一點指導(dǎo),少走一點彎路。
所謂1602是指顯示的內(nèi)容為16*2,即可以顯示兩行,每行16個字符。目前市面上字符液晶絕大多數(shù)是基于HD44780液晶芯片的,控制原理是完全相同的,因此基于HD44780寫的控制程序可以很方便地應(yīng)用于市面上大部分的字符型液晶。 ![]() ![]() ![]() 字符型LCD1602通常有14條引腳線或16條引腳線的LCD,多出來的2條線是背光電源線VCC(15腳)和地線GND(16腳),其控制原理與14腳的LCD完全一樣,引腳定義如下表所示: ![]() HD44780內(nèi)置了DDRAM、CGROM和CGRAM。 DDRAM就是顯示數(shù)據(jù)RAM,用來寄存待顯示的字符代碼。共80個字節(jié),其地址和屏幕的對應(yīng)關(guān)系如下表: ![]() 也就是說想要在LCD1602屏幕的第一行第一列顯示一個"A"字,就要向DDRAM的00H地址寫入“A”字的代碼就行了。但具體的寫入是要按LCD模塊的指令格式來進行的,后面我會說到的。那么一行可有40個地址呀?是的,在1602中我們就用前16個就行了。第二行也一樣用前16個地址。對應(yīng)如下: DDRAM地址與顯示位置的對應(yīng)關(guān)系 ![]() (事實上我們往DDRAM里的00H地址處送一個數(shù)據(jù),譬如0x31(數(shù)字1的代碼)并不能顯示1出來。這是一個令初學(xué)者很容易出錯的地方,原因就是如果你要想在DDRAM的00H地址處顯示數(shù)據(jù),則必須將00H加上80H,即80H,若要在DDRAM的01H處顯示數(shù)據(jù),則必須將01H加上80H即81H。依次類推。大家看一下控制指令的的8條:DDRAM地址的設(shè)定,即可以明白是怎么樣的一回事了) 1602液晶模塊內(nèi)部的字符發(fā)生存儲器(CGROM)已經(jīng)存儲了160個不同的點陣字符圖形,如下表所示,這些字符有:阿拉伯?dāng)?shù)字、英文字母的大小寫、常用的符號、和日文假名等,每一個字符都有一個固定的代碼,比如大寫的英文字母“A”的代碼是01000001B(41H),顯示時模塊把地址41H中的點陣字符圖形顯示出來,我們就能看到字母“A” ![]() 上表中的字符代碼與我們PC中的字符代碼是基本一致的。因此我們在向DDRAM寫C51字符代碼程序時甚至可以直接用P1='A'這樣的方法。PC在編譯時就把“A”先轉(zhuǎn)為41H代碼了。 字符代碼0x00~0x0F為用戶自定義的字符圖形RAM(對于5X8點陣的字符,可以存放8組,5X10點陣的字符,存放4組),就是CGRAM了。后面我會詳細(xì)說的。 0x20~0x7F為標(biāo)準(zhǔn)的ASCII碼,0xA0~0xFF為日文字符和希臘文字符,其余字符碼(0x10~0x1F及0x80~0x9F)沒有定義。 那么如何對DDRAM的內(nèi)容和地址進行具體操作呢,下面先說說HD44780的指令集及其設(shè)置說明,請瀏覽該指令集,并找出對DDRAM的內(nèi)容和地址進行操作的指令。共11條指令: 1.清屏指令 ![]() 功能:<1> 清除液晶顯示器,即將DDRAM的內(nèi)容全部填入"空白"的ASCII碼20H; <2> 光標(biāo)歸位,即將光標(biāo)撤回液晶顯示屏的左上方; <3> 將地址計數(shù)器(AC)的值設(shè)為0。 2.光標(biāo)歸位指令 ![]() 功能:<1> 把光標(biāo)撤回到顯示器的左上方; <2> 把地址計數(shù)器(AC)的值設(shè)置為0; <3> 保持DDRAM的內(nèi)容不變 3.進入模式設(shè)置指令 ![]() 功能:設(shè)定每次定入1位數(shù)據(jù)后光標(biāo)的移位方向,并且設(shè)定每次寫入的一個字符是否移動。參數(shù)設(shè)定的情況如下所示: 位名 設(shè)置 I/D 0=寫入新數(shù)據(jù)后光標(biāo)左移 1=寫入新數(shù)據(jù)后光標(biāo)右移 S 0=寫入新數(shù)據(jù)后顯示屏不移動 1=寫入新數(shù)據(jù)后顯示屏整體右移1個字 4.顯示開關(guān)控制指令 ![]() 功能:控制顯示器開/關(guān)、光標(biāo)顯示/關(guān)閉以及光標(biāo)是否閃爍。參數(shù)設(shè)定的情況如下: 位名 設(shè)置 D 0=顯示功能關(guān) 1=顯示功能開 C 0=無光標(biāo) 1=有光標(biāo) B 0=光標(biāo)閃爍 1=光標(biāo)不閃爍 5.設(shè)定顯示屏或光標(biāo)移動方向指令 ![]() S/C R/L 設(shè)定情況 0 0 光標(biāo)左移1格,且AC值減1 0 1 光標(biāo)右移1格,且AC值加1 1 0 顯示器上字符全部左移一格,但光標(biāo)不動 1 1 顯示器上字符全部右移一格,但光標(biāo)不動 6.功能設(shè)定指令 ![]() 位名 設(shè)置 DL 0=數(shù)據(jù)總線為4位 1=數(shù)據(jù)總線為8位 N 0=顯示1行 1=顯示2行 F 0=5×7點陣/每字符 1=5×10點陣/每字符 7.設(shè)定CGRAM地址指令 ![]() 8.設(shè)定DDRAM地址指令 ![]() (注意這里我們送地址的時候應(yīng)該是0x80+Address,這也是前面說到寫地址命令的時候要加上0x80的原因) 9.讀取忙信號或AC地址指令 ![]() 當(dāng)BF=0時,液晶顯示器可以接收單片機送來的數(shù)據(jù)或指令; <2> 讀取地址計數(shù)器(AC)的內(nèi)容。 10.數(shù)據(jù)寫入DDRAM或CGRAM指令一覽 ![]() <2> 將使用者自己設(shè)計的圖形存入CGRAM。 11.從CGRAM或DDRAM讀出數(shù)據(jù)的指令一覽 ![]() 基本操作時序: 讀狀態(tài) 輸入:RS=L,RW=H,E=H 輸出:DB0~DB7=狀態(tài)字 寫指令 輸入:RS=L,RW=L,E=下降沿脈沖,DB0~DB7=指令碼 輸出:無 讀數(shù)據(jù) 輸入:RS=H,RW=H,E=H 輸出:DB0~DB7=數(shù)據(jù) 寫數(shù)據(jù) 輸入:RS=H,RW=L,E=下降沿脈沖,DB0~DB7=數(shù)據(jù) 輸出:無 呵呵,看到這么多的控制指令希望你沒有頭暈。其實這么多的指令剛開始的時候沒有必要全部掌握,隨著學(xué)習(xí)的深入可以再嘗試去用更復(fù)雜的控制指令。下面讓我們一起驅(qū)動1602的液晶吧。下面是我的液晶的連接圖,用的是那種藍底白字的液晶,其實藍底白字和那種綠底黑字的液晶唯一的區(qū)別就是顏色的問題,至于用哪種液晶,就看各位自己的喜好咯。 這就是我做測試用的最小系統(tǒng),單片機是STC89C516,晶振為12M。液晶為藍底白字的那種1602。 ![]() 當(dāng)我們硬件連接錯誤,或者程序錯誤時就會出現(xiàn)下圖這種情況,就是上排顯示16的白色的塊(藍底黑字的液晶則顯示的是16個黑塊)。 ![]() 下面我們來驅(qū)動1602吧在1602的上排顯示“LCD1602 check ok”下排顯示“study up”程序中沒有用到忙檢測,而是用的是延時函數(shù)來替代忙檢測 #include<reg52.h> //包含頭文件,這個嘛,就不用多說了~~ #define uint unsigned int //預(yù)定義一下 #define uchar unsigned char sbit rs=P3^5; //1602的數(shù)據(jù)/指令選擇控制線 sbit rw=P3^6; //1602的讀寫控制線 sbit en=P3^7; //1602的使能控制線 /*P2口接1602的D0~D7,注意不要接錯了順序,我以前可在這上面吃過虧~*/ uchar code table[]="LCD1602 check ok"; //要顯示的內(nèi)容1放入數(shù)組tablel uchar code table1[]="study up"; //要顯示的內(nèi)容2放入數(shù)組table1 void delay(uint n) //延時函數(shù) { uint x,y; for(x=n;x>0;x--) for(y=110;y>0;y--); } void lcd_wcom(uchar com) //1602寫命令函數(shù) { rs=0; //選擇指令寄存器 rw=0; //選擇寫 P2=com; //把命令字送入P2 delay(5); //延時一小會兒,讓1602準(zhǔn)備接收數(shù)據(jù) en=1; //使能線電平變化,命令送入1602的8位數(shù)據(jù)口 en=0; } void lcd_wdat(uchar dat) //1602寫數(shù)據(jù)函數(shù) { rs=1; //選擇數(shù)據(jù)寄存器 rw=0; //選擇寫 P2=dat; //把要顯示的數(shù)據(jù)送入P2 delay(5); //延時一小會兒,讓1602準(zhǔn)備接收數(shù)據(jù) en=1; //使能線電平變化,數(shù)據(jù)送入1602的8位數(shù)據(jù)口 en=0; } void lcd_init() //1602初始化函數(shù) { lcd_wcom(0x38); //8位數(shù)據(jù),雙列,5*7字形 lcd_wcom(0x0c); //開啟顯示屏,關(guān)光標(biāo),光標(biāo)不閃爍 lcd_wcom(0x06); //顯示地址遞增,即寫一個數(shù)據(jù)后,顯示位置右移一位 lcd_wcom(0x01); //清屏 } void main() //主函數(shù) { uchar n,m=0; lcd_init(); //液晶初始化 lcd_wcom(0x80); //顯示地址設(shè)為80H(即00H,)上排第一位 for(m=0;m<16;m++) //將table[]中的數(shù)據(jù)依次寫入1602顯示 { lcd_wdat(table[m]); delay(200); } lcd_wcom(0x80+0x44); //重新設(shè)定顯示地址為0xc4,即下排第5位 for(n=0;n<8;n++) //將table1[]中的數(shù)據(jù)依次寫入1602顯示 { lcd_wdat(table1[n]); delay(200); } while(1); //動態(tài)停機 } 程序?qū)懞煤鬅龑戇M單片機,現(xiàn)在讓我們看看效果吧 ![]() 這就是顯示的效果,你做成功了嗎? 下面讓我們來看看如何顯示一個自定義的字符吧 我們從CGROM表上可以看到,在表的最左邊是一列可以允許用戶自定義的CGRAM,從上往下看著是16個,實際只有8個字節(jié)可用。它的字符碼是00000000-00000111這8個地址,表的下面還有8個字節(jié),但因為這個CGRAM的字符碼規(guī)定0-2位為地址,3位無效,4-7全為零。因此CGRAM的字符碼只有最后三位能用也就是8個字節(jié)了。等效為0000X111,X為無效位,最后三位為000-111共8個。 如果我們要想顯示這8個用戶自定義的字符,操作方法和顯示CGROM的一樣,先設(shè)置DDRAM位置,再向DDRAM寫入字符碼,例如“A”就是41H。現(xiàn)在我們要顯示CGRAM的第一個自定義字符,就向DDRAM寫入00000000B(00H),如果要顯示第8個就寫入00000111(08H),簡單吧! 好!現(xiàn)在我們來看怎么向這八個自定義字符寫入字模。有個設(shè)置CGRAM地址的指令大家還記得嗎?趕快再找出來看看。 ![]() 從這個指令可以看出指令數(shù)據(jù)的高2位已固定是01,只有后面的6位是地址數(shù)據(jù),而這6位中的高3位就表示這八個自定義字符,最后的3位就是字模數(shù)據(jù)的八個地址了。例如第一個自定義字符的字模地址為01000000-01000111八個地址。我們向這8個字節(jié)寫入字模數(shù)據(jù),讓它能顯示出“℃” 地址:01000000 數(shù)據(jù):00010000 圖示:○○○■○○○○ 01000001 00000110 ○○○○○■■○ 01000010 00001001 ○○○○■○○■ 01000011 00001000 ○○○○■○○○ 01000100 00001000 ○○○○■○○○ 01000101 00001001 ○○○○■○○■ 01000110 00000110 ○○○○○■■○ 01000111 00000000 ○○○○○○○○ 下面我們寫一段程序讓這8個自定義字符顯示出一個心的圖案: (由于上面那個顯示程序已經(jīng)有很詳細(xì)的注釋了,因此這個程序只對與上個程序不同的地方寫注釋) #include<reg52.h> #define uint unsigned int #define uchar unsigned char sbit rs=P3^5; sbit rw=P3^6; sbit en=P3^7; uchar code table[]={0x03,0x07,0x0f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x18,0x1E,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x07,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x10,0x18,0x1c,0x1E,0x1E,0x1E,0x1E,0x1E, 0x0f,0x07,0x03,0x01,0x00,0x00,0x00,0x00, 0x1f,0x1f,0x1f,0x1f,0x1f,0x0f,0x07,0x01, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1c,0x18,0x00, 0x1c,0x18,0x10,0x00,0x00,0x00,0x00,0x00};//心圖案 /*uchar code table1[]={0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00};//字符℃ */ void delay(uint n) { uint x,y; for(x=n;x>0;x--) for(y=110;y>0;y--); } void lcd_wcom(uchar com) { rs=0; rw=0; P2=com; delay(5); en=1; en=0; } void lcd_wdat(uchar dat) { rs=1; rw=0; P2=dat; delay(5); en=1; en=0; } void lcd_init() { lcd_wcom(0x38); lcd_wcom(0x0c); lcd_wcom(0x06); lcd_wcom(0x01); } void main() { char m=0; lcd_init(); lcd_wcom(0x40); //設(shè)定CGRAM地址 for(m=0;m<64;m++) //將心型代碼寫入CGRAM中 { lcd_wdat(table[m]); } lcd_wcom(0x85); //設(shè)定上排的顯示位置 for(m=0;m<4;m++) //顯示心型圖案的上半部分 { lcd_wdat(m); } lcd_wcom(0xc5); //將顯示坐標(biāo)轉(zhuǎn)移到下排和上排相對應(yīng)的地方 for(m=4;m<8;m++) //顯示心型圖案的下半部分 { lcd_wdat(m); } while(1); } 讓我們一起來看看顯示的效果吧~~ 在綠底黑字液晶模塊和藍底白字液晶模塊上分別顯示的效果。 ![]() ![]() 下面再為大家展示幾種可能出現(xiàn)的問題 1:通電之后,程序也燒寫進去了,但是1602就是不顯示,只顯示一排黑塊(一般都是在上排8個小黑塊,記得剛開始用1602液晶的時候,被這個整怕了~~),怎么樣,你郁悶了吧,其實出現(xiàn)這種問題的原因無非以下幾種:硬件連線上的錯誤,這種錯誤一般用萬用表仔細(xì)檢查后很容易找出來。第二種情況就是硬件連接上是正確的,那么此時出問題最大的就是程序上了,如果你用的是忙檢測,看一下忙檢測函數(shù)寫對了沒,如果用的是延時函數(shù),那么看看延時的時間是否夠長。再就是看看時序圖,這點很重要的哦。如果硬件和軟件都沒有錯,那么就要考慮1602是否壞了,但是出現(xiàn)這種情況的幾率很小,如果遇到這種情況,你可以考慮去買彩票了~~ 下面這種情況你遇到過嗎?我遇到過了的,搞得我很是郁悶~~ ![]() 我做的實驗是要液晶顯示ABC這三個字母,并且開光標(biāo),光標(biāo)閃爍。大家可以在第一排的最后幾位看到ABC和光標(biāo)都已經(jīng)顯示出來了。但是為什么其它位會顯示這么多8呢?嘿嘿~~郁悶吧。出現(xiàn)這種情況的原因就是在初始化液晶的時候,要把清屏指令放在最后面,否則就會出現(xiàn)上圖這種情況。怎么樣,第一次聽說吧~不過,我不知道其它的液晶是否也有這個問題出現(xiàn),至少我用的這塊就有這種情況,但是我的另一個液晶則沒有這種情況出現(xiàn),不管是在一開始就清屏還是最后清屏。大家注意下就可以了,萬一出現(xiàn)了這種情況,就會處理了~~ ![]() 上面這張圖是用1602作為顯示的溫度電子鐘~~上面的年月日三個字就是用自定義字符的方法顯示的。呵呵,怎么樣~~到此1602的驅(qū)動基本上結(jié)束了,剩下的就靠大家自己去發(fā)揮了。 |
|