应用笔记 4039

使用DS89C450作为静态LCD显示屏控制器


摘要 : 很多公司的微控制器集成了LCD显示屏控制器,它采用硬件实现。有的微控制器,例如DS89C450,不提供这一功能,但是可以通过软件实现一个简单的显示控制器。本应用笔记阐述怎样利用DS89C450超高速快闪微控制器来驱动7段数位静态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的详细信息,请参考以下文档:
  • LCD显示板工作需要多少段和多少公共驱动器? 控制静态LCD显示板时,被驱动的每一段需要一条驱动线(端口引脚),公共(COM)背板线需要额外的一个端口引脚。
对于本应用笔记,选用了Lumex® LCD-S401C52TR显示屏。该LCD是5V静态显示屏,含有4个7段数字和3个符号段(一个逗号和三个十进制小数点)。LCD上的每位数都由7段构成,如图1所示,A、B、G、E和D段被打开,显示数字"2"。

图1. 7段LCD显示数字
图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. 静态LCD段的交替驱动波形

如图2所示,静态显示屏的COM引脚一直被一个50%占空比的方波驱动,该方波电平在VLCD (我们的设置是5V)和GND之间。利用两个模式之一来驱动每一条段线。
  • 要把段打到OFF,应采用和我们驱动COM引脚一样的波形来驱动它。这可以保证SEG/COM对上的直流电压始终为零,意味着段将保持关断。
  • 要把段打到ON,应采用和COM波形相反的信号来驱动它。这意味着,一半的时间以正电压驱动段,另一半的时间以负电压驱动它。这两个状态有相同的视觉显示,因此,段看起来一直接通。由于电压差的平均直流值是零,不会有导致损害LCD玻璃的静态直流偏置。
不同的LCD显示板有不同的LCD驱动频率(称之为帧频率)。实际应用合适的频率值取决于特定硬件设置的试验情况。由于LCD段状态改变率受限于段的总电容,LCD只在特定范围的帧频率内才能正常工作。一般而言,这一范围在20Hz至200Hz之间。本应用笔记的实例代码使LCD大概运行在30Hz左右。特定显示屏的帧速率太高或者太低都会导致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

结论

利用微控制器的大量专用数字外设,可以根据需要在软件中实现静态或者多路复用LCD显示屏控制器。简单的静态显示使这一实现非常直观。DS89C450等8051微控制器的标准通用I/O功能可以用于驱动LCD上的SEG和COM波形。使用高性能DS89C450,即使在软件中实现LCD控制器,也可以保证主应用程序有足够的处理能力。
下一步
EE-Mail 订阅EE-Mail,接收关于您感兴趣的新文档的自动通知。
© , Maxim Integrated Products, Inc.
The content on this webpage is protected by copyright laws of the United States and of foreign countries. For requests to copy this content, contact us.
APP 4039:
应用笔记 4039,AN4039, AN 4039, APP4039, Appnote4039, Appnote 4039