POV(Persistence of Vision):是一种“视觉暂留”现象,每当人的眼睛在观察物体之后,物体的映像会在视网膜上保留一段很短暂的时间。在这短暂的时间段里,当前面的视觉形象还没完全消退,新的视觉形象有继续产生时,就会在人的大脑里形成连贯的视觉错觉。如将单片机控制的LED流水灯设备稍作改进,让它动起来,就能神奇地显示各种字符或图案,其效果如漂浮在空中一般。 硬盘时钟就是将淘汰、废弃的硬盘改造或制作成一个时钟。 这神奇的创意最早是由加拿大Alan Parckh提出的,在他的个人网页里首次介绍了如何将一个废旧的硬盘制作成时钟,后来美国康奈尔大学的两位大学生Jason Amsel和Konstantin Klitenik根据这个创意作了一个电子设计项目,这个项目中,除增加了一个带触摸功能的LCD屏,将双色LED改成三色LED外,其他方面没突破性的进展。真正让这个创意耳目一新的是另一个美国人,他采用高亮度的RGB三色LED柔性灯条作光源,不但使显示的亮度明显增加,并能产生绚丽多彩的动态图案。遗憾的是他的作品并非是真正意义上的时钟,仅仅是演示而已。 这里做的硬盘时钟将在他们的基础上进行如下改进: 1) 使用专门的直流电动机驱动IC,让硬盘的转速可调,在降低转速后,可进一步减小噪声,同时有利于单片机的控制。 2) 将开机画面与时钟显示有机结合,用mini型LED作为辅助显示,一方面扩展显示“年月日”信息,另一方面方便调时。 3) 时钟的整个屏面一改过去的全黑局面,并增加时间刻度,方便辨识。 使制作的硬盘时钟真正向实用性迈进了一大步。 一、POV项目 POV项目
| POV LED硬盘时钟
| 发光体
| 18只RGB三色LED
| 运动方式
| LED处于静止状态,硬盘盘片旋转
| 供电方式
| 外接电源,单电源,直流12V输入
| 传感器
| 光耦传感器(光断续器)
| 主控芯片
| AT89S52
| 调控方式
| 按键
| 功能
| 盘片上显示“时分秒”,辅助显示LED显示“年月日”
|
1) 单片机:采用支持ISP下载功能的单片机,方便程序下载及调试。 2) LED:由于硬盘时钟是靠透过一窄缝的光线来显示发光颜色的,光的损失量很大,如果能选用高亮度的LED,考虑到为方便安装LED,选用的是成品的高亮度RGB三色全彩柔性光条。 3) Mini型LED显示模块:为了让硬盘时钟更加方便实用,在硬盘时钟前面另外增加一个辅助显示屏,考虑到发光盘在视觉上的主导性,这个辅助屏尽量选用尺寸较小的mini型LED。 4) 传感器:由于在硬盘的盘片上开有缺口,采用透射式光断续器。 5) 主轴电动机驱动:主轴电动机为无刷无传感器直流电动机,可以沿用原来硬盘板上的电路来驱动它。但这样的方式问题很多:一是大多数情况下难以保证电动机常转;二是转速固定无法改变,相应噪声和震动很大。采用专门的驱动IC驱动这个直流电动机。 6) 功能:除主要的发光盘面显示很酷的“时分秒”信息外,辅助LED显示屏还显示“年月日”信息。另外,在开机状态下,发光盘显示多彩的色彩变换,辅助屏显示如欢迎类的英文字符;在调试状态时,辅助显示屏提示和显示调整项及调整值。 二、显示原理及系统状态转移图 1. POV LED硬盘时钟的显示原理 在高速旋转的开口盘片下,用单片机控制LED的发光,并让LED的闪烁与旋转的细缝同步协调。也就是每当开细缝的圆盘转到特定的位置,就让LED闪现特定颜色的光,这样就会让我们的眼睛感觉到那个位置有一光条出现。 由于人眼有“视觉暂留”现象,只要我们在几处特定的位置让LED闪现所需要的光,就会让我们眼睛产生圆盘上同时有几个光条的错觉。 将此原理进一步推广,我们还可以让整个圆盘发出五颜六色的光,在本制作中,我们以此原理,还在发光的表盘上增加时钟刻度。
2. 系统状态转移图 系统分为3种工作状态:开机画面状态、正常显示状态和调整时间状态,转换通过“设置”按键来完成。
3. 系统框图
三、硬件制作
1. 找一个废弃或坏的硬盘,将硬盘废物利用来制作时钟。将其拆卸,取出所有零部件(电机除外),因为要利用的是硬盘的外壳、电机和盘片。
2. 直流电动机驱动电路原理图
3. 主控部分原理图
4. 找一块铝皮做成弧形,用来弥补硬盘盘片下方的空缺。
5. 准备背光光源,为了制作简便,采用成品的RGB-LED全彩发光二极管柔性光带,本例中光带共用了18颗LED,此光带自带3M自粘胶,共阳极,DC12V驱动电源。
6. 将硬盘盘片用锯条开一条锯缝,该锯缝有两个作用,一是作为转速计数用的基准点,二是作为透过背光显示时钟的重要显示的必要条件。为了让背光显得稍微亮一点,再用白纸剪一个底圆作为背光衬底。
7. 将白色衬底粘于盘片底部,弧形铝条沿弧形支撑固定于硬盘两边。
8. 准备两块印制板,一块作为前面控制板(根据硬盘弧形开出印板的弧形),一块作为背后的主控制板,这里采用了易购的洞洞板吧。
9. 准备光断续器小板,为了检测到硬盘的转速和计算出时钟指针的具体位置,用了遮蔽式光断续器检测硬盘盘片的那个锯缝。
10. 将光断续器小板安装在硬盘盘体的左上角这个位置正好合适,再将柔性LED发光条安装贴在硬盘沿盘片一周的盘体内侧上。
11. 焊接前面控制板,由于前后板子连接线有点多,为了美观,采用了接插件。
焊接排线
12. 焊接后面主控板
焊接飞线
13. 联机通电,将程序烧写进单片机,为了方便将主控板上预留了ISP烧录接口。
14. 拼接装配,将按钮上写上相应的文字。
15. 开机测试
视频效果:youku视频:
[size=14.399999618530273px]源程序如下:
[size=14.399999618530273px]ds12887.头文件程序:[size=14.399999618530273px]- #include <absacc.h>
- //BCD码转换
- #define NUM2BCD(x) ((((x)/10)<<4)|(x%10))
- #define BCD2NUM(x) (((x)>>4)*10+((x)&0x0f))
- //时钟寄存器
- #define TIME_SECOND XBYTE[0xff00]
- #define TIME_MINUTE XBYTE[0xff02]
- #define TIME_HOUR XBYTE[0xff04]
- #define TIME_DAY XBYTE[0xff06]
- #define TIME_DATE XBYTE[0xff07]
- #define TIME_MONTH XBYTE[0xff08]
- #define TIME_YEAR XBYTE[0xff09]
- //控制寄存器
- #define REGISTERA XBYTE[0xff0A]
- #define REGISTERB XBYTE[0xff0B]
- #define REGISTERC XBYTE[0xff0C]
- #define REGISTERD XBYTE[0xff0D]
- unsigned char Time[]={0,0,0,1,1,10}; //时钟数组
- //设置DS12C887
- SetupDs12887(void)
- {
- REGISTERA = 0x70;
- REGISTERB = 0xa2;
- //设置时间
- TIME_SECOND= NUM2BCD(Time[0]);
- TIME_MINUTE= NUM2BCD(Time[1]);
- TIME_HOUR = NUM2BCD(Time[2]);
- TIME_DATE = NUM2BCD(Time[3]);
- TIME_MONTH = NUM2BCD(Time[4]);
- TIME_YEAR = NUM2BCD(Time[5]);
- //计时开始
- REGISTERA = 0x20; //开始走时
- REGISTERB = 0x22;
- }
- //读出DS12C887
- void ReadDs12887(void)
- {
- Time[0] = BCD2NUM(TIME_SECOND);
- Time[1] = BCD2NUM(TIME_MINUTE);
- Time[2] = BCD2NUM(TIME_HOUR);
-
- Time[3] = BCD2NUM(TIME_DATE);
- Time[4] = BCD2NUM(TIME_MONTH);
- Time[5] = BCD2NUM(TIME_YEAR);
- }
复制代码
[size=14.399999618530273px]主源程序:
- //-------------------------------------------------------------------------------
- //程序名: 硬盘时钟程序
- //说 明: 单片机89S52 晶振24M
- //-------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------
- //**嵌入文件**
- //-------------------------------------------------------------------------------
- #include <reg52.h> //51单片机硬件资源参数说明
- #include <intrins.h> //调用"_nop_()"函数
- #include "ds12887.h" //实时时钟IC驱动
- //-------------------------------------------------------------------------------
- //**宏定义函数**
- //-------------------------------------------------------------------------------
- //位置修正函数
- #define XZ 8 //传感器位置与"十二点"位置间隔(指针走的步数)
- #define Ru(x) (x+XZ)>59? (x+XZ-60):(x+XZ)
- //延时函数
- #define Delay5Us {_nop_();_nop_();_nop_();_nop_();_nop_();\
- _nop_();_nop_();_nop_();_nop_();_nop_();}
- #define Delay50Us {Delay5Us;Delay5Us;Delay5Us;Delay5Us;Delay5Us;\
- Delay5Us;Delay5Us;Delay5Us;Delay5Us;Delay5Us;}
- //-------------------------------------------------------------------------------
- //**端口定义**
- //-------------------------------------------------------------------------------
- //字符LED显示端口
- #define WORDPORT P2
- #define DIGPORT P1
- //RGB_LED端口定义
- sbit P_LED_R=P3^0;
- sbit P_LED_G=P3^1;
- sbit P_LED_B=P3^4;
- //设置键端口定义
- sbit Key1=P1^6;
- sbit Key2=P1^7;
- sbit Key0=P3^5;
- //指示LED端口定义
- sbit Put_LED=P3^3;
- //-------------------------------------------------------------------------------
- //**变量说明**
- //-------------------------------------------------------------------------------
- //定义颜色值
- unsigned char code LED_COL[8][3]=
- {
- 1,1,1, //白
- 1,0,0, //红
- 0,1,0, //绿
- 0,0,1, //蓝
- 1,1,0, //黄
- 1,0,1, //品红
- 0,1,1, //青
- 0,0,0, //黑
- };
- //钟盘及指针颜色模式
- unsigned char code COL_MOD[4][5]=
- {
- 4,0,3,2,1,
- 5,1,4,2,3,
- 6,2,3,4,1,
- 7,4,3,2,1
- };
- //开机画面LED动态显示的字符
- unsigned char code CH[]={
- 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, //全空格
- // 0x48,0x54,0x54,0x50,0x3A,0x2f,0x2f,0x48, //http://h
- 0x20,0x42,0x44,0x38,0x43,0x47,0x20,0x20, // BD8CG
- //0x49,0x2e,0x42,0x41,0x49,0x44,0x55,0x2e, //i.baidu.
- 0x44,0x49,0x59,0x3A,0x50,0x4f,0x56,0x20, //DIY:POV
- //0x43,0x4F,0x4D,0x2f,0x35,0x32,0x5f,0x44, //com/52_d
- 0x4c,0x45,0x44,0x43,0x4c,0x4f,0x43,0x4b, //LEDCLOCK
- //0x49,0x59,0x20,0x20,0x20,0x20,0x20,0x20, //iy
- 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
- 0x20,0x20 //
- };
- // 设置状态LED显示的字 符
- unsigned char code SD[5][6]={
- 77,73,78,85,84,69,
- 72,79,85,82,32,32,
- 68,65,84,69,32,32,
- 77,79,78,84,72,32,
- 89,69,65,82,32,32
- };
- //调整时钟的限制量
- unsigned char code maxnum[]={59,23,31,12,99,7}; //调整值最大限量
- unsigned char code minnum[]={0,0,1,1,0,1}; //调整值最小限量
- //其它变量定义
- unsigned char t1,t2,t3; //时分秒中间变量
- unsigned char M1,M2,M3; //时分秒中间变量
- unsigned char PT_col[60]; //指针颜色
- unsigned char BL_col; //表盘颜色
- unsigned char KD_col; //刻度颜色
- unsigned char ST; //进程标志
- unsigned char CM; //表盘色彩组合模式
- unsigned char TZ; //调整项
- unsigned int Tn; //指针位置值
- unsigned int Tp; //定时中断T1设置值
- unsigned char N; //实际表盘平分分割数
- unsigned char S; //预设表盘平分分割数,取值60
- unsigned char Cn; //色彩变量
- //-------------------------------------------------------------------------------
- //**函数定义**
- //-------------------------------------------------------------------------------
- /*外部中断0*/
- void intersvr0(void) interrupt 0 using 1
- {
- unsigned char Q;
- unsigned int D;
- TH1=-1;TL1=-1; //让定时中断T1与外部中断适当错开,使屏显示稳定
- D=D+(N-S); //修正值。实际值与预设值比较,作出校正
- Tp=50000+D; //得到定时中断T1的初设值
- //50000大约是旋转一周所需的时间,这样在开机时可显示
- //从1到60等分圆的动态过程
- if((N==S)&&(Q<500)){
- Q++;
- if(Q==99) ST=1;
- } //显示稳定后进入下一进程
- N=0;
- Tn=0;
- Cn=0;
- }
- /*T1处理函数*/
- void timer1(void) interrupt 3 using 1
- {
- TH1=-Tp/256;
- TL1=-Tp%256;
- if(ST==0){ //开机画面
- if(Cn<7) Cn++; else Cn=1; //只显示7种色
- P_LED_R=LED_COL[Cn][0];
- P_LED_G=LED_COL[Cn][1];
- P_LED_B=LED_COL[Cn][2];
- }
- if(ST==1){ //正常工作状态
- //产生指针颜色
- P_LED_R=LED_COL[PT_col[Tn]][0];
- P_LED_G=LED_COL[PT_col[Tn]][1];
- P_LED_B=LED_COL[PT_col[Tn]][2];
- //产生指针宽度
- Delay50Us;Delay50Us; Delay50Us;
- //产生背景颜色
- P_LED_R=LED_COL[BL_col][0];
- P_LED_G=LED_COL[BL_col][1];
- P_LED_B=LED_COL[BL_col][2];
- Tn++; //下一个显示单元
- }
- N++; //从传感器位置开始,定时中断次数计数
- }
- /*延时(用于较长时间段)*/
- void Delay(unsigned int tt)
- {
- unsigned char ii;
- for(;tt>0;tt--)
- for(ii=94;ii>0;ii--);
- }
- /*主程序*/
- void main(void)
- {
- unsigned char i,j; //循环变量
- unsigned char Xz=3; //刻度校正
- S=60; //旋转一周,定时中断T1产生60个中断
- CM=0; //使用显示屏颜色组合,可有四种模式选择
- BL_col=COL_MOD[CM][0]; //背景光颜色
- KD_col=COL_MOD[CM][1]; //刻度颜色
- //中断初始化
- TMOD=0x11;
- TH1=0; TL1=0;
- TR1=1; ET1=1;
- IT0=1; EX0=1;
- EA=1;
- //显示屏初始化
- for(i=0;i<60;i++) PT_col[i]=BL_col;
- PT_col[0+Xz]=0;
- PT_col[5+Xz]=0;
- PT_col[10+Xz]=0;
- PT_col[15+Xz]=0;
- PT_col[20+Xz]=0;
- PT_col[25+Xz]=0;
- PT_col[30+Xz]=0;
- PT_col[35+Xz]=0;
- PT_col[40+Xz]=0;
- PT_col[45+Xz]=0;
- PT_col[50+Xz]=0;
- PT_col[55+Xz]=0;
- DIGPORT=0xec; WORDPORT=32;
- DIGPORT=0xed; WORDPORT=32;
- DIGPORT=0xee; WORDPORT=32;
- DIGPORT=0xef; WORDPORT=32;
- DIGPORT=0xd3; WORDPORT=32;
- DIGPORT=0xd7; WORDPORT=32;
- DIGPORT=0xdb; WORDPORT=32;
- DIGPORT=0xdf; WORDPORT=32;
- P_LED_R=0;
- P_LED_G=0;
- P_LED_B=0;
- //SetupDs12887();
-
- //开机画面
- Delay(6000);
- for(j=0;j<35;j++){ //LED动态显示字符
- DIGPORT=0xec; WORDPORT=CH[7+j];
- DIGPORT=0xed; WORDPORT=CH[6+j];
- DIGPORT=0xee; WORDPORT=CH[5+j];
- DIGPORT=0xef; WORDPORT=CH[4+j];
- DIGPORT=0xd3; WORDPORT=CH[3+j];
- DIGPORT=0xd7; WORDPORT=CH[2+j];
- DIGPORT=0xdb; WORDPORT=CH[1+j];
- DIGPORT=0xdf; WORDPORT=CH[0+j];
- Delay(4000);
- }
- P_LED_R=1;
- P_LED_G=0;
- P_LED_B=0;
- //进入主循环
- for(;;){
- if(TZ==0){ //LED显示年月日信息
- ReadDs12887(); //读取时间值
- Put_LED=1;
- M1=Time[0];M2=Time[1];M3=Time[2];
- DIGPORT=0xec; WORDPORT=0x30+Time[3]%10;
- DIGPORT=0xed; WORDPORT=0x30+Time[3]/10;
- DIGPORT=0xee; WORDPORT=0x2d;
- DIGPORT=0xef; WORDPORT=0x30+Time[4]%10;
- DIGPORT=0xd3; WORDPORT=0x30+Time[4]/10;
- DIGPORT=0xd7; WORDPORT=0x2d;
- DIGPORT=0xdb; WORDPORT=0x30+Time[5]%10;
- DIGPORT=0xdf; WORDPORT=0x30+Time[5]/10;
- }
- else //LED显示调整项
- {
- DIGPORT=0xec; WORDPORT=0x30+Time[TZ]%10;
- DIGPORT=0xed; WORDPORT=0x30+Time[TZ]/10;
- DIGPORT=0xee; WORDPORT=SD[TZ-1][5];
- DIGPORT=0xef; WORDPORT=SD[TZ-1][4];
- DIGPORT=0xd3; WORDPORT=SD[TZ-1][3];
- DIGPORT=0xd7; WORDPORT=SD[TZ-1][2];
- DIGPORT=0xdb; WORDPORT=SD[TZ-1][1];
- DIGPORT=0xdf; WORDPORT=SD[TZ-1][0];
- M1=Time[0];M2=Time[1];M3=Time[2];
- Put_LED=~Put_LED; //LED指示在设置状态闪亮
- }
- Delay(50);Delay(50);
- Delay(50);Delay(50);
- //用背景色覆盖原来的指针色
- PT_col[t1]=BL_col;
- PT_col[t2]=BL_col;
- PT_col[t3]=BL_col;
- //取指针数值
- if(M3>12) M3=M3-12;
- t1=Ru(M1);t2=Ru(M2);t3=Ru(M3*5+M2/12);
- //刷新时钟刻度色
- PT_col[0+Xz]=KD_col;
- PT_col[5+Xz]=KD_col;
- PT_col[10+Xz]=KD_col;
- PT_col[15+Xz]=KD_col;
- PT_col[20+Xz]=KD_col;
- PT_col[25+Xz]=KD_col;
- PT_col[30+Xz]=KD_col;
- PT_col[35+Xz]=KD_col;
- PT_col[40+Xz]=KD_col;
- PT_col[45+Xz]=KD_col;
- PT_col[50+Xz]=KD_col;
- PT_col[55+Xz]=KD_col;
- //显示时钟
- PT_col[t1]=COL_MOD[CM][2];
- PT_col[t2]=COL_MOD[CM][3];
- PT_col[t3]=COL_MOD[CM][4];
- Delay(50);Delay(50);
- Delay(50);Delay(50);
- Delay(50);Delay(50);
-
- //键盘处理
- if(Key0==0){ //设置键
- Delay(120);
- if(Key0==0) {if(TZ>0) TZ--; else TZ=5;}
- if(TZ==0) SetupDs12887();
- Delay(1200);
- }
- Delay(50);Delay(50);
- Delay(50);Delay(50);
- if(Key1==0){ //加法键
- Delay(120);
- if(Key1==0) {
- if(Time[TZ]<maxnum[TZ-1]) Time[TZ]++; else Time[TZ]=minnum[TZ-1];
- }
- Delay(1200);
- }
- Delay(50);Delay(50);
- Delay(50);Delay(50);
-
- if(Key2==0){ //减法键
- Delay(120);
- if(Key2==0) {
- if(Time[TZ]>minnum[TZ-1]) Time[TZ]--; else Time[TZ]=maxnum[TZ-1];
- }
- Delay(1200);
- }
- Delay(50);Delay(50);
- Delay(50);Delay(50);
- }
- }
复制代码
|