摘要:很多公司的微控制器集成了LCD顯示屏控制器,它采用硬件實現。有的微控制器,例如DS89C450,不提供這一功能,但是可以通過軟件實現一個簡單的顯示控制器。本應用筆記闡述怎樣利用DS89C450超高速快閃微控制器來驅動7段數位靜態LCD顯示板。
本應用筆記闡述怎樣利用DS89C450超高速快閃微控制器來實現一個簡單的7段數位靜態LCD顯示控制器。由于沒有采用DS89C450的特殊功能,該實例代碼可以很容易導出到任何8051兼容微控制器中,只要微控制器有足夠的引腳來驅動本應用中所采用的LCD顯示板。
可以下載(ASM)本應用筆記的實例代碼。
圖1. 7段LCD顯示數字
LCD-S401C52TR顯示屏含有一個COM背板(連接至兩個引腳)和32個顯示段,每個都連接至段驅動引腳。在這個例子中,我們只使用7段數字中的三個,意味著DS89C450需要驅動21條SEG線(三個數位中的每一個需要7段)和一條COM線,從而共需要22個端口引腳。當沒有采用擴展存儲器總線配置工作時,DS89C450提供24個推拉端口引腳。因此,微控制器有足夠的I/O容量來完成這一任務(端口0還有其他的8個端口引腳。但是,三個引腳開漏,需要額外的上拉電阻才能用作通用I/O)。
LCD-S401C52TR顯示屏上的段和公共線通過靠近原型區的J4插頭連接至DS89C450的端口引腳。段行通過1kΩ電阻連接至端口引腳,而沒有直接和端口引腳連接。之所以采用這種設置是因為DS89C450的端口引腳比LCD顯示板驅動線常用的方式有更強的驅動能力(0態和單穩態強下拉,1態強上拉,然后弱上拉)。由于COM線有較大的電容,需要較強的驅動,它直接連接到其端口引腳。但是,本應用不建議段行直接由端口引腳驅動。這種配置會出現一個問題:隨著越來越多的段打開,通過LCD顯示屏,在段和公共面之間的電容耦合會使COM線偏離其預置狀態(之所以出現這一問題,是因為工作段對公共面總是保持正電壓)。結果,應該關掉的段會被部分打開。所以通過電阻連接端口引腳,以減小驅動能力,避免這一問題的發生。
表1. LCD顯示板和端口引腳連接
還需要對硬件設置進行一些說明:
電壓差的極性并不影響驅動LCD段。例如,驅動3V閾值電壓LCD的控制器可以通過設置COM至地,SEGn至3V來接通段n,也可以設置COM至3V,SEGn至地達到同樣目的。這一事實非常重要,因為如果LCD上的靜態直流電壓保持時間過長,段可能會被損害,無法再正常開關。為避免這一問題,不論段處于ON還是OFF狀態,總是以交替波形來驅動LCD段,以確保每個段上總的直流電壓保持為零(圖2)。
圖2. 靜態LCD段的交替驅動波形
如圖2所示,靜態顯示屏的COM引腳一直被一個50%占空比的方波驅動,該方波電平在VLCD (我們的設置是5V)和GND之間。利用兩個模式之一來驅動每一條段線。
下面是驅動LCD段運行的主程序。
簡介
液晶顯示屏(LCD)廣泛用于各種現代電子設備上,例如計算器、手持式血糖儀、氣站泵和電視機等。LCD功耗低,直視時有清晰的顯示,在很多應用中已經替代了老的LED顯示屏。很多微控制器(例如MAXQ2000)集成了能夠驅動LCD顯示板的LCD控制器,其占空比高達?。但是在某些情況下,具體應用中理想的微控制器不一定集成LCD控制器。在這些情況下,可以使用微控制器的端口引腳來驅動顯示器,在軟件中實現顯示控制器。本應用筆記闡述怎樣利用DS89C450超高速快閃微控制器來實現一個簡單的7段數位靜態LCD顯示控制器。由于沒有采用DS89C450的特殊功能,該實例代碼可以很容易導出到任何8051兼容微控制器中,只要微控制器有足夠的引腳來驅動本應用中所采用的LCD顯示板。
可以下載(ASM)本應用筆記的實例代碼。
選擇一款LCD顯示板
在應用中選擇LCD顯示板時,應該為LCD仔細挑選匹配的兼容微控制器和LCD顯示控制器。進行選擇時,應考慮以下問題。- LCD的工作電壓范圍是多少? 由于DS89C450是5V微控制器,其端口引腳工作在5V電平上,我們必須選擇一款5V LCD顯示板。注意,很多集成了LCD控制器的微控制器使用專用供電輸入(VLCD)來設置LCD控制器使用的電壓范圍。
- LCD的占空比是多少? 靜態LCD顯示板將顯示屏中的每一段連接至專用驅動線。這意味著段驅動器的數量必須等于被驅動的LCD段的數量。然而,多路復用LCD顯示板的每一條段驅動線(SEG)要驅動一個以上的LCD段。這些顯示板使用多路公共背板(COM)輸出,根據所采用的占空比,驅動SEG和COM線上VLCD和GND之間的多電平。由于我們使用的8051微控制器DS89C450只能將其端口引腳線驅動到5V和GND,因此,我們的情況限于靜態LCD。關于驅動多路復用LCD的詳細信息,請參考以下文檔:
- 應用筆記3548,"Using an LCD with MAXQ Microntrollers"。
- MAXQ?系列用戶指南:MAXQ2000補充材料(English only)
- LCD顯示板工作需要多少段和多少公共驅動器? 控制靜態LCD顯示板時,被驅動的每一段需要一條驅動線(端口引腳),公共(COM)背板線需要額外的一個端口引腳。
圖1. 7段LCD顯示數字
LCD-S401C52TR顯示屏含有一個COM背板(連接至兩個引腳)和32個顯示段,每個都連接至段驅動引腳。在這個例子中,我們只使用7段數字中的三個,意味著DS89C450需要驅動21條SEG線(三個數位中的每一個需要7段)和一條COM線,從而共需要22個端口引腳。當沒有采用擴展存儲器總線配置工作時,DS89C450提供24個推拉端口引腳。因此,微控制器有足夠的I/O容量來完成這一任務(端口0還有其他的8個端口引腳。但是,三個引腳開漏,需要額外的上拉電阻才能用作通用I/O)。
硬件設置
這一例子的硬件設置基于DS89C450評估(EV)套件(B版),去掉了存儲器接口CPLD (U5)和兩個外部存儲器芯片(U6和U7)。這一改動釋放了多個端口引腳,可供我們的應用程序使用,否則這些引腳可用于實現擴展存儲器總線,特別是端口0 (所有8條線)、端口2 (所有8條線)、端口3.6和3.7。請參見表1 (注意:在這一例子中沒有使用端口0)。DS89C450含有64kB內部代碼空間和1kB內部數據SRAM,對于這一例子已經足夠了。LCD-S401C52TR顯示屏上的段和公共線通過靠近原型區的J4插頭連接至DS89C450的端口引腳。段行通過1kΩ電阻連接至端口引腳,而沒有直接和端口引腳連接。之所以采用這種設置是因為DS89C450的端口引腳比LCD顯示板驅動線常用的方式有更強的驅動能力(0態和單穩態強下拉,1態強上拉,然后弱上拉)。由于COM線有較大的電容,需要較強的驅動,它直接連接到其端口引腳。但是,本應用不建議段行直接由端口引腳驅動。這種配置會出現一個問題:隨著越來越多的段打開,通過LCD顯示屏,在段和公共面之間的電容耦合會使COM線偏離其預置狀態(之所以出現這一問題,是因為工作段對公共面總是保持正電壓)。結果,應該關掉的段會被部分打開。所以通過電阻連接端口引腳,以減小驅動能力,避免這一問題的發生。
表1. LCD顯示板和端口引腳連接
DS89C450 Port Pin | J4 Header Pin | LCD Pin(s) | LCD Signal | Notes |
P1.0 | 1 | 21 | 4A | Through 1kΩ |
P1.1 | 2 | 20 | 4B | Through 1kΩ |
P1.2 | 3 | 19 | 4C | Through 1kΩ |
P1.3 | 4 | 18 | 4D | Through 1kΩ |
P1.4 | 5 | 17 | 4E | Through 1kΩ |
P1.5 | 6 | 22 | 4F | Through 1kΩ |
P1.6 | 7 | 23 | 4G | Through 1kΩ |
P1.7 | 8 | 1, 40 | COM | Connect directly |
P2.0 | 21 | 25 | 3A | Through 1kΩ |
P2.1 | 22 | 24 | 3B | Through 1kΩ |
P2.2 | 23 | 15 | 3C | Through 1kΩ |
P2.3 | 24 | 14 | 3D | Through 1kΩ |
P2.4 | 25 | 13 | 3E | Through 1kΩ |
P2.5 | 26 | 26 | 3F | Through 1kΩ |
P2.6 | 27 | 27 | 3G | Through 1kΩ |
P3.0 | 10 | 30 | 2A | Through 1kΩ |
P3.1 | 11 | 29 | 2B | Through 1kΩ |
P3.2 | 12 | 11 | 2C | Through 1kΩ |
P3.3 | 13 | 10 | 2D | Through 1kΩ |
P3.4 | 14 | 9 | 2E | Through 1kΩ |
P3.5 | 15 | 31 | 2F | Through 1kΩ |
P3.6 | 16 | 32 | 2G | Through 1kΩ |
還需要對硬件設置進行一些說明:
- 采用了一個標準16.384MHz晶振(插在Y1上)來為DS89C450提供時鐘。
- 運行應用程序時,DIP開關SW1.1和SW4.2應在ON位置;所有其他應為OFF。
- 裝入應用程序(使用MAXQ微控制器工具套件(MTK)或者另一開發工具)時,DIP開關SW1.1、SW1.2、SW1.3、SW4.1和SW4.2應在ON位置;所有其他應為OFF。
- 當LCD顯示屏運行時,總是可以在LED條形顯示U10上看到端口1的狀態。這屬于正常,由于LCD顯示屏被緩沖過,因此,不會影響應用程序。
- P3.0和P3.1也被用于串口0的Tx/Rx線。所以,當應用程序裝入(使用串口啟動加載程序)時,由于這些線的活動,LCD的一兩個段會閃爍。這屬于正常。當應用程序運行時,DIP開關SW1.2和SW1.3應關斷,以禁止串口功能。
- LCD顯示屏上任何沒有使用的段必須驅動到OFF狀態,不允許浮空。可以通過將一個或者多個沒有使用的段連接至驅動到OFF狀態(和COM相同的電壓波形)的端口引腳來完成這一任務,也可以將沒有使用的段直接連接至COM。
驅動LCD段
LCD段的默認狀態是OFF (例如,透明);沒有加電壓時,段應該為透明狀態,相對于LCD顯示板背景是看不到的。此外,當相同的電壓加在段線(SEG)和公共背板(COM)上時,段保持關斷。當段的SEG引腳和COM面之間有電壓差時,段才切換到ON (例如,不透明)狀態。當該電壓達到一個特殊電平時,即閾值電壓,段變暗,最終完全不透明。閾值電壓是LCD顯示板指定工作電壓的百分比,不同的LCD有不同的閾值電壓。電壓差的極性并不影響驅動LCD段。例如,驅動3V閾值電壓LCD的控制器可以通過設置COM至地,SEGn至3V來接通段n,也可以設置COM至3V,SEGn至地達到同樣目的。這一事實非常重要,因為如果LCD上的靜態直流電壓保持時間過長,段可能會被損害,無法再正常開關。為避免這一問題,不論段處于ON還是OFF狀態,總是以交替波形來驅動LCD段,以確保每個段上總的直流電壓保持為零(圖2)。
圖2. 靜態LCD段的交替驅動波形
如圖2所示,靜態顯示屏的COM引腳一直被一個50%占空比的方波驅動,該方波電平在VLCD (我們的設置是5V)和GND之間。利用兩個模式之一來驅動每一條段線。
- 要把段打到OFF,應采用和我們驅動COM引腳一樣的波形來驅動它。這可以保證SEG/COM對上的直流電壓始終為零,意味著段將保持關斷。
- 要把段打到ON,應采用和COM波形相反的信號來驅動它。這意味著,一半的時間以正電壓驅動段,另一半的時間以負電壓驅動它。這兩個狀態有相同的視覺顯示,因此,段看起來一直接通。由于電壓差的平均直流值是零,不會有導致損害LCD玻璃的靜態直流偏置。
下面是驅動LCD段運行的主程序。
Main: mov IE, #080h ; Disable timer 0 interrupt temporarily mov R2, DigitP1 ; Grab local copies of digit variables mov R3, DigitP2 mov R4, DigitP3 mov IE, #082h ; Re-enable timer 0 interrupt mov A, R2 call getDigit ; Calculate segment pattern for ones digit anl A, #01111111b ; Ensure that COM (P1.7) is driven low mov P1, A mov A, R3 call getDigit ; Calculate segment pattern for tens digit mov P2, A mov A, R4 call getDigit ; Calculate segment pattern for hundreds digit mov P3, A ;;;; Delay loop ;;;; mov R0, #0FFh L1A: mov R1, #0FFh L1B: djnz R1, L1B djnz R0, L1A ;;;;;;;;;;;;;;;;;;;;;; mov A, R2 call getDigit ; Calculate segment pattern for ones digit cpl A ; Inverse of the pattern driven on the first frame half orl A, #10000000b ; Ensure that COM (P1.7) is driven high mov P1, A mov A, R3 call getDigit ; Calculate segment pattern for tens digit cpl A ; Inverse of the pattern driven on the first frame half mov P2, A mov A, R4 call getDigit ; Calculate segment pattern for hundreds digit cpl A ; Inverse of the pattern driven on the first frame half mov P3, A ;;;; Delay loop ;;;; mov R0, #0FFh L2A: mov R1, #0FFh L2B: djnz R1, L2B djnz R0, L2A ;;;;;;;;;;;;;;;;;;;;;; ljmp Main ; Go back for another frame cycle (endless loop)注意,COM線(連接至P1.7)總是采用相同的波形驅動:前半幀為低電平,后半幀為高電平。對于段線,幀第一部分的模式驅動和第二部分的相反。以同樣方式將三個數位的每一個分別連接至三個端口之一,所以,段A總是連接至Px.0,段B連接至Px.1,依此類推。這種配置使實例代碼能夠使用getDigit子程序來計算三個LCD顯示板數位中每一個的段模式。
;*************************************************************************** ;* ;* getDigit ;* ;* Returns an LCD segment pattern (in Acc) for the decimal digit (0 to 9) ;* input (also in Acc) ;* getDigit: cjne A, #0, getDigit_not0 ; xgfedcba mov A, #00111111b ; Zero ret getDigit_not0: cjne A, #1, getDigit_not1 ; xgfedcba mov A, #00000110b ; One ret getDigit_not1: cjne A, #2, getDigit_not2 ; xgfedcba mov A, #01011011b ; Two ret getDigit_not2: cjne A, #3, getDigit_not3 ; xgfedcba mov A, #01001111b ; Three ret getDigit_not3: cjne A, #4, getDigit_not4 ; xgfedcba mov A, #01100110b ; Four ret getDigit_not4: cjne A, #5, getDigit_not5 ; xgfedcba mov A, #01101101b ; Five ret getDigit_not5: cjne A, #6, getDigit_not6 ; xgfedcba mov A, #01111101b ; Six ret getDigit_not6: cjne A, #7, getDigit_not7 ; xgfedcba mov A, #00000111b ; Seven ret getDigit_not7: cjne A, #8, getDigit_not8 ; xgfedcba mov A, #01111111b ; Eight ret getDigit_not8: cjne A, #9, getDigit_not9 ; xgfedcba mov A, #01101111b ; Nine ret getDigit_not9: mov A, #0 ret
運行計數器
實例代碼中LCD上顯示的模式是3位十進制計數器,上電時從000開始,遞增至001,002,直到999,然后翻轉。由于程序的主循環驅動LCD段和公共模式,我們必須找到另一方法來周期性地遞增計數器值。我們的方案是使用定時器0來周期性地觸發中斷。mov TMOD, #021h ; Timer 1: 8-bit autoreload from TH1 ; Timer 0: 16-bit mov TCON, #050h ; Enable timers 0 and 1 mov CKMOD, #038h ; Use system clock for all timer inputs mov IE, #082h ; Enable timer 0 interrupt每次發生定時器中斷時,寄存器中的延時計數器被遞減。當延時計數器達到零時,LCD 3位計數器值遞增1 (根據需要,每一數位翻轉);延時計數器初始化到其最大值。由于定時器0寬度是16位,實例代碼將延時計數器設置為20,3位計數器大概每(1/16.384MHz) × (216) × 20 = 0.08s ,即每秒12次,遞增一次。
org 000Bh ; Timer 0 interrupt ljmp IntTimer0 ;*************************************************************************** ;* ;* IntTimer0 (INTT0) ;* ;* Timer interrupt service routine ;* IntTimer0: push ACC ; Save off accumulator and R0 push R00 mov R0, Count ; Only increment LCD digits every [CountMax] ; interrupt cycles djnz R0, INTT0_Done inc DigitP1 ; Increment ones digit on display mov A, DigitP1 cjne A, #10, INTT0_Continue ; Check for rollover mov DigitP1, #0 inc DigitP2 ; Increment tens digit on display mov A, DigitP2 cjne A, #10, INTT0_Continue ; Check for rollover mov DigitP2, #0 inc DigitP3 ; Increment hundreds digit on display mov A, DigitP3 cjne A, #10, INTT0_Continue ; Check for rollover mov DigitP3, #0 INTT0_Continue: mov R0, CountMax ; Reset to starting cycle count INTT0_Done: mov Count, R0 ; Update cycle counter pop R00 pop ACC ; Restore accumulator and R0 reti
評論
查看更多