基于51系列单片机的led旋转显示器.docx
《基于51系列单片机的led旋转显示器.docx》由会员分享,可在线阅读,更多相关《基于51系列单片机的led旋转显示器.docx(27页珍藏版)》请在冰豆网上搜索。
基于51系列单片机的led旋转显示器
题目:
基于51单片机的LED旋转显示器
LED旋转显示器
LED旋转显示器的设计
摘要
LED旋转显示器时基于视觉暂留原理,开发的一种旋转式LED显示屏。
其在具有一定转速地载体上安装16个LED发光器件,各LED发光管等间距排位一条直线,随着旋转速度的加快,在计算机软件精确的时序控制下,不断扫描出预设的文字,图案等。
使用一个光耦(U型槽的红外对管)作为定位传感器,当旋转一周时,挡光板遮挡光源,光敏三极管的集电极输出高电平,当离开挡光板时,集电极再次输出低电平,从而给单片机一个下降沿的跳变型号,产生一个中断,从而更新显示。
供电部分,因为整个装置是在不停的高速旋转当中,所以我们做了一个简单的电刷装置,把220V的交流电通过变压器变成12V的交流电,再由桥式整流电路,和滤波电路,变为平滑的直流电,最后通过7805芯片输出我们需要的5V直流电源,通过电刷把电源和指针板上的单片机连接为其供电。
而旋转载体因为需要12V的电压源,所以采用分别供电的方式。
关键字:
LED,视觉暂留,直流电机,桥式整流,旋转。
1LED旋转显示器在实际中的意义
LED的特点非常明显,寿命长、光效高、无辐射与低功耗。
LED的光谱几乎全部集中于可见光频段,其发光效率可达80~90%。
将LED与普通白炽灯、螺旋节能灯及T5三基色荧光灯进行对比,结果显示:
普通白炽灯的光效为12lm/W,寿命小于2000小时,螺旋节能灯的光效为60lm/W,寿命小于8000小时,T5荧光灯则为96lm/W,寿命大约为10000小时,而直径为5毫米的白光LED为20~28lm/W,寿命可大于100000小时。
有人还预测,未来的LED寿命上限将无穷大。
由于LED的种种优势,使得其在现在的各个领域里运用越来越广泛,我们设计的旋转LED显示屏幕,具有结构新颖,节约材料的特点,一列16个LED灯旋转显示之后,可以代替显示近似于16x180像素的显示宽度和内容,我想以后不断完善和改进之后应该可以在灯箱广告和儿童玩具方面有一定的发展空间。
2显示原理
旋转时钟是利用视觉暂留效应设计出来的,物体在快速运动时,当人眼所看到的影像消失后,人眼仍能继续保留其影像0.1-0.4秒左右的图像,这种现象被称为视觉暂留现象。
是人眼具有的一种性质。
人眼观看物体时,成像于视网膜上,并由视神经输入人脑,感觉到物体的像。
但当物体移去时,视神经对物体的印象不会立即消失,而要延续0.1-0.4秒的时间,人眼的这种性质被称为“眼睛的视觉暂留”。
假设我们设定我们的眼睛的暂留时间是0.4秒,如果我们的16个LED旋转一周的时间快过0.4秒,那么我们看到的图像就是这一列LED在各个位置显示的图像的叠加,如右图,如果我们用定时器把LED旋转一周的各个位置分割出180分,让它在相应的位置显示相应的图像,那么我们就可以得到一个累加的图像效果了。
3系统硬件电路的设计
3.1系统硬件框图
系统硬件框图右图所示。
3.2系统硬件原理图
系统硬件原理如下图所示。
3.3光耦传感器模块的设计
对射式U型槽光耦具有,响应速度快,驱动简单,安装容易,容易于单片机通信等特点。
如图所示,当上电之后,光耦的光敏三极管的集电极时输出低电平,当有物体挡住了光敏三极管感应的红外光线时,光敏三极管的集电极和发射极处于高阻态,所以集电极输出高电平,当光敏三极管再次感应到红外光源的时候,集电极再次输出低电平,从而给单片机一个中断信号。
3.4遥控模块的设计
3.4.1.遥控接收硬件部分
遥控模块部分,我们选择了一个市面上最常见的一种红外遥控接收头,把它的信号引脚直接接在单片机的外部中断1的借口上(P3.3),然后,遥控器,我们用了一个以前用的CD播放器的一个遥控器。
接收头如图
3.4.2.遥控编码特点
我们采用的遥控器是脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图2所示。
上述“0”和“1”组成的32位二进制码经38kHz的载频进行二次调制以提高发射效率,达到降低电源功耗的目的。
然后再通过红外发射二极管产生红外线向空间发射,如图3所示,连发波形如图4所示。
UPD6121G产生的遥控编码是连续的32位二进制码组,其中前16位为用户识别码,能区别不同的电器设备,防止不同机种遥控码互相干扰。
该芯片的用户识别码固定为十六进制01H;后16位为8位操作码(功能码)及其反码。
UPD6121G最多额128种不同组合的编码。
当遥控器在按键按下后,周期性地发出同一种32位二进制码,周期约为108ms。
一组码本身的持续时间随它包含的二进制“0”和“1”的个数不同而不同,大约在45~63ms之间,图4为发射波形图。
当一个键按下超过36ms,振荡器使芯片激活,将发射一组108ms的编码脉冲,这108ms发射代码由一个起始码(9ms),一个结果码(4.5ms),低8位地址码(9ms~18ms),高8位地址码(9ms~18ms),8位数据码(9ms~18ms)和这8位数据的反码(9ms~18ms)组成。
如果键按下超过108ms仍未松开,接下来发射的代码(连发代码)将仅由起始码(9ms)和结束码(2.5ms)组成。
代码格式(以接收代码为准,接收代码与发射代码反向)
①位定义
②单发代码格式
③连发代码格式
3.4.3.遥控解码
解码的关键是如何识别“0”和“1”,从位的定义我们可以发现“0”、“1”均以0.56ms的低电平开始,不同的是高电平的宽度不同,“0”为0.56ms,“1”为1.68ms,所以必须根据高电平的宽度区别“0”和“1”。
如果从0.56ms低电平过后,开始延时,0.56ms以后,若读到的电平为低,说明该位为“0”,反之则为“1”,为了可靠起见,延时必须比0.56ms长些,但又不能超过1.12ms,否则如果该位为“0”,读到的已是下一位的高电平,因此取(1.12ms+0.56ms)/2=0.84ms最为可靠,一般取0.84ms左右均可。
根据码的格式,应该等待9ms的起始码和4.5ms的结果码完成后才能读码。
3.5显示模块的设计
LED显示器具有功耗低,接口控制方便等优点,而且模块的接口信号和操作指令具有广泛的兼容性,并能直接与单片机接口,可方便地实现各种不同的操作,在各类测量及控制仪表中被广泛的应用。
当在LED上显示汉字时,应先取得汉字的点阵构成数据,然后将其写入显示存储器中进行显示。
旋转LED显示器是一种通过同步控制发光二极位置和点亮状态来实现图文显示的新型显示器,其结构新颖,成本低廉,可视角度达360°。
本设计采用16个并排发光二极管,利用人眼的“视觉暂留效应”显示文字及图案。
显示模块如图1.8所示。
图1.8显示模块框图
3.6电源模块的设计
电网提供的交流电源经过整流、滤波,可得到直流电压,但此电压仍然存在波纹。
同时,由于交流电网电压的波动,负载的变化和温度的影响等,使输出电压纹波会更大,即输出电流电压不稳定。
为了得到稳定的输出电压,在滤波电路与负载之间常常加入稳压电路,以使负载得稳定的输出电压。
通过上面的分析可知,直流稳压电源主要由变压器、整流电路、滤波电路和稳压电路四大部分组成。
直流稳压电路组成框图如图1.10所示。
图1.10直流稳压电源的组成框图
桥式整流滤波电路如图1.11所示。
图1.11桥式整流滤波电路
220V交流电压经过变压器输出为12V交流电压,再经过桥式整流成为单向脉冲电压,再经过滤波电路成为比较平稳的直流电压,最后通过7805稳压成输出稳定的5V直流电。
7805引脚图如图1.12所示。
图1.127805引脚图
对于7805三端稳压IC,它和其它78XX一样,都属于+V电压稳压输出链路。
其1脚为输入端,2脚接地,3脚稳压输出。
7805是正电压三端固定稳压器集成电路,属于线性稳压器件。
7806应用非常广泛,在各种稳压电源、充电器、家电等产品中均有运用。
主要参数:
最高输入电压:
36V
最大输出电流:
1.5A。
输出电压偏差:
典型=5V;最低=4.75V;最高=5.25V。
工作温度范围:
0~70℃。
3.7指针板的制作
3.7.1.指针板,由于为了使指针板的体积减小,减少飞线的发生,所以指针板,我们选择用PORTEL99SE设计,然后用手工制作PCB板的方式制作。
首先我们在PORTEL99SE软件上设计出原理图的PCB文件,然后,把它的顶层信号层和底层信号层分别打印到光华的不干胶纸上面,如图A.
3.7.2.将打印好的不干胶纸两面的各个过孔对齐,然后把清洗干净且剪裁大小合理的双面覆铜板夹在两张不干胶纸之间,然后用电熨斗压在上面加热转印,让附着在不干胶纸上面的碳粉完全受热融化后转印到双面覆铜板上面。
如图B。
3.7.3.然后把转印好的覆铜板放入三氯化铁溶液中腐蚀,三氯化铁溶液会把没有碳粉的所有铜箔都腐蚀掉,留下有碳粉的部分,就是PCB的信号线路图了,腐蚀过程图如图C。
3.7.4.等待的时候可以不停的摇晃容器,加速腐蚀过程,等到把所有没有覆铜的部分全部腐蚀之后,不覆铜板拿出溶液清洗,然后剪裁掉多余的部分,打孔之后就可以焊接元件了。
见D图。
4程序的设计
4.1程序设计的分析
程序部分刚刚开始想了很久都没有想到上面好的办法,后来,还是决定去网络上找找资料,看了很多程序后,总算有了一点自己的感悟,最让人头疼的是,用上面方法把电机转一周的各个位置分成360分来送显示,看到有的人说“先测得电机转一周的时间,然后除以360就行了”,但是我觉得这样做首先在程序上比较难实现,而且这样还有一个问题就是,如果这样,那么我们显示的标准总是以前面一周的转速为基准的。
不太科学,放弃了。
后来又看到一个方法,他们是把程序写好之后不停的拆卸调试,知道用来控制显示的总段服务程序的初值大概让其显示稳定就把初值固定下来,这样做,首先我想到,如果电机转速时快时慢,那么显示的字或者图案就会被拉长或者压段,而且这样我也不能真正很准确的吧一周分成360个显示单位,还是不科学,再次放弃了。
后来反复思考,总算找到一个解决这个问题的好办法。
通过思考,我发现需要让LED的闪亮频率与旋转的频率同步,所以采用了自适应的算法,自适应调节的算法的思想就是在外部中断处理程序里,先给定时器T0一个合适的初始值,通过定时器T0处理程序给T0的中断的次数计数,当旋转一周完成后,根据实际中断次数与预设的N比较,根据比较结果,在外部中断处理程序中对T0的初设值进行修正,直到定时器T0的中断次数达到N为止。
如此一来就如同有一个反馈信号一样,在不停的反馈的调节定时器的初值,旋转一周所产生的终端次数始终保持在我设定的一个值里面,这样我就可以很方便的设定在某一个位置显示上面类容了,而且整个调节过程是动态的,程序中并不需要知道旋转一周的具体时间值。
算法用了一个很简单的数学公式:
D=D+(S-N)
其中N为预设的固定值。
当S>N时,修正值D增大,使定时器T0的时间值增大,随之N值减小。
当S当S=N时,修正值不产生变化。
附上大概的自适应算法的框架:
/*外部中断0处理函数*/
voidintersvr0(void)interrupt0using1
{
D=D+(S-N)*2;//修正值
Pt=Pt+D;//得到定时器T0的初设值
S=0;//计数器清零,将重新计数
}
/*定时中断0处理函数*/
voidtimer0(void)interrupt1using1
{
TH0=-Pt/256;TL0=-Pt%256;//设置定时器T0的初设值
S++;//计数
}
这样一来,显示稳定的问题解决了,但是这个定时器T0因为初值时不确定的,所以不能再用它作为时间跳动的定时器计数了,因此时间的逻辑要用T1单独来实现。
4.2各模块程序的流程图及功能说明
4.2.1.MAIN函数部分
Main函数主要是用来初始化各个函数,然后进入空指令的死循环,这样来等待其他中断的来临,显示部分在中断服务程序中实现,参看右流程图
4.2.2.外部中断0服务程序部分(voidINT00(void)interrupt0)
外部中断0主要用于显示更新和定位用以及校正定时器T0的初值,所以我们在硬件部分加了一个光耦来给外部中断0提供中断信号,当旋转一周就会产生一个中断,当中断出现的时候,中断服务程序就会把这一周实际的T0中断次数与设定的值对比,然后校正T0的初值,最后,把各个显示数据清零,达到更新显示和定位的目的,详细参看右流程图。
4.2.3.定时器T0中断服务程序(voidkjp_test(void)interrupt1)
定时器T0的终端服务程序主要是为了把旋转一周的各个位置分为180分,这样方便显示和确定显示位置及显示内容。
在T0的中断服务程序中,首先重新给T0赋校正后的初值,然后判断当前的T0中断次数,及确定当前显示的位置,然后再判断是否改送显示,如果要送显示,则调用相应的现实之程序,然后T0中断次数自加一,最后跳出中断,详细参看右流程图
4.2.4.定时器T1中断服务程序(voidTime_luoji()interrupt3)
因为上面的T0的初值不是固定的,所以我们不能再使用T0用做时间运行的标准,要不时间会时快是慢,所以这里我们使用了T1作为时间的运行标准,我们给T1的定时为20毫秒,当产生50个中断,就是一秒钟,所以50个中断,时间秒的值加1。
详细参看流程图。
4.3完整的C程序
#include
unsignedcharcodeshuzi_0[8]={0x82,0x7C,0x7C,0x7C,0x7C,0x7C,0x82,0xfe};/*"0",0*/
unsignedcharcodeshuzi_1[8]={0xfe,0x7e,0x7a,0x00,0x7e,0x7e,0xfe,0xfe};/*"1",1*/
unsignedcharcodeshuzi_2[8]={0x3a,0x3c,0x5c,0x5c,0x6c,0x6c,0x72,0xfe};/*"2",2*/
unsignedcharcodeshuzi_3[8]={0xba,0x7c,0x7c,0x6c,0x6c,0x6c,0x92,0xfe};/*"3",3*/
unsignedcharcodeshuzi_4[8]={0x9e,0xae,0xb6,0xba,0x00,0xbe,0xbe,0xfe};/*"4",4*/
unsignedcharcodeshuzi_5[8]={0xa0,0x6c,0x6c,0x6c,0x6c,0x6c,0x9c,0xfe};/*"5",5*/
unsignedcharcodeshuzi_6[8]={0x82,0x6c,0x6c,0x6c,0x6c,0x6c,0x9a,0xfe};/*"6",6*/
unsignedcharcodeshuzi_7[8]={0xfc,0xfc,0xfc,0x0c,0xf4,0xf8,0xfc,0xfe};/*"7",7*/
unsignedcharcodeshuzi_8[8]={0x92,0x6c,0x6c,0x6c,0x6c,0x6c,0x92,0xfe};/*"8",8*/
unsignedcharcodeshuzi_9[8]={0xb2,0x6c,0x6c,0x6c,0x6c,0x6c,0x82,0xfe};/*"9",9*/
unsignedcharcodefuhao_A[8]={0xfe,0xfe,0xbb,0xbb,0xfe,0xfe,0xfe,0xfe};/*":
",10*/
/*******************************************************************************************************************************/
unsignedcharcodehanzi_a[2][16]={0xf7,0x37,0x47,0x70,0x17,0x73,0xb7,0xbd,0xdd,0xdd,0xed,0xe5,0xc9,0xdd,0xbf,0xff,
0xf7,0xe7,0xf7,0xf7,0x00,0xfb,0xbb,0xbd,0xbd,0xbd,0x81,0xbd,0xbc,0x9d,0xbf,0xff};/*轻*/
unsignedcharcodehanzi_b[2][16]={0xff,0xfb,0xfb,0xfb,0xfb,0xfb,0xfb,0x03,0xfb,0xfb,0xfb,0xfb,0xfb,0xfb,0xff,0xff,
0xdf,0xdf,0xdf,0xdf,0xdf,0xdf,0xdf,0xc0,0xdf,0xdf,0xdf,0xdf,0xdf,0xdf,0xdf,0xff};/*工*/
unsignedcharcodehanzi_c[2][16]={0xfb,0xcb,0x3d,0xfb,0x3b,0xc3,0xdf,0xef,0xf0,0x17,0xf7,0xf7,0xd7,0xe7,0xff,0xff,
0xef,0xf7,0xf9,0xfe,0x7d,0x73,0xbf,0xcf,0xf3,0xfc,0xf3,0xef,0x9f,0x3f,0xbf,0xff};/*欢*/
unsignedcharcodehanzi_d[2][16]={0xbf,0xbd,0xbb,0x37,0xff,0x03,0xfb,0xfd,0x7d,0x03,0xfb,0xfb,0xfb,0x01,0xfb,0xff,
0xff,0xbf,0xdf,0xe0,0xdf,0xb8,0xbd,0xbe,0xbf,0x80,0xbf,0xbd,0xbb,0x9c,0xdf,0xff};/*迎*/
unsignedcharcodehanzi_e[2][16]={0xbf,0xdf,0xef,0x03,0xdc,0xef,0x73,0x98,0xfb,0x0b,0xfb,0xbb,0x6b,0x73,0xff,0xff,
0xbf,0xcf,0xff,0x88,0x7f,0x7e,0x77,0x6d,0x4b,0x7c,0x7f,0x1f,0xff,0xee,0x9f,0xff};/*您*/
unsignedintTcz,s=0,bt=0;//s表示中断次数,Tcz表示定时器TO的初值
unsignedchariDex=0,hiDex=0;//控制字母和汉字的码值具体位数
unsignedcharHour=10,Minute=15,Second=45,Hour_Flag=0,buffer=0;
voidhanzi_Show(unsignedcharm);
voidTime_Show(unsignedcharm);//数字0-9加:
显示函数
voidTime_Amend();//时间逻辑控制函数
voidHour_Display();//小时显示函数
voidMinute_Display();//分钟显示函数
voidSecond_Display();//秒钟显示函数
voidA_Display();//“:
”显示函数
voidInit();//初始化函数
intk=0;
voidINT00(void)interrupt0//外部中断服务函数,定位更新显示和定时器0初值校正
{
k=k+(s-180);//k为校正值,当定时中断过快,K变大,Tcz同时变大;
Tcz=Tcz+k;//Tcz是用来给定时器T0赋初值的,通过这个公式来校正T0的初值,
iDex=0;//iDex值清零,防止调用显示子函数中时的值不同步,出现乱码
hiDex=0;//hiDex值清零,防止调用显示子函数中时的值不同步,出现乱码
s=0;//定时器中断次数清零,更新显示第二周
}
说明:
因为用了校正初值的方法,所以当旋转稳定下来的时候,旋转一周的中断次数就是我预设的180次,这样一来,旋转的位置就和我中断的次数是同步的,他们是正比的关系,显示的角度也就是中断次数乘以2的值,
及“显示角度=S*2”;
因此,这里可以用中断的当前次数来判断显示的位置。
voidkjp_test(void)interrupt1//定时器中断0中断服务程序。
用于判断显示位置,和//控制显示
{TH0=(-Tcz)/256;//把校正后的值给定时器T0赋值
TL0=(-Tcz)%256;
if(buffer==0)//判断buffer的值,确定显示上面内容
{//显示模式选择,0为数字式时钟
if(iDex>=8){iDex=0;}
if(s<16)//判断T0的终端次数,是否小于16
Hour_Display();//小于16,调用显示“小时值”的子函数
if(s>=16&&s<24)//判断T0的中断次数是否大于16且小于24
A_Display();//是则调用显示“:
”的子函数
if(s>=24&&s<40)//判断位置,
Minute_Display();//调用显示“分钟值”的子函数
if(s>=40&&s<48)//判断位置,
A_Display();//调用显示“:
”的子函数
if(s>=48&&s<64)//判断位置
Second_Display();//调用显示“秒钟值”的子函数
if(s>=64)//判断位置
{
P0=0xfe;P2=0xff;//关闭所有LED
}
}
if(buffer==1)//判断buffer的值,确定显示内容,1为显示“轻工欢迎您”
{
if(hiDex>=16)hiDex=0;
if(s<16)//判断位置是否在0-32度之间
{
hanzi_Show(0);//调用显示“轻”的子函数
}
if(s>=16&&s<32)//判断位置是否在32-64度之间
{
hanzi_Show
(1);//调用显示“工”的子函数
}
if(s>=32&&s<48)//判断位置是否在64-96度之间
{
hanzi_Show
(2);//调用显示“欢”的子函数
}
if(s>=48&&s<64)//判断位置是否在96-128度之间
{
hanzi_Show(3);//调用显示“迎”的子函数
}
i