摘要:很多公司的微控制器集成了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
评论
查看更多