基于89C51单片机的旋转显示屏设计与实现
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
基于89C51单片机的旋转显示屏设计与实现
摘要:本设计利用高速旋转中控制LED的亮灭,进行字符或图形的显示,控制器采用廉价的89C51单片机,完成显示内容的传输、字库的转换、显示等功能。
显示的内容给人一种漂浮的感觉,并且是360°全方位的显示,可以用于很多的场合,比如广告牌、家庭装饰、记分牌、娱乐显示等。
关键词:单片机,LED显示器,旋转显示
我院本次针对四川省大学生电子竞赛的选拔赛题目是设计一种电子显示屏,我们通过思考,决定设计一个以旋转方式进行显示的新的显示屏,在参赛的同时提高自己的电路设计和单片机开发能力。
一、设计思路
旋转显示屏是通过一个旋转的支架,支架上排列的LED受单片机控制在特定的位置亮或灭,从而显示出特定的字符或图形。
本作品主要是根据人的视觉暂留原理设计的。
人眼的视觉暂留时间是0.02S,如果采用3000转/S的高速电动机,带动LED高速旋转时,从视觉效果上就会产生一种柱状的LED显示屏的状态。
显示的字符或图形看起来是漂浮在空中的,不仅美观,而且新颖。
1、总体设计思路
本显示屏应由机械旋转部分、显示电路、通信电路等几部分构成。
由于显示屏在高速旋转的情况下不便于接线进行显示内容的更改,所以设计中我们选用了红外通信模式传输数据。
总体构成如下图所示:
图1 总体构成图
2、结构设计思路
显示屏的主体为两个可旋转的矩形框架。
我们在框架的两边都安装上发光二极管,由电动机通过中心轴带动框架进行高速旋转,框架上的两列发光二极管因高速旋转产生柱状显示屏。
其中一列发光二极管作为显示过程中的背景光灯使用。
主体框架如下图所示:
图2 框架设计图
二、硬件组成
1.总体构成
经过分析、比较,我们最终确定了以89C51作为控制器。
由于89C51芯片的片内存储器容量有限,我们另外扩展了一块存储器芯片29C040作为字库存储器,储存一个完整的汉字库,为显示屏提供显示内容。
为了解决计算机向旋转体中的单片机传送数据的问题,我们采用了红外传输的方式:计算机先通过RS232接口将数据传送到一个89C2051单片机,将数据用38KHz调制后利用红外设备传输。
此外,在显示屏高速旋转时,显示内容的稳定与否取决于转动的相位与LED发光时间的配合精度,所以还需要一个校准器件来判断显示屏的转动是否到达准确位置并根据结果做出校正处理,本设计中选用了霍尔元件进行相位检查并完成校正功能,使LED发光与旋转保持同步。
图3 硬件构成图
2.部分单元电路的设计
在硬件电路中,对几个关键电路我们采用的设计是:
(1)显示电路
图4 显示电路图
发光二极管的亮度决定了显示屏的视觉效果,我们采用2803芯片为发光二极管提供驱动,驱动电流达35mA(为保证发光二极管在该电流下能正常工作,我们对发光二极管进行了抽样测试,在45mA下工作十个小时,没有损坏)。
另外,由于89C51是8位数据处理器,我们可显示的最大数据位数可为16位,在设计时使用两片74HC573对接的方法提供两级数据锁存、缓冲,实现对端口的扩展能力。
2、字库电路
图5 字库电路图
我们为89C51扩展了一片512 KB容量的存储器芯片29C040,能够满足普通使用要求。
3、红外发送、接收电路
从计算机串口接收数据,可以在显示屏工作的状态下直截通过电脑进行显示内容的改变。
实现该功能的是红外发送和接收电路。
红外发送电路有双重功能:一是从计算机串口接收数据,二是将接收的信号调制成红外传输信号。
计算机的数据可以通过串口调试软件直接发出,发出的数据每包为固定的20个ASCII字符或10个汉字,采用低速发送以提高红外传输的准确度。
为了在显示屏高速旋转工作的状态下能准确无误地接收到红外数据,我们,在与显示主板相联的旋转中轴上端打一个通孔,在通孔上安装红外接收模块,让红外模块接收到的数据直截送给显示主板上的控制器。
在红外接收模块的正上方上固定安装的一个与红外发射头相联的发射板。
发射板是以AT89C2051为控制核心。
所有从电脑串口传送过来的数据都是通过它转变成带有38K载波的红外数据格式送给红外发射头供发射使用。
具体电路如下:
接接
图6 红外发送电路
电路中,T1完成从RS232到单片机串口的电平转换。
T2是红外发光管的驱动管。
图7 红外接收端电路图
该红外接收端位于转轴顶部,自带38KHz 解码功能,电路设计简单、使用方便。
AT89C2051中的程序如下: ORG 0 JMP START ORG 0BH JMP T0_INT START: CLR P3.5 MOV TMOD,#2
MOV TH0,#243;红外发射38K 参数设置 MOV TL0,#243 SETB EA SETB ET0
WAIT:
CLR P1.0;信号指示灯 JB
P1.7,WAIT;P1.7串口信号输入
SETB P3.5
SETB TR0
SETB P1.0
JNB P1.7,$
CLR P3.5
CLR TR0
MOV TH0,#243
MOV TL0,#243
JMP WAIT
T0_INT:
CPL P3.5;载波38K输出
RETI
END
4、相位检测电路
图8 相位检测电路图
图中CON 3为霍尔元件,当显示屏旋转到磁铁所在位置时,输出低电平脉冲至89C51,作为位置基准信号。
三、控制程序
显示内容的接收、转换、同步、显示都在控制器的控制之下。
根据数据的处理过程,我们编写了相应的控制软件。
1、控制程序流程图
图9 控制程序流程图
2、控制程序说明
(1)当程序开始运行的时候,首先就是要对程序进行初始化,主要作用就是让程序按照我们所设计的初始值去运行。
初始化之后,由红外接收器判断是否有数据送入,如果有数据送入就通过红外接收器进行接收,然后将该数据转换为点阵,再存入显示缓冲区,再根据霍尔元件检测到的同步信号,对数据进行校对,如果有数据输入的话那就将数据计数器进行清零。
执行完成后返回到初始化操作之后重复以上的操作。
(2)如果红外接收器没有检测到数据输入,则直接检查霍尔元件是否有同步信号,有则就返回初始化操作,无就直接读取片外存储器提取字库,然后进行显示。
对送数据计数器进行加1指令。
随后返回到初始化操作之后进行循环扫描。
四、设计制作过程中问题的解决
1、结构问题
我们认为,整机的机械结构是决定作品成败的关键。
经过试验,结构强度对稳定性影响很大,因此采用角钢支架,以钢板为底,同时对转盘进行平衡调节,基本解决了转动的稳定性问题。
防振采取了以下措施:
(1)在各种元器件的摆放位置上都做到平衡放置的要求;
(2)将电源、变压器设为一体以便加大主体的重量,从而抑制振动;
(3)采用钢质材料,从而加固各级子部件之间的联接,来减小振动;
(4)关键传动部位,严格要求加工精度,从根本上消除振动。
3、噪声问题
由于我们的框架是纯手工制作,所以它的精度并不是很高,在高速旋转的情况下难免会有一定的声音,于是我们采取了一系列的方法来减少噪音,比如让中间的主旋转框架在旋转的过程中尽量保持平衡,不致因哪一方过重从而导致噪音过大,在齿轮和轴承上添加润滑油以减小噪音的产生,在主框架外加上玻璃罩以隔离噪音。
4、供电问题
由于我们的电路板是随主框架一起高速旋转的,所以不可能使用导线进行供电,因为这样会把导线缠上,所以我们就另外想了两种方案。
(1)在中心轴的周围装上4个电刷,由电源通过电刷对高速旋转中的主电路板进行供电。
采用电刷供电的优点是结构简单、成本低。
但有电火花干扰的顾虑。
(2)在中心轴上面放一个小型发电机,发电机的定子为磁铁,固定在轴上,线圈为转子,直接连接在电路上,高速旋转时即可产生电流,对主电路进行供电。
这种方法可克服由电刷产
生的干扰。
在制作过程中我们先进行了实验,用单片机做了个循环灯控制器作为主电路,然后用电刷进行供电,结果发现使用碳刷后再使用稳压电路,可保证单片机系统的正常工作,因此我们选择了由电源通过电刷来对主电路板进行供电。
5、信号传输问题
由于控制单片机是在旋转的支架上,从计算机到单片机的数据传送也成了一个问题。
数据的传输不能采用电刷来进行,因此采用了红外传输的方式。
为了使红外传输可靠,采用了家电红外遥控的38KHz调制方式,使用了带有放大和解调功能的接收模块,很好地解决了这一问题。
6、同步问题
显示的图像或文字要稳定,同步是关键。
要达到同步的目的,同步信号的取得是关键。
在设计前,我们考虑了多种方案,如光电式、磁场式等,最后选择了使用霍尔器件的磁场方式,达到很好的效果。
五、小结
本系统以单片机芯片为核心部件,通过电脑来控制和调节所要显示的内容,通过红外数据传输可实现在显示屏旋转中对显示内容进行更改。
在设计过程中,力求硬件线路简单,充分发挥软件在编程方面灵活的特点,来满足系统设计的要求。
制作的成品也能完全实现显示所要达到的要求,能够显示出清晰,美丽的图案,但由于时间有限,系统还存在一些误差,存在很多有待改进的地方。
在竞赛的过程中,我们遇到了许多突发性的不太好解决的问题,也曾有过对自己没有信心的时候,但经过仔细冷静地思考之后,我们还是以最快的时间调整自我,回归参赛状态,继续进行设计。
通过这次比赛,我们深深的体会到了团队间的共同协作的重要性,提高了自己的动手能力和解决问题的能力。
C51源程序:
#include <reg51.h>
sbit gate11=P3^0;
sbit gate12=P3^1;
#define unit unsigned int
#define uchar unsigned char
uchar code GB_16[] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" " 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" " 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" " 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" " 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" " 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" " 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x34,0x40,0xC4,0x38,0x04,0x0F, //"欢" 0xC4,0x07,0xFC,0x3C,0x3E,0x98,0x24,0x60,
0x18,0x30,0x0E,0x1C,0xEF,0x0F,0xCA,0x07,
0x48,0x1C,0x38,0x70,0x1C,0xE0,0x08,0x40,
0x00,0x00,0x40,0x40,0x46,0xE0,0xDC,0x3F, //"迎" 0xE8,0x1F,0x40,0x32,0xFE,0x67,0xFC,0xC3,
0x06,0xC2,0x03,0xC1,0xFD,0xFF,0xFC,0xDF,
0x04,0xC2,0xFC,0xC7,0xFE,0xC3,0x04,0x40,
0x00,0x00,0x00,0x80,0x80,0x80,0x82,0x40, //"光" 0xBC,0x60,0x98,0x38,0x80,0x1F,0xFF,0x07,
0xFE,0x00,0x80,0x7F,0xE0,0xFF,0xB8,0xC0,
0x9E,0xC0,0xC4,0xC0,0x80,0xF0,0x00,0x40,
0x00,0x00,0xF8,0x0F,0xF0,0x07,0x00,0x00, //"临" 0xFF,0xFF,0xFE,0x7F,0x80,0x00,0x60,0xFF,
0x38,0x7E,0x1E,0x42,0x3F,0x7E,0x6A,0x7E,
0x68,0x42,0xC8,0xFE,0x4C,0x7F,0x08,0x02,
0x00,0x00,0x04,0x00,0x04,0x00,0x44,0x70, //"艺" 0x44,0xF8,0x5F,0xCC,0x5F,0xC6,0x44,0xC2,
0x44,0xC1,0xC4,0xC1,0xFF,0xC0,0x5F,0xC0,
0x04,0xC0,0x04,0xC0,0x06,0xF8,0x04,0x60,
0x00,0x00,0x44,0x22,0x4C,0x1A,0x7C,0x4E, //"新" 0xD7,0xFF,0xE6,0x7F,0x7C,0x0E,0x5C,0x9A,
0x44,0x60,0xFE,0x3F,0xFC,0x1F,0x46,0x00,
0xC2,0xFF,0xC3,0xFF,0x62,0x00,0x40,0x00,
0x00,0x00,0x00,0x40,0x04,0x40,0x04,0x40, //"工" 0x04,0x40,0x04,0x40,0x04,0x40,0xFC,0x7F,
0xFC,0x7F,0x04,0x40,0x04,0x40,0x04,0x40,
0x06,0x40,0x04,0x60,0x00,0x40,0x00,0x00,
0x00,0x00,0x80,0x00,0x60,0x00,0xF8,0xFF, //"作" 0xEF,0x7F,0x06,0x00,0x40,0x00,0x30,0x00,
0x1C,0x00,0xFF,0xFF,0xFA,0xFF,0x48,0x04,
0x48,0x04,0x68,0x04,0x4C,0x06,0x08,0x04,
0x00,0x00,0x08,0x00,0x1C,0x80,0x0E,0x81, //"室" 0x94,0x8B,0xD4,0x89,0x75,0x89,0x37,0xFF,
0x16,0xFF,0x34,0x89,0xD4,0x89,0x94,0x8B,
0x1C,0x81,0x0E,0xC0,0x04,0x80,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" " 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" " 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" "
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" "
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" "
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //" "
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
unit disp1,disp2,key1,key2;
unit a,w,ii,jj,kk;
void Delay(unit ms)
{
while(--ms);
}
void display_HZ(void)
{
gate11=key1;gate12=key2;
for(ii=0;ii<5;ii++){
for(jj=0;jj<16;jj++){
P2=~GB_16[jj*2+ii*32+kk*2];P1=~GB_16[1+jj*2+ii*32+kk*2]; Delay(120);
P1=0xff;P2=0xff;
}
P1=0xff;P2=0xff;
}
}
void intersvr0(void) interrupt 0
{
key1=!key1;
key2=!key2;
w=1;
if(kk<240) kk++; else kk=0;
}
void main(void)
{
EA=1;IT0=1;EX0=1;
key1=0;key2=1;
for(;;){
w=0;
Delay(40);
if(w==1) display_HZ();
}
利用人眼的视觉暂留效应,使手在摆动到不同位置的时候,让位于一条直线上的LED显示二维图像的不同的列,实现图形扫描显示。
物理机制:
当我们在摆动手臂的时候,短时间内摆动位置和左右幅度不会有太大变化,利用我们手臂的这个运动规律,只要能得到棒从一侧摆动到另一侧的时间,然后把这个时间分成N份,然后在每一份的时间里显示不同的花样就能实现图形的显示。
当我们在摆动手臂的时候,并不能预先得知此次摆动需要的时间,怎样得到从一侧摆动到另一侧的时间呢?再想想,短时间内我们手臂的摆动频率也不会有特别大的变化,我们只要能得到前一次摆动所用的时间,然后用这个时间近似得到下一次摆动所需要的时间,然后分N份就可以了。
得到一次摆动所需的时间的任务由光遮断器完成,在棒上装一个可以摆动的用来遮挡光遮断器光线的细杆,粗细比光遮断器的狭缝稍宽,我用的是整流桥焊后剪下的一段管脚。
每左右摆动一次这个杆就会通过一次光遮断器,使单片机产生一次中断,两次中断之间的时间就是想要得到的时间,实现这个功能用掉2051的一个定时器T0和外部中断INT0。
然后用2051的另一个定时器T1,其定时时间是T0的N分之一,每次中断依次显示一列,就是照片上的效果。
再细想一下,手臂摆动的频率大于2Hz的时候才大概能由视觉暂留看出图形,再快也不过十几Hz,由T0为16位定时器的最长计时时间得到2051的时钟频率1MHz最合适,还有遮挡
用的细杆的粗细也可以大概估算,使其不会对计时精度产生太大影响。
所用元件:
AT89C2051 8元
高亮LED共8个 3.6元
电池盒0.5元
单面万用板8元可以裁成3块,做3个摇棒
陶瓷谐振器1元
光遮断器2元
电阻电容导线2元
外壳* 0.7元可以裁成2块
可选元件:
电源开关1元
总成本不超过20元
*外壳我用在家乐福买的半透明文件夹卷成的
此电路电路非常简单,代码短,但是用到了2051的外部中断编程和T0、T1定时器编程,非常适合初学者练习。
我制作这个玩具花费了两个晚上的时间。
此电路和程序还有扩展改进
的空间,如在长时间不摆动的情况下可以使单片机进入省电模式,显示不仅限于对称图片等,大家可以试试。
以下是源程序:
/*************************************************************************/
#include <reg51.h>
#define SEG 17 //每帧图片分成17列来显示
#define INTERVAL 20 //每幅图片在左右摇摆20次后换下一幅
typedef unsigned char uchar;
typedef unsigned int uint;
code char pattern[17][3]={ //3幅图片的字模
{0xff,0xff,0xff},
{0xff,0xff,0xff},
{0xff,0xff,0xff},
{0xff,0x9f,0xff},
{0xff,0x6f,0xf9},
{0xff,0x77,0x65},
{0xfe,0xbb,0x1e},
{0x7e,0xdd,0xfe},
{0x00,0xee,0xfe},
{0x7e,0xdd,0xfe},
{0xfe,0xbb,0x1e},
{0xff,0x77,0x65},
{0xff,0x6f,0xf9},
{0xff,0x9f,0xff},
{0xff,0xff,0xff},
{0xff,0xff,0xff},
{0xff,0xff,0xff},
};
uchar phase,th1,tl1,index,count;
main()
{
EA=0;
EX0=1;
ET1=1;
PX0=1;
IT0=1;
TMOD=0x11;
index=0;
EA=1;
while(1)
{
}
}
void Int0_Handle(void) interrupt 0 using 2 {
uint t0_time;
TR0=0;
TR1=0;
TF1=0;
t0_time=TH0<<8|TL0;
TL0=0;
TH0=0;
TR0=1;
t0_time=65535-t0_time/SEG;
th1=t0_time/256;
TH1=th1;
tl1=t0_time % 256;
TL1=tl1;
if (count<3*INTERVAL){ //3幅图片循环count++;
}
else{
count=0;
}
index=count/INTERVAL;
if (th1!=0xff || tl1!=0xff){ //如果摆动特别慢,定时器溢出就不显示phase=0;
TR1=1;
}
else{
TR1=0;
}
}
void Timer1(void) interrupt 3 using 3
{
if (phase<SEG) //17段依次显示
{
P1=pattern[phase][index];
phase++;
TH1=th1;
TL1=tl1;
}
}。