摘 要:由于普通LED点阵显示屏动态显示通常采用硬件扫描驱动,这在一些需要特殊显示的场合显得不够灵活。文中提出了一种利用PC机和单片机的通讯来实现显示屏灵活的动态显示和远程监控的设计方法,同时该方法还可以将显示内容在PC机上进行预览。 关键词:LED;动态显示;远程控制;显示预览
1引言 LED 点阵电子显示屏是集微电子技术、计算机技术、信息处理技术于一体的大型显示屏系统。它以其色彩鲜艳,动态范围广,亮度高,寿命长,工作稳定可靠等优点而成为众多显示媒体以及户外作业显示的理想选择。同时也可广泛应用到军事、车站、宾馆、体育、新闻、金融、证券、广告以及交通运输等许多行业。 目前大多数的LED点阵显示系统自带字库。其显示和动态效果(主要是显示内容的滚动)的实现主要依靠硬件扫描驱动,该方法虽然比较方便,但显示只能按照预先的设计进行。而实际上经常会遇到一些特殊要求的动态显示,比如电梯运行中指示箭头的上下移动、某些智能仪表幅值的条形显示、广告中厂家的商标显示等。这时一般的显示系统就很难达到要求。另外,由于受到存储器本身的局限,其特殊字符或图案也往往难以显示,同时显示内容也不能随意更改。本文提出一种利用PC机和单片机控制的LED显示系统通讯方法。该方法可以对显示内容(包括汉字和特殊图符)进行实时控制,从而实现诸如闪动、滚动、打字等多种动态显示效果。该方法同时还可以调节动态显示的速度,同时用户也可以在PC机上进行显示效果的预览,显示内容亦可以即时修改。另外,通过标准的RS232/485 转换模块还可以实现对显示系统的远程控制。2系统硬件设计 本 系统主要的硬件设计是下位机单片机的显示 控制部分。而上位机(PC机)与单片机显示控制部分的接口为标准RS232通讯方式。若需实现远程监控,只需增加RS232/485转换模块即可,该部分已有成熟的电路设计,故不再详细叙述。 具体的LED显示屏控制电路如图1所示。整个电路由单片机89C52、点阵数据存储器6264、列驱动电路ULN2803、行驱动电路TIP122、移位寄存器4094及附属电路组成。该电路所设计的电子屏可显示10个汉字,需要40个8×8 LED点阵模块,可组成16×160的矩形点阵。由于AT89C52仅有8k存储空间,而显示的内容由PC机控制,因此不可能预先把需要显示的内容做成点阵存在单片机中,而只能由PC机即时地把所需显示的点阵数据传给单片机并存入缓冲区6264。 该电路的显示采用逐行扫描方式。工作时,由单片机从缓冲区取出第一行需要显示的20字节点阵数据,再由列点阵数据输入端P1.2口按位依次串行输入至列移位寄存器,其数据输入的顺序与显示内容的顺序相反。然后置行点阵选通端P1.3为1,即置行移位寄存器的D为高电平,STR使能(所有4094的OE 引脚接+5V电平),从而使列移位寄存器中的数据同时并行输出以选通该行。经延时一段时间后再进行下一行点阵数据的显示。需要注意的是,每次只能选通一行数据,即要通过不断的逐行扫描来实现汉字或字符的显示。3显示与控制的设计 在笔者设计的PC机控制多单片机显示系统中,用PC机实现的主要功能包括单片机显示子系统的选择,显示方式选择(包括静态、闪动、滚动、打字等),滚动方向选择(包括上下滚动和左右滚动),动态显示速度调节(即文字闪动频率、滚动速度、打字显示速度等),显示内容输入及显示预览等。单片机一般通过 RS232/485串行接收PC机发出的显示指采用定时器中断方式进行行扫描,每次中断显示一行,定时中断时间为1.25ms,这样整屏的刷新率为 50Hz,因而无闪烁感。
实现动态显示速度调节的方法通常是改变定时器的中断时间,但是当显示速度很慢的时候,该方法容易使整屏的刷新率降低,从而使显示内容出现闪烁。因此,本设计采用一种“软定时”方法,即在程序中命名一变量作为“软定时器”,以用来设定两次动态显示的时间间隔。在对定时中断调用计数时,如果调用次数达到设定值,则改变显示内容。为保证能够正常显示,“软定时器”的设定值必须大于整屏显示周期。由于显示屏每行显示1.25ms,整屏显示周期为20ms,考虑到余量的情况,可将软定时器的设定值定在大于30ms。如此循环计数,即可实现动态显示。“软定时器”的设定值可以通过上位机PC机来改变,这样既可实现 LED动态显示的速度调节,又可保持显示内容的流畅和无闪烁感。3.1单片机动态显示控制 以上提到的静态、闪动、滚动和打字等4种显示方式,实际上是单片机定时中断程序进行行扫描处理的不同方法。下面将分别说明如何实现这4种显示方式。 静态显示只需在定时中断处理程序中从显示缓冲区调入相应的一行显示数据,然后选中该行即可实现该行的显示,如此循环,便可显示整个内容。闪动显示与此类似,不同的是要间隔一个“软定时器”的定时时间,在行扫描时,行移位寄存器的D端打入的全为0,可使得整屏不显示,以确保黑屏时间与显示时间相等,从而实现汉字或图符的闪动显示。 滚动显示要求需要显示的内容每隔一定时间向指定方向(这里以从右向左为例)移动一列,这样显示屏可以显示更多的内容。为此,需要在下次移动显示之前对显示缓冲区的内容进行更改,从而完成相应点阵数据的移位 *** 作。具体 *** 作方法是: 设置一个显示缓冲区(如图2所示),该区应包括两部分:一部分用来保存当前LED显示屏上显示的10个汉字点阵数据;另一部分为点阵数据预装载区,用来保存即将进入LED显示屏的1个汉字的点阵数据。滚动指针始终指向显示屏的最右边原点。当滚动指针移动到需要显示的点阵数据存储区的第1个汉字的首地址时,显示缓冲区LED显示区为空白,而预装载区已保存了第1个待显示汉字的点阵数据。当需要滚动显示时,则可在接下来的扫描周期的每个行扫描中断处理程序中,将对显示缓冲区的相应行点阵数据左移一位,同时更改显示缓冲区的内容。(需要注意的是,要确保该 *** 作能在1.25ms的中断时间内完成。这里89C52采用22MHz晶振,实验证明可以实现该 *** 作)。这样,在一个扫描周期后,整个汉字将左移一列,而显示缓冲区的内容也同时更改。由于预装载区保存了1个汉字点阵数据,即16×16点阵,所以当前显示缓冲区的内容只能移动16列。当下一个滚动到来时,滚动指针将移动到点阵数据存储区的下一个汉字的首地址,并在预装载区存入该汉字的点阵数据。然后重复执行上述 *** 作便可实现滚动显示。特殊字符或图形的显示与此类似,这里不再赘述。
打字显示要求汉字在显示屏上按从左到右的顺序一个个的出现,如同打字的效果。设计时可采用如下方法:首先将LED显示屏对应的显示缓冲区全部清零,即 LED显示空白,然后每间隔一个“软定时器”设定的动态显示时间,显示缓冲区依次加入一个汉字点阵数据并进行扫描显示,这样就可达到打字显示的效果。3.2 PC机控制程序 a.通讯功能的实现 在Windows环境下,实现PC与单片机的通讯可利用Windows的通讯API函数或者利用VC++(或其它语言)的标准通讯函数_inp、_outp来实现。但上述两种方法比较繁琐,而采用ActiveX控件MSComm32来实现则非常方便。该控件用事件的方式简化了对串口 *** 作的编程,并可设置串行通信的数据发送和接收,还可对串口状态及串口通信的信息格式和协议进行设置。其初始化程序如下: 一般情况下,PC要与多个单片机89C51系统进行主从式通讯,为了区分各单片机系统,可以使89C51采用串口工作方式3,即11位异步接收/发送方式,该方式的有效数据为9位,其中第9位为地址/数据信息的标志位,其作用是使从机据此判断发送的数据是否为地址,从而实现多机 *** 作。但现在由于采用的是MSCOMM控件来实现PC机和单片机之间的通讯,这是一种标准的10位串口通信方式,即8位标准数据位和该数据的起始位、停止位各1位。因此二者格式不相符,故很难利用上述方案。因此可考虑将单片机串口设为工作方式1,即改为10位异步接收/发送方式来解决,其通讯流程如下: 首先发通信开始标志,接着发送需要 *** 作的单片机系统地址,然后发送显示工作命令字,该命令包括2个字节,前一字节用于设定显示方式和滚动方向,后一字节则用于设定显示速度。再往下是传送显示内容的点阵数据,最后对数据进行校验。该通讯规约非常简便,能够较好的解决上述问题,从而实现PC机与多单片机之间的主从式通讯及对显示的控制。 需要注意的是,当显示内容需要改变时,为了避免在单片机串行中断接收数据时,显示屏出现乱码,应使显示屏暂不显示(处于“黑屏”状态),直到数据接收完全,串行中断处理结束时再显示。 汉字字模的提取非常关键,本文的字模数据取自UCDOS下的字库文件HZK16。关于这方面的介绍较多,文献〔2〕给出了较为具体的在VC下提取汉字字模的方案,这里不再赘述。对于特殊字符或图形点阵数据的提取,简便的方法可以先做一个BMP文件,然后用一些取模软件(如字模提取v2.1)来获得。为了显示方便,点阵数据的格式应为n×(16×8),不足要求的则应以0数据补充。 b.动态效果模拟显示 为了方便调节LED的显示效果,笔者在PC机的控制界面上设计了LED显示屏的模拟显示,它同实际的显示效果完全一样。用户可以设定显示的模式,并调节显示速度,然后在界面上对显示效果进行预览,同时还可以随时修改和设定参数,因而十分方便简捷。 为此,可先在界面上描绘出虚拟的LED显示屏,由于实际的显示屏为160×16点阵,故须在界面 上设定相同的区域。 实现动态显示效果的方法和以上几种基本类似,这里以滚动显示为例作一说明。对于需要滚动的文字,可以将其设置为位图格式,暂存于内存中,然后利用VC 提供的位图拷贝函数BitBlt将位图复制到显示位置。对于特殊字符或图形,则可以直接利用BitBlt函数调用到显示位置。然后在类CLEDDlg的 OnTimer函数中调用该函数,以实现文字的滚动显示。另外,也可以通过设定不同的响应时间间隔来改变文字的滚动速度。
汉字显示屏广泛应用与汽车报站器,广告屏等。本文介绍一种实用的汉字显示屏的制作,考虑到电路元件的易购性,没有使用88的点阵发光管模块, 而是直接使用了256个高量度发光管,组成了16行16列的发光点阵。同时为了降低制作难度, 仅作了一个字的轮流显示,实际使用时可根据这个原理自行扩充显示的字数。
1汉字显示的原理:
我们以UCDOS中文宋体字库为例,每一个字由16行16列的点阵组成显示。即国标汉字库中的每一个字均由256点阵来表示。我们可以把每一个点理解为一个像素,而把每一个字的字形理解为一幅图像。事实上这个汉字屏不仅可以显示汉字, 也可以显示在256像素 范围内的任何图形。
用8位的AT89C51单片机控制, 由于单片机的总线为8位,一个字需要拆分为2个部分。
软件打开后输入汉字,点“检取”,十六进制数据的汉字代码即可自动生成,把我们所需要的竖排数据复制到我们的程序中即可。
我们把行列总线接在单片机的i0口,然后把上面分析到的扫描代码送入总线, 就可以得到显示的汉字了。 在这个例子里,由于一共用到16行,16列, 如果将其全部接入89c51
单片机, 一共使用32条io口,这样造成了io资源的耗尽,系统也再无扩充的余地。 实际应用中我们使用4-16线译码器74ls154来完成列方向的显示。 而行方向16条线则接在
p0口和p2口。
程序清单:
ORG 00H
LOOP: MOV A,#0FFH ;开机初始化,清除画面
MOV P0,A ;清除P0口
ANL P2,#00 ;清除P2口
MOV R2,#200
D100MS: MOV R3,#250 ;延时100毫秒
DJNZ R3,$
DJNZ R2,D100MS
MOV 20H,#00H ;取码指针的初值
l100: MOV R1,#100 ;每个字的停留时间
L16: MOV R6,#16 ;每个字16个码
MOV R4,#00H ;扫描指针清零
MOV R0,20H ;取码指针存入R0
L3: MOV A,R4 ;扫描指针存入A
MOV P1,A ;扫描输出
INC R4 ;扫描指针加1,扫描下一个
MOV A,R0 ; 取码指针存入A
MOV DPTR,#TABLE ;取数据表的上半部分的代码
MOVC A,@A+DPTR
MOV P0,A ; 输出到P0
INC R0 ;取码指针加1,取下一个码。
MOV A,R0
MOV DPTR,#TABLE ;取数据表下半部份的代码
MOVC A,@A+DPTR
MOV P2,A ;输出到P2口
INC R0
MOV R3,#02 ;扫描1毫秒
DELAY2: MOV R5,#248 ;
DJNZ R5,$
DJNZ R3,DELAY2
MOV A,#00H ;清除屏幕
MOV P0,A
ANL P2,#00H
DJNZ R6,L3 ;一个字16个码是否完成?
DJNZ R1,L16 ;每个字的停留时间是否到了?
MOV 20H,R0 ;取码指针存入20H
CJNE R0,#0FFH,L100 ;8个字256个码是否完成?
JMP LOOP ;反复循环
TABLE :
;汉字“倚”的代码
db 01H,00H,02H,00H,04H,00H,1FH,0FFH
db 0E2H,00H,22H,00H,22H,0FCH,26H,88H
db 2AH,88H,0F2H,88H,2AH,0FAH,26H,01H
db 63H,0FEH,26H,00H,02H,00H,00H,00H
;以下分别输入天,一,出, 宝,刀,屠,龙,的代码,略。
end
电路中行方向由p0口和p2口完成扫描,由于p0口没有上拉电阻,因此接一个47k8的排阻上拉。 如没有排阻,也可用8个普通的47k 1/8w电阻。为提供负载能力,接16个2n5551的NPN三极管驱动。
列方向则由4—16译码器74LS154完成扫描,它由89C51的P10---P13控制。同样,驱动部分则是16个2N5401的三极管完成的。
电路的供电为一片LM7805三端稳压器,耗电电流为100Ma左右。
采用一块1220cm的万能电路板,应当选用质量好些的发光管,(否则有坏点现象, 更换起来较麻烦)首先将256个发光管插入电路板,注意插入方向,同时使高度一致,行方向直接焊接起来, 列方向则搭桥架空焊接,完成后用万用表测试一下如有不亮的更换掉。
然后找一个电脑硬盘的数据线, 截取所需的长度,分别将行,列线引出至电路的相关管脚即可。原理图为了简洁,故只画出了示意图,行列方向只画出了2个三极管,屏幕只画出4个发光管, 实际上发光管为256只,三极管行列方向各16只,一共32只。焊接过程认真仔细一天时间即可完成全部制作。将程序编译后烧写入89c51, 插入40pin Ic座,即可看到屏幕轮流显示:“倚天一出宝刀屠龙”。
当然,你可将程序的汉字代码部分更换为您所需要的代码即可显示你所需要的汉字
元件清单:
名称 数量 规格
4.7k 1/8w 32 电阻
47k8排阻 1
2n5551 16 小功率NPN三极管
2n5401 16 小功率PNP三极管
led 256 3mm白发红高亮度
22P 2 瓷片电容
10uf/50v 1 电解电容
100uf/25v 2 电解电容
AT89C51 1 或AT89S51
40pin Ic座 1 插89c51用
12M 1 晶体
74LS154 1 或74HC154
LM7805 1 稳压IC
电源插座 1
稳压电源 1
include <c8051f330h>
#include <stdioh>
#include <mathh>
#include <Intrinsh>
#include <absacch>
#define SPEED 2000
#define TIME 1800
#define uint unsigned int
#define uchar unsigned char
#define BLKN 2 //用于点阵列扫描的595的个数
#define TOTAL 26 //待显示的字数
#define CONIO P1
uchar number;//d=0,
uchar control=0,choose,send;
uchar receive_number=3,send_number=0;
bit BUSY;
bit SMB_RW;
uchar hou=0;
uchar min=0;
uchar sec=0;
uchar sg;//时高位
uchar sd;//时低位
uchar fg;//分高位
uchar fd;//分低位
uchar mg;//秒高位
uchar md;//秒低位
sbit CLK=P1^7; //输出脉冲
sbit scyx=P1^6; //复位
sbit led=P0^7;
sbit pcf8563_scl=P0^5;//时钟频率
sbit pcf8563_sda=P0^4;//串行数据传输脚
uchar idata dispram[(BLKN/2)32]={0};
uchar receive1[3]={0x00,0x00,0x00};//时、分、秒
uchar send1[4]={0x02,0x33,0x25,0x03};//时、分、秒、器件地址
/字模表/
uchar code Bmp[][32]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF0,0x1C,0x18,0x38,0x1C,0x38,0x0E,0x38,0x0E,
0x78,0x0E,0x38,0x0E,0x38,0x0E,0x38,0x1C,0x1C,0x18,0x07,0xF0,0x00,0x00,0x00,0x00},
/数字0/
{0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xC0,0x0F,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,
0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x01,0xC0,0x0F,0xFC,0x00,0x00,0x00,0x00},
/数字1/
{0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF8,0x18,0x1C,0x38,0x1C,0x18,0x1C,0x00,0x38,
0x00,0x70,0x01,0xC0,0x07,0x00,0x0C,0x06,0x3F,0xFC,0x3F,0xFC,0x00,0x00,0x00,0x00},
/数字2/
{0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x38,0x38,0x38,0x1C,0x00,0x38,0x03,0xF0,
0x03,0xF0,0x00,0x1C,0x00,0x0E,0x38,0x1C,0x38,0x1C,0x0F,0xF0,0x00,0x00,0x00,0x00},
/数字3/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0xF0,0x01,0xF0,0x03,0x70,0x0C,0x70,
0x18,0x70,0x30,0x70,0x3F,0xFE,0x00,0x70,0x00,0x70,0x03,0xFE,0x00,0x00,0x00,0x00},
/数字4/
{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFC,0x18,0x00,0x18,0x00,0x18,0xC0,0x1F,0xF8,
0x18,0x1C,0x00,0x1E,0x10,0x0E,0x38,0x1C,0x38,0x18,0x0F,0xF0,0x00,0x00,0x00,0x00},
/数字5/
{0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF8,0x0C,0x3C,0x18,0x08,0x38,0x00,0x3F,0xF8,
0x7C,0x1C,0x78,0x0E,0x38,0x0E,0x38,0x0E,0x1C,0x1C,0x07,0xF0,0x00,0x00,0x00,0x00},
/数字6/
{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFE,0x3C,0x0C,0x30,0x18,0x00,0x30,0x00,0x60,
0x00,0xC0,0x01,0xC0,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00},
/数字7/
{0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF8,0x38,0x0C,0x30,0x0E,0x3C,0x0C,0x1F,0xF8,
0x0F,0xF0,0x38,0x3C,0x30,0x0E,0x70,0x0E,0x38,0x0C,0x0F,0xF0,0x00,0x00,0x00,0x00},
/数字8/
{0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x38,0x18,0x30,0x0C,0x70,0x0E,0x30,0x0E,
0x38,0x3E,0x1F,0xEE,0x00,0x1C,0x18,0x18,0x3C,0x30,0x1F,0xE0,0x00,0x00,0x00,0x00},
/数字9/
{0x00,0x40,0x40,0x40,0x30,0x40,0x10,0x40,0x00,0x48,0x00,0x7C,0xF0,0x40,0x10,0x40,
0x10,0x40,0x10,0x40,0x10,0x40,0x12,0x40,0x14,0x40,0x18,0x44,0x17,0xFE,0x00,0x00},//让
{0x00,0x08,0x3F,0xFC,0x21,0x08,0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08,0x21,0x08,
0x21,0x08,0x3F,0xF8,0x20,0x08,0x02,0x00,0x51,0x88,0x50,0x96,0x90,0x12,0x0F,0xF0},//思
{0x10,0x80,0x10,0xA0,0x20,0x94,0x21,0xFE,0x49,0x10,0xFB,0x10,0x15,0xFC,0x21,0x10,
0x41,0x10,0xFD,0xFC,0x01,0x10,0x01,0x10,0x1D,0x14,0xE1,0xFE,0x41,0x00,0x01,0x00},//维
{0x01,0x20,0x41,0x24,0x2F,0xFE,0x21,0x24,0x01,0x24,0x8F,0xFC,0x49,0x24,0x09,0x20,
0x1F,0xFE,0x29,0x22,0xE1,0x22,0x21,0x2A,0x22,0x24,0x22,0x20,0x24,0x20,0x28,0x20},//沸
{0x00,0x40,0x7A,0x48,0x49,0x50,0x4B,0xFC,0x48,0x80,0x7F,0xFE,0x49,0x10,0x4A,0x0E,
0x4D,0xF4,0x78,0x10,0x49,0x14,0x49,0xFE,0x48,0x04,0x4F,0xE4,0x88,0x14,0x18,0x08},//腾
{0x08,0x00,0x08,0x08,0x08,0xFC,0x7E,0x08,0x08,0x08,0x08,0x08,0xFE,0xF8,0x08,0x88,
0x28,0x80,0x2E,0x84,0x28,0x84,0x28,0x7C,0x28,0x00,0x58,0x06,0x8F,0xFC,0x00,0x00},//起
{0x01,0x00,0x01,0x00,0x01,0x08,0x7F,0xFC,0x01,0x00,0x21,0x10,0x19,0x30,0x09,0x44,
0xFF,0xFE,0x03,0x80,0x05,0x40,0x09,0x30,0x31,0x1E,0xC1,0x04,0x01,0x00,0x01,0x00},//来
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0C,0x00,0x1E,0x00,0x1E,0x00,0x0C,0x00,0x04,0x00,0x08,0x00,0x10,0x00,0x00,0x00},//,
{0x00,0x40,0x40,0x40,0x30,0x40,0x10,0x40,0x00,0x48,0x00,0x7C,0xF0,0x40,0x10,0x40,
0x10,0x40,0x10,0x40,0x10,0x40,0x12,0x40,0x14,0x40,0x18,0x44,0x17,0xFE,0x00,0x00},//让
{0x20,0x00,0x22,0x04,0x3F,0x7E,0x48,0x44,0x08,0x44,0xFF,0xC4,0x10,0x44,0x14,0x7C,
0x22,0x44,0x42,0x10,0x9F,0xF8,0x10,0x10,0x1F,0xF0,0x10,0x10,0x1F,0xF0,0x10,0x10},//智
{0x08,0x20,0x7E,0xFC,0x08,0x20,0x3E,0xF8,0x08,0x20,0x7E,0xFC,0x08,0x20,0x3F,0xF8,
0x00,0x08,0xFF,0xFE,0x00,0x08,0x3F,0xF8,0x09,0x00,0x28,0xA8,0x48,0x24,0x07,0xE0},//慧
{0x08,0x08,0x0B,0xFC,0x10,0x00,0x20,0x00,0x40,0x00,0x08,0x04,0x17,0xFE,0x30,0x10,
0x50,0x10,0x90,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x50,0x10,0x20},//行
{0x00,0x40,0x08,0x40,0x7C,0x40,0x00,0x44,0x05,0xFE,0xFE,0x44,0x10,0x44,0x10,0x44,
0x20,0x44,0x24,0x44,0x42,0x84,0xFE,0x84,0x41,0x04,0x01,0x04,0x02,0x28,0x04,0x10},//动
{0x08,0x00,0x08,0x08,0x08,0xFC,0x7E,0x08,0x08,0x08,0x08,0x08,0xFE,0xF8,0x08,0x88,
0x28,0x80,0x2E,0x84,0x28,0x84,0x28,0x7C,0x28,0x00,0x58,0x06,0x8F,0xFC,0x00,0x00},//起
{0x01,0x00,0x01,0x00,0x01,0x08,0x7F,0xFC,0x01,0x00,0x21,0x10,0x19,0x30,0x09,0x44,
0xFF,0xFE,0x03,0x80,0x05,0x40,0x09,0x30,0x31,0x1E,0xC1,0x04,0x01,0x00,0x01,0x00},//来
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3C,0x00,0x66,0x00,0x66,0x00,0x66,0x00,0x3C,0x00,0x00,0x00},//};
uchar code Bmp1[][32]={
{0x01,0x00,0x11,0x00,0x11,0x00,0x11,0x08,0x3F,0xFC,0x21,0x00,0x41,0x00,0x41,0x00,
0x81,0x10,0x3F,0xF8,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x04,0xFF,0xFE,0x00,0x00},
{0x00,0x10,0x1F,0xF8,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x1F,0xF0,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,0x00,0x00},
{0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x88,0x5B,0xFC,0x54,0x88,0x50,0x88,0x90,0x88,
0x10,0x88,0x1F,0xFE,0x10,0x80,0x11,0x40,0x11,0x20,0x12,0x10,0x14,0x0E,0x18,0x04},
{0x00,0x20,0x00,0xF0,0x1F,0x00,0x10,0x00,0x11,0x00,0x11,0x00,0x21,0x04,0x7F,0xFE,
0x01,0x00,0x01,0x00,0x09,0x20,0x09,0x10,0x11,0x08,0x21,0x0C,0x45,0x04,0x02,0x00},
{0x00,0x00,0x38,0x1C,0x44,0x22,0x54,0x2A,0x44,0x22,0x37,0xEC,0x08,0x10,0x10,0x08,
0x2E,0x74,0x20,0x04,0x21,0x84,0x24,0x24,0x13,0xC8,0x08,0x10,0x07,0xE0,0x00,0x00}
};
/字模表/
/系统函数的配置/
void sysclk_int(void) //内部晶振
{
OSCICL=0x83;
OSCICN=0xc3; //不分频
CLKSEL=0x00;
}
/
void delay1(uchar n) //延时程序
{
uchar j;
for(j=0;j<n;j++)
{
_nop_();
_nop_();
_nop_();
_nop_();
}
}
/
void delay(uint i) //毫秒延时
{
register uchar j;//
for(;i;i--)
for(j=0;j<255;j++);
}
void SPI_INT(void) //SPI初始化
{
SPI0CFG=0x40;
SPI0CN=0x01; //0000 0001最后一位是SPI使能位 SPI工作在三线主方式
SPI0CKR=0x0f; //SPI 时钟频率设置为100kHz 0x63
IE &= 0xbf; //关闭SPI中断
}
void SMB_INT(void) //SMB初始化
{
SMB0CF=0x5f; //使能SMB 允许时间扩充 定时器2低字节溢出时钟源
SMB0CF|=0x80;
}
void TIME01_INT(void) //定时器初始化
{
TCON=0x10; //定时器0允许
TMOD=0x01; //定时器0在16位工作方式
CKCON &= 0xf8;
CKCON |= 0x01; //定时器0使用系统4分频
TL0=0x03;
TH0=0x03;
IE|=0x02;
}
void TIME2_INT(void) //定时器2用于配置SMB的时钟频率
{
TMR2CN=0x00;
CKCON |= 0x00;
TMR2RLL = 0xef;
TMR2RLH = 0xff;
TMR2L = 0xef;
TMR2H = 0xff;
TMR2CN |= 0x0c;
}
void TIME3_INT(void)//用于I2C的超时检测
{
TMR3CN = 0x00; //定时器3为16位自动重载
CKCON &= ~0x40;
TMR3RLL = 0xaf;
TMR3RLH = 0x57;
TMR3L = 0xaf;
TMR3H = 0x57;
EIE1|=0x00;
TMR3CN |= 0x04;
}
void pio_int(void) // 端口配置
{
XBR0=0x06;
XBR1=0x40;
P0MDIN=0xff; //禁止模拟输入,0为模拟,1为数字
P0MDOUT=0x0d; //0为开漏,1为推挽(ff)
P0SKIP=0x08;
P1MDIN=0xff;
P1MDOUT=0xff; //低四位用于138
P1SKIP=0x00;
P0=0xff;
P1=0xff;
}
/呈现各种显示效果的函数集/
//void Jump_Word(uchar a) 立即跳显示一个字或一张图案
//void M_Word(uchar a) 卷帘出显示一个字或一张图案
//void M_Words(number) 显示几个交换的字
//void UP_Run(number) 向上滚屏n个字
//void L_Removeout(number) 右入左出n个字
//void M_Black(void) 黑屏
//void R_Removeout(number) 左入右出n个字
//
/立即跳显示一个字或一张图案/
void Jump_Word(uchar a)
{register uchar i,q;
for(i=0;i<32;i++)
{
for(q=0;q<BLKN/2;q++)
{dispram[i+q32]=Bmp[a][i];}
//if(i%2)delay(SPEED);
}
}
/卷帘出显示一个字或一张图案/
void M_Word(uchar a)
{register uchar i,q;
for(i=0;i<32;i++)
{
for(q=0;q<BLKN/2;q++)
{dispram[i+q32]=Bmp1[a][i];}
if(i%2)delay(SPEED);
}
}
/卷帘出显示number个字/
void M_Words(number)
{register uchar i,q,w;
for(w=0;w<number2/BLKN;w++)
{
for(i=0;i<32;i++)
{
for(q=0;q<BLKN/2;q++){dispram[i+q32]=Bmp[q+wBLKN/2][i];}
if(i%2)delay(SPEED);
}
delay(TIME);
}
}
/向上滚屏number个字/
void UP_Run(number)
{register uchar i,j,k,q;
for(i=0;i<number2/BLKN;i++)
{
for(j=0;j<16;j++)
{
for(k=0;k<15;k++)
{
for(q=0;q<BLKN/2;q++)
{
dispram[k2+q32]=dispram[(k+1)2+q32];
dispram[k2+1+q32]=dispram[(k+1)2+1+q32];
}
}
for(q=0;q<BLKN/2;q++)
{
dispram[30+q32]=Bmp[q+iBLKN/2][j2];
dispram[31+q32]=Bmp[q+iBLKN/2][j2+1];
}
delay(2SPEED);
}
delay(2TIME);
}
}
/左移出显示number个字/
void L_Removeout(number)
{register uchar i,j,k,l,q;
for(i=0;i<number;i++)
{
for(j=0;j<2;j++)
for(k=0;k<8;k++)
{
for(l=0;l<16;l++)
{
for(q=0;q<BLKN/2;q++)
{
dispram[l2+q32]=dispram[l2+q32]<<1|dispram[l2+1+q32]>>7;
if(q==BLKN/2-1)
dispram[l2+1+q32]=dispram[l2+1+q32]<<1|Bmp[i][l2+j]>>(7-k);
else
dispram[l2+1+q32]=dispram[l2+1+q32]<<1|dispram[l2+(q+1)32]>>7;
}
}
delay(2SPEED);
}
}
}
/卷帘出黑屏/
void M_Black(void)
{register uchar i,q;
for(i=0;i<32;i++)
{
for(q=0;q<BLKN/2;q++)
{dispram[i+q32]=0x00;}
if(i%2)delay(SPEED);
}
}
/右移出显示number个字/
void R_Removeout(number)
{register uchar i,j,k,l,q;
for(i=0;i<number;i++)
{
for(j=2;j>0;j--)
for(k=0;k<8;k++)
{
for(l=0;l<16;l++)
{
for(q=0;q<BLKN/2;q++)
{
dispram[l2+1+q32]=dispram[l2+1+q32]>>1|dispram[l2+q32]<<7;
if(q==0)
dispram[l2+q32]=dispram[l2+q32]>>1|Bmp[i][l2+j-1]<<(7-k);
else
dispram[l2+q32]=dispram[l2+q32]>>1|dispram[l2+1+(q-1)32]<<7;
}
}
delay(2SPEED);
}
}
}
/用于时钟芯片 *** 作的函数集/
//void write_pcf8563(void)写时钟芯片
//void read_pcf8563(void)读时钟芯片
//
void write_pcf8563(void)
{
SPI0CN &=~0x01; //暂时关掉SPI通讯
send =0xa2; //送子器件地址
while(BUSY);
BUSY=1;
SMB_RW=0;control=5;
STA=1; //开始传送
while(BUSY);
SPI0CN |=0x01; //重新开启SPI
}
void read_pcf8563(void) //读当时的时,分,钞
{
SPI0CN &=~0x01;
send =0xa2; //送子器件地址
while(BUSY);
BUSY=1;
SMB_RW=0;
STA=1;
send=0x02;
while(!SI);
BUSY=1;
send=0xa2;
while(BUSY);
BUSY=1;
SMB_RW=1;
STA=1;
while(BUSY);
BUSY=1;
sec=receive1[2]&0x7f;//读秒
min=receive1[1]&0x7f;//读分
hou=receive1[0]&0x3f;//读时
SPI0CN |=0x01;
}
void spit_time(void)//分别计算时、分、秒的各位数字
{
sg=(int)hou/16;
sd=(int)hou%16;
fg=(int)min/16;
fd=(int)min%16;
mg=(int)sec/16;
md=(int)sec%16;
}
void dat_int(void) //数据初始化
{
scyx=1;
CLK=0;
}
void sysclk_int(void);
void delay(unsigned n);
void pio_int(void);
void SPI_INT(void);
void SMB_INT(void);
void dat_int(void);
void TIME01_INT(void);
void TIME2_INT(void);
void TIME3_INT(void);
void write_pcf8563(void);
void read_pcf8563(void);
void spit_time(void);
/主函数/
void main(void) //主程序
{
register uchar i;
PCA0MD &= ~0x40;// 关闭看门狗
sysclk_int();
OSCICN |= 0x02;
/初始化检测pcf8563/
while(!pcf8563_sda)
{
XBR1 = 0x40;
pcf8563_scl = 0;
for(i = 0; i < 255; i++);
pcf8563_scl = 1;
while(!pcf8563_scl);
for(i = 0; i < 10; i++);
XBR1 = 0x00;
}
/初始化检测pcf8563/
pio_int();
TIME01_INT();
TIME2_INT();
TIME3_INT();
SPI_INT(); //SPI0DAT是SPI的数据寄存
SMB_INT();
spit_time();
EIE1|=0x01;
EA=1;
dat_int();
led=0;
TR0=0;
delay(1000);
write_pcf8563();
while(1)
{
TR0=0;
BUSY=0;
read_pcf8563();
spit_time();
TR0=1;
//Jump_Word(sg);
//Jump_Word(sd);
//Jump_Word(fg);
//Jump_Word(fd);
//Jump_Word(md);
//M_Word(md);
M_Word(4);
delay(10TIME);
M_Words(TOTAL);
UP_Run(TOTAL);
M_Black();
delay(10TIME);
L_Removeout(TOTAL);
delay(3TIME);
M_Black();
delay(10TIME);
R_Removeout(TOTAL);
delay(3TIME);
}//总while循环的结束
}//main函数的结束
void t0(void) interrupt 1 //定时器0的中断服务程序
{
register uchar m,n=BLKN;
TH0 = 0xfc;
TL0 = 0x18;
m = CONIO;
m = (++m)&0x0f;
for(n=0;n<BLKN;n++)
{
SPI0DAT = dispram[30-m2+n];//dispram[((BLKN-)n/2+1)30-m2-(n/2)30+n]
while(!SPIF);
SPIF=0;
}
CONIO &= 0xf0;
CLK = 1;
CONIO |= m;
CLK = 0;
scyx=1;
}
void SMBus_ISR (void) interrupt 7
{
bit FAIL = 0;
if (ARBLOST == 0) //如果赢得总线
{
switch (SMB0CN & 0xF0) //确定中断来源
{
case 0xe0: //主发送方式下产生开始条件
SMB0DAT = send;
SMB0DAT &= 0xFE;
SMB0DAT |= SMB_RW; //对发送的数据进行处理
STA = 0;
break;
case 0xc0: //主方式下发送
if (ACK)
{
if (SMB_RW == 0)
{
if(send_number<=3)
{
if(control==0)
{
SMB0DAT = send;
send_number+=5;
}
else
{
SMB0DAT = send1[send_number];
send_number++;
}
}
else
{
send_number=0;control=0;
STO = 1;
BUSY = 0;
}
}
else {;} //BUSY=0
}
else
{
STO = 1;
STA = 1;
}
break;
case 0x80: //主方式下接收
if (receive_number-1>0) //控制接收3个数据就结束
{
receive1[receive_number-1] = SMB0DAT;
ACK = 1;
receive_number--;
}
else
{
receive1[receive_number-1] = SMB0DAT;
receive_number=3;
BUSY = 0;
ACK = 0;
STO = 1;
}
break;
default:
FAIL = 1;
break;
}
}
else //输掉总线
{
FAIL = 1;
}
if (FAIL) //SMBUS通讯失败后的处理
{
SMB0CF &= ~0x80;
SMB0CF |= 0x80;
STA = 0;
STO = 0;
ACK = 0;
BUSY = 0;
FAIL = 0;
}
SI = 0;
}
void T3_ISR() interrupt 14 //定时器3中断服务程序 用于检测SMBus是否超时
{
SMB0CF &= ~0x80;
SMB0CF |= 0x80;
TMR3CN &=~0x80;
STA = 0;
BUSY = 0;
pcf8563_scl=1;
}
怕有些地方你不知道是怎么来的,所有我就把完整的程序都给你了。你自己取出你所需要的就好了。因为不同的显示效果还是得靠住函数来实现的。
如果住函数没有写好,那么你的移动方式的函数就很难写了的。希望能给楼主一点灵感。呵呵~
我还有几种效果的,但是所有的效果都必须基于我的那种显示方式。你仔细看下,和你的是不是一样的,显示缓存,各种动画效果就是移动的控制方式了
给你借鉴一下,具体的呢还得根据你的硬件来了。
什么是点阵汉字
所谓一个字的点阵 其实就是指这个汉字用多少个象素点来描述 每个象素点显示为什么颜色, 通常情况下, HZK16采用的是1616点阵, 即256个象素点描述一个汉字
这些点的颜色分为两种, 一种是前景色, 一种是显示为背景色
那么,关于那些点显示为前景色, 那些点显示为背景色, 是如何得知的呢
可以这样来考虑, 你在纸上比较正正方方的写一个规则的楷字, 然后在这个字的从上到下,左到右, 分别画十七条直线, 那么这
个字就被放置于一个1616的方格之内, 这样我们就可以很明显的看出, 1616的方格内的具体哪些点有笔划经过, 有笔划经过与没笔化经过的即就是应该被分别填充上前景色与背景色的点
现在,找到了一个汉字的点阵, 那么还须要用数据来记录点阵的信息, 通常情况下, 我们会用32个字节来表示1616点阵的汉字,
即每一行用二个字节来记录十六个象素点的色色彩情况, 0表示背景色, 1表示前景色 16行其须要32个字节点阵汉字的原理同时也决定了它的缺点, 他不具务放大特性, 因为它的显示是基于被定死的点阵, 放大后, 会产生明显的锯齿,非常的难看, 当然, 可以进行一些光滑处理, 但基本上没有多在的改观但点阵汉字简易, 对于复杂汉字, 它比矢量显示汉字法更快带矢量显示是基于记录汉字的笔化的 对于简单的汉字它比较占优势, 容易放大处理 但对于复杂的汉字, 表示起来, 则笔化太多复杂
二 关于字库的建立及其原理
现在讲完了汉字点阵 也说了一个汉字点阵的存放方式, 但具体的点阵如何存放, 读者也应该了解
通常情况下, 一般的DOS下的程序都会提供一个汉字库, 这样在脱离汉字平台(如UCDO)的支持下也可以进行汉字显示, 但是这样会存一个问题, 就是如果每个DOS下的程序员都这么做的话, 就会造成一定的磁盘空间浪费 所以有的DOS下的程序,针对自己所需要的汉字, 就会定制自己的小型字库, 那么字库的制作到底应该如何进行呢 下面我们将就这个问题进行一些基本的讨论
众所周知,一个ASCII字符占一个字节,它的数值从0到255, 那么汉字字符将如何与ASCII字符区别开来呢实际上,仔细观察ASCII字符表,从第161(即0xa1)个字符开始,后面的字符并不经常为E文所使用。充分利用这一特性,将161-255之间的数值空间作为汉字的标识码。既然255-161 = 94不能满足汉字容量的要求,就将每两个字符并在一块(即一个汉字占两个字节),显然,94 94 =8836基本上已经满足了常用汉字个数的要求。
从以上的讨论可以知道, 用二个字节来表示一个汉字, 其原因就是上面说的, 这个就是我们常说的汉字机内码, 一个汉字的机内码是由值都大于0xa1的值组成的
说完机内码, 有的朋友可能就会问题, 机内码与建立汉字字库有什么关系呢
我们常见的标准的汉字字库HZX16(点阵1616),HZK24(2424)两种由上面的讨论我们得知, 一个汉字点阵须要256个象素点阵来表示, 我们采用一个字节的8位来表示八个象素, 其须32个字节; 字库中要存放的是所有常用的汉字的二进制点阵数据, 它的存放是有序的, 下面我们说一下这个顺序:
首先对于"我"字来说, 它的机内码是0xce,0xd2; 机内码每个字节均从0xa1开始, 那么我们已经采用的建立点阵字在库中的索引方法是:
将整个字库里面的汉字是9494的二维数组, 要找任意一个汉字的点阵, 就须要知道这个汉字在这个二维数组当中的X维与Y维
x维 = (机内码字节1-0xa1) & 0x7f;
y维 = (机内码字节2-0xa1) & 0x7f;
求汉字在X,Y维后, 那么按照每个汉字占用32个字节, 则可以得出汉字相对于字库头的偏移是 offset = (x94 + y)32;
其实,X与Y就是汉字的区位码, 汉字的区位码是从0-94的 但实际上只用了16-87
其中一级汉字在16-55二级汉字在56-87是按照一定的规则来确定区位码的对于一级汉字是按拼音首字母级笔划二级汉字是按部首来的我特意生了一个汉字的区痊码,机内码在字库中偏移的文件大家可以下载来看一下 可以知道:
啊-------------区位码(x = 15, y = 0); offset=b040; 机内码:(0xb0,0xa1);
其中,区位码(x=0-14)与(88-94)都是没有对应汉字的字库中实际的对应汉字点阵字数为9472=6768个汉字
实际上, 一个字库中有前1632个字节没有表示具体的汉字的, 在字库里被用来表示什么东西没有什么具体的要求, 如果说你自己要做一个字库那么这一段你可以自己发挥, 填充为一个中文的符号,笑脸,特别文字什么的这些没有具体的要求
同理对于(88---94)32, 你也可以自己发挥 然后告知别人如何使用,因为这个没有标准, 所以一定要有特别的说明,别人才可可以使用
在一般的HZK16当中, 最前1632个节有表示两个大小的"A"及两个感叹号, 一个在圆内的"帅"字大家可以仔细看一下,其它几个没作特别使用
DotMatrixTool(点阵液晶屏取模工具)V1221
语言:简体中文
大小:305MB
类别:系统工具
介绍:DotMatrixToolV1221是一款专业的显示器件取模的一款专用软件本版本,采用字库与软件分离存放,需要什么字体,就调用什么样的字库。1、LED点阵屏取模;2、液晶取模,如常用的LCD12864、LCD12832、LCD24064、LCD320240等;3、TFT彩色液晶屏取模本软件支持支持十二种大小字模取模:12点16点24点32点40点48点56点64点72点80点88点96点等;支持十六种取模方式;支持将字模文件保存为c或h形式;
以上就是关于51单片机128*64液晶C程序全部的内容,包括:51单片机128*64液晶C程序、求基于51单片机的LED点阵显示屏系统的设计与实现的论文、基于单片机的LED点阵显示设计等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)