摘 要
本实验以单片机和LED发光二极管为核心,辅以必要的电路,通过电机带动驱动板,由于人眼的视觉暂留现象而显示出一面时钟,并可通过红外遥控改变显示模式和调时。
【关键词】单片机系统 红外遥控 视觉停留 显示时间
一丶引言
1.1丶本设计的目的
(1) 培养单片机的正确应用,培养初步解决工业控制、工业检测等领域具体问题的能力。
(2) 本设计熟悉单片机应用系统的开发和开发过程,熟悉软硬件设计的工作方法、工作内容和工作步骤。
(3) 提高学生理论与实践相结合的能力,把理论知识运用到实践中,从而更好地掌握教科书中的理论知识。
1.2丶本设计的基本要求
(1)驱动盘在电机转动的带动下,可以显示时钟画面,可以自动计时。
(2)在电机工作电压和环境因素的影响下,电机转速不稳定导致时钟画面不稳定,需要程序自适应调节转速,使时钟画面基本稳定。
(3)要求能够实现动画显示,通过红外遥控、数字时钟和指针时钟三种模式之间的转换
(4)时间可通过红外遥控设定。
二丶总体设计
2.1丶基本工作原理
旋转时钟利用了视觉暂留原理。用LED灯排成一列,取中间一点为圆心,他们围绕圆心旋转,利用单片机控制驱动板,使驱动板旋转到特定位置时点亮特定的LED发光二极管这样,如果在0.1s内这该列发光二极管能旋转完一圈,由于人眼的视觉暂留现象人眼就会产生错觉,而把先后点亮的LED连成一个完整的画面。
2.2丶硬件总体设计
2.2.1丶硬件组成
(1)驱动板
驱动板上包括DS1302时钟芯片,单片机最小系统,光电开关,红外接收头,LED发光二极管等元件,负责红外接收与解码、光电信号检测,并解析这些控制信号,从而控制单片机的输入与输出,从而控制LED的亮与灭。驱动板的主控芯片采用了STC1F2K08S2。驱动板的电源由电源提供,该电源通过电机轴上的导电环传递到驱动板上。
驱动板上有一排超高亮 LED。它们就是显示部件了,亮点所在。
驱动板上有一个光电开关,在外壳的对应位置安装了一个挡片。驱动板每旋转一周,光电开关就会经过一次挡片位置,并产生一个低电平信号。这个信号被称为“过零信号” 。该信号将对单片机产生一个外部中断。有了这个信号,单片机就可以在旋转的过程中实时检测计算驱动板的角度位置。并根据指针板所处的不同位置,点亮相应的 LED,利用人眼的视觉暂留效应,形成完整的显示画面。
通过检测两次过零信号的时间间隔, 就可以计算出电机转速,或者驱动板旋转一周的时间。把该时间等分所需要的份数,即可求得每个显示列的位置。这样,就不必再去对电机进行匀速控制了。
(2)电机
电机采用的是普通直流电机,负责带动驱动板旋转。电机的启动或停止,由电源直接控制。由于没有采用转速匀速控制,因此电机驱动电路较简单。
2.2.2丶方案选择
(1)供电方式选择
1、常见的供电方式
根据调查的结果,指针板的供电方式一般有以下四种:
1)自感应发电
这种方法,就是从驱动板上引出导线,接入到电机内部绕在转子上,电机旋转时该导线切割磁场产生感应电动势,经过整流后作为指针板上的电源。
A、这种方式的优点是:
设计很巧妙,无机械磨损。
更巧妙的是,由于感应出来的电动势是交流的,所以可以利用该过零 信号来定位,不必另外准备定位信号了。
B、这种方式的缺点是:
提供的电流有限,只能适合 LED 较少的旋转时钟,当 LED 数量较多时,需要更多的电流,这种方式就不能满足了。
其次,这种方式要对电机本身进行改造,也有一定的难度。并不是所有的电机都适合这种改造。而且这种改造可能会给电机带来损害。
另外还有一个问题,就是这种方式,只有在电机旋转时才能发电给驱动板供电,一旦停止转动,供电也就无以为继了,这样要实现旋转时钟的不间断走时,还得另加备用电池并采用低功耗设计。
2)自备电池
这种方式,就是在驱动板上安装电池,由电池供电。一般是用两到三节 7 号电池。
A、这种方式的优点是:
Ⅰ.不用担心电压波动。
Ⅱ.也不存在机械磨损,不用担心接触不良之类问题的困扰。
B、这种方式的缺点是:
Ⅰ.很费电池,三天两头换电池,既不经济也不环保,还很麻烦!
Ⅱ.电池很重,一般的电机带不动,必须用很大很大的电机哦。这也意味了成本的上升。
3)机械传导供电
也就是采用滑环和电刷,通过机械接触传导电流。
A、这种方式的优点是:
能够提供比较大的工作电流。
B、这种方式的缺点是:
Ⅰ.有机械摩擦,会产生磨损。因此要求滑环和电刷材料要耐磨,经得起折腾。另外,还得有足够的弹性,并且要耐锈,否则会导致接触不良。
Ⅱ.有机械阻力,因此要求电机有比较大一点的功率。
Ⅲ.有机械噪音。
4)感应供电
原理和变压器原理相当,就是在2个相距很近的线圈中,一只线圈作为电能发送端,另一只线圈作为电能接收端,发送端接入交变电流,在相距很近的接收端就能同时感应到交变电流。
A、这种方式的优点是:
无机械噪音。
B、这种方式的缺点是:
Ⅰ.线圈耦合度低,供电效率低。
Ⅱ.制作难度大。
Ⅲ.需增加震荡电路和滤波整流电路。
综合以上四种:第一种,虽然优点多,但难度很大,并且成本很高;第二种,没有太多的担心,可是使用起来相当的麻烦,可能还会因为更换电池不及时而导致其中其他的零件受损;第三种,虽然能提供较大电流,但是会产生些摩擦,而摩擦会带来噪音及影响转速和稳定性。第四种,虽然需要在驱动板上加上滤波整流电路,增加驱动板重量。但是不产生机械噪音而且供电稳定,但是所以我们采用的是第四种感应供电。
(2)过零信号产生电路选择
1)霍尔传感器
霍尔传感器处于工作状态时输出总是处于高电平状态,当磁钢N极接近传感器正面的有效距离,输出端变为低电平。当磁钢撤离传感器有效距离。输出端又显示低电平,从而产生下降沿,是单片机中断口接收到下降沿,从而产生中断。
2)光电开关
光电开关处于工作状态时输出总是处于高电平状态,当光电开关经过挡片时,输出端变为低电平。当光电开关离开挡片时,输出端又显示低电平,从而产生下降沿,是单片机中断口接收到下降沿,从而产生中断。
3)红外对管
红外接收头正常情况下处于高阻态,电路相当于断路,输出高电平,当经过红外发射管时,输出端变为低电平。当红外接收头离开红外发射管时,输出端又显示高电平,从而产生下降沿,是单片机中断口接收到下降沿,从而产生中断。
红外对管和光电开关利用光敏二极管对光的敏感性原理制作的,反应较灵敏,其中光电开关需要额外增加挡片,设计不方便。而霍尔传感器利用磁场对电场的作用原理制作的,反应较迟缓,且感应磁钢的距离远,当转速较快时难以控制。故该设计采用红外对管。
(3)LED选择
由于旋转LED要求时钟的分辨率高且重量轻,长度短,故该设计选用贴片LED发光二极管
(4)单片机选择
由于旋转LED驱动板上包括较多元器件和芯片,空间不足,所以该设计选用LQFP封装的单片机,这种封装的单片机为正方形,面积小,质量轻。
2.3丶软件总体设计
2.3.1丶系统主程序设计
主程序的功能是完成系统的初始化,系统开启后,先打开需要的定时器中断、外部中断。接着进入时钟显示,再根据是否有外部中断进入相应的中断程序。主程序流程图如下:
图2.1主程序流程图
2.3.2丶外部中断1设计
外部中断1实现时钟模式的转换,时钟的调整。中断采用STC15F2K08S2内部的外部中断,由红外接收头接收到的程序引发。外部中断的流程图如下:
图2.2 外部中断1流程图
2.3.3丶定时器中断设计
定时器中断实现时钟计数,时钟显示,消除误差等功能。中断采用STC15F2K08S2内部定时器0实现。定时器1中断的流程图如下:
图2.3定时器0流程图
三丶硬件设计
3.1 STC15F2K08S2
3.1.1 STC15F2K08S2介绍
STC15F2K08S2是一种低功耗、高性能CMOS8位微控制器,具有 8K 在系统可编程Flash 存储器。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得STC15F2K08S2为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。 具有以下标准功能: 8k字节Flash,2048字节SRAM, 42位I/O 口线,看门狗定时器,内置53KB EEPROM,MAX810复位电路,6个16 位 定时器/计数器,一个6向量2级中断结构,全双工串行口。另外 STC15F2K08S2可降至0Hz 静态逻辑操作,支持2种软件可选择节电模式。空闲模式下,CPU 停止工作,允许RAM、定时器/计数器、串口、中断继续工作。掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。最高运作频率35MHz,6T/12T可选。
STC15F2K08S2单片机的主要性能参数有:
1. 增强型8051 单片机,6 时钟/机器周期和12 时钟/机器周期可以任意 选择,指令代码完全兼容传统8051.[1]
2. 工作电压:5.5V~3.3V(5V 单片机)/3.8V~2.0V(3V 单片机)
3. 工作频率范围:0~40MHz,相当于普通8051 的0~80MHz,实际工作 频率可达48MHz
4. 用户应用程序空间为8K 字节
5. 片上集成2048字节SRAM
6. 通用I/O 口(42 个),复位后为:P0/P1/P2/P3 是准双向口/弱上拉, P0 口是漏极开路输出,作为总线扩展用时,不用加上拉电阻,作为 I/O 口用时,需加上拉电阻。
7. ISP(在系统可编程)/IAP(在应用可编程),无需专用编程器,无 需专用仿真器,可通过串口(RxD/P3.0,TxD/P3.1)直接下载用户程 序,数秒即可完成一片
8. 具有EEPROM 功能
9. 具有看门狗功能
10. 共3 个16 位定时器/计数器。即定时器T0、T1、T2
11. 外部中断4 路,下降沿中断或低电平触发电路,Power Down 模式可 由外部中断低电平触发中断方式唤醒
12. 通用异步串行口(UART),还可用定时器软件实现多个UART
13. 工作温度范围:-40~+85℃(工业级)/0~75℃(商业级)
3.1.2 STC15F2K08S2引脚介绍
STC15F2K08S2如图3.1所示是一个有40个引脚的芯片,引脚配置如图3.1所示。
图3.1 STC15F2K08S2引脚图
VCC 电源电压。
GND 接地。
RST 复位输入。当RST变为高电平并保持2个机器周期时,所有I/O引脚复位至“1”。
P0口(P0.0~P0.7,40~44引脚,1~3引脚):P0口是一个漏极开路的8位双向I/O口。作为输出端口,每个引脚能驱动8个TTL负载,对端口P0写入“1”时,可以作为高阻抗输入。在访问外部程序和数据存储器时,P0口也可以提供低8位地址和8位数据的复用总线。此时,P0口内部上拉电阻有效。在Flash ROM编程时,P0端口接收指令字节;而在校验程序时,则输出指令字节。验证时,要
P1口(P1.0~P1.7,4~5,7~12引脚): 8位双向I/O口。引脚P1.2~P1.7提供内部上拉,当作为输入并被外部下拉为低电平时,它们将输出电流,这是因内部上拉的缘故。P1.0和P1.1需要外部上拉,可用作片内精确模拟比较器的正向输入(AIN0)和反向输入(AIN1),P1口输出缓冲器能接收20mA电流,并能直接驱动LED显示器;P1口引脚写入“1” 后,可用作输入。在闪速编程与编程校验期间,P1口也可接收编码数据。
P2口(P2.0~P2.7,30~37引脚):P2口是一个带内部上拉电阻的8位双向I/O端口。P2的输出缓冲器可以驱动(吸收或输出电流方式)4个TTL输入。对端口写入1时,通过内部的上拉电阻把端口拉到高电平,这时可用作输入口。P2作为输入口使用时,因为有内部的上拉电阻,那些被外部信号拉低的引脚会输出一个电流()
P3口(P3.0~P3.7,18~25引脚): 引脚P3.0~P3.5与P3.7为7个带内部上拉的双向I/0引脚。P3.6在内部已与片内比较器输出相连,不能作为通用I/O引脚访问。P3口的输出缓冲器能接收20mA的灌电流;P3口写入“1”后,内部上拉,可用输入。P3口也可用作特殊功能口,其功能见表2.1所示。P3口同时也可为闪速存储器编程和编程校验接收控制信号。
表3.1 P3口引脚复用功能
引脚号 | 复用功能 |
P3.0 | RXD(串行输入口) |
P3.1 | TXD(串行输出口) |
P3.2 | INT0(外部中断0) |
P3.3 | INT1(外部中断1) |
P3.4 | T0(定时器0的外部输入) |
P3.5 | T1(定时器1的外部输入) |
P3.6 | INT2(外部中断2)RXD2(串行输入口) |
P3.7 | INT3(外部中断3)RXD3(串行输出口) |
3.2 DS1302
3.2.1 DS1302介绍
DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。DS1302是DS1202的升级产品,与DS1202兼容,但增加了主电源/后备电源双电源引脚,同时提供了对后备电源进行涓细电流充电的能力。
3.2.2 DS1302引脚介绍:
图3.2 DS1302引脚图
VCC2:主电源;
VCC1备份电源;当vcc1>vcc2+0.2时,由vcc1向DS1302供电,当vcc1<vccc2时,由vcc2向DS1302供电。
SCLK:串行时钟,输入,控制数据的输入与输出;
I/O:三线接口的双向数据线;
CE:输入信号,在读、写数据期间,必须为高。该引脚有两个功能:1CE开始控制字访问移位寄存器的控制逻辑;2CE提供结束字节或多字节的传输方法。
3.2.3 时钟芯片DS1302原理图
图3.3 DS1302原理图
3.3 74HC595锁存器
3.3.1 74HC595介绍
74HC595 是一款漏极开路输出的 CMOS 移位寄存器,输出端口为可控的三态输出端,亦能串行输出控制下一级级联芯片 。
3.3.2 74HC595引脚介绍
图3.4 74HC595引脚图
O E:三态允许控制端,低电平有效
D0-D7:数据输入端
00-07:数据输出端
LE:锁存控制端
GND:接地(0V)
VCC:接电源电压
3.4 红外对管
3.4.1 红外对管介绍
红外对管是红外线发射管与光敏接收管,或者红外线接收管,或者红外线接收头配合在一起使用时候的总称。在光谱中波长自0.76至400微米的一段称为红外线。。
3.4.2 红外对管原理
红外接收头是一个具有光敏特征的PN结,属于光敏二极管,具有单向导电性,因此工作时需加上反向电压。无光照时,有很小的饱和反向漏电流(暗电流)。此时光敏管不导通。当光照时,饱和反向漏电流马上增加,形成光电流,在一定的范围内它随入射光强度的变化而增大。在光电耦合器输入端加电信号使发光源发光,光的强度取决于激励电流的大小,此光照射到封装在一起的受光器上后,因光电效应而产生了光电流,由受光器输出端引出,这样就实现了电一光一电的转换。
图3.5红外对管实物图
3.5 无线供电
3.5.1 无线供电介绍
无线供电,无线能量传输或称无线电力传输是一种不经由电导体将电力能量从发电装置或供电端转送到电力接收装置的技术。无线能量传送是一个通称,当中可使用多种不同技术达成,包括电场、磁场及电磁波。发射器把电能转换成相对应场的能量状态,传输经过一空间后由一个或多个接收器接收并转换回为电能。。
3.6 红外遥控器
红外遥控的发射电路是采用红外发光二极管来发出经过调制的红外光波;红外接收电路由红外接收二极管、三极管或硅光电池组成,它们将红外发射器发射的红外光转换为相应的电信号,再送后置放大器。
发射器一般由指令键(或操作杆)、指令编码系统、调制电路、驱动电路、发射电路等几部分组成。当按下指令键或推动操作杆时,指令编码电路产生所需的指令编码信号,指令编码信号对载波进行调制,再由驱动电路进行功率放大后由发射电路向外发射经调制定的指令编码信号。
图3.9红外遥控器实物图
3.7 红外接收头
红外接收电路通常被厂家集成在一个元件中,成为一体化红外接收头。 内部电路包括红外监测二极管,放大器,限副器,带通滤波器,积分电路,比较器等。红外监测二极管监测到红外信号,然后把信号送到放大器和限幅器,限幅器把脉冲幅度控制在一定的水平,而不论红外发射器和接收器的距离远近。交流 信号进入带通滤波器,带通滤波器可以通过30khz到60khz的负载波,通过解调电路和积分电路进入比较器,比较器输出 高低电平,还原出发射端的信号波形。注意输出的高低电平和发射端是反相的,这样的目的是为了提高接收的灵敏度。
图3.9 红外接收头
图3.10 红外接收头典型电路
四丶软件设计
4.1 自适应转速
系统开机后,程序先进入测试转速阶段, 测试2次中断之间(旋转一圈)定时器中断数,与所需的中断数对比,通过对比调整定时器初设值,达到改变旋转1圈定时器中断数的目的。
/*外部中断0处理函数*/
void intersvr0(void) interrupt 0 using 1
{
D=D+(S-N)*2; //修正值
Ti0=600+D; //得到定时器T0的初设值
S=0; //计数器清零,将重新计数
}
/*定时中断0处理函数*/
void timer0(void) interrupt 1 using 1
{
TH1=-Ti0/256;TL1=-Ti0%256;//设置定时器T0的初设值
S++; //计数
}
其中S为旋转一圈定时器1实际中断数(既实际显示的列数),D为调整值,N为旋转一圈定时器所需的中断数(既所需显示的列数)。程序分析如下:
0、在外部中断的处理程序里,先给定时器一个合适的初设值Pt。
程序开始,D=0,Ti0即为600(按需设定),并得到定时器初设值。
1、定时器开始计数,每溢出一次S自加一次。
2、完成一圈后,处理外部中断函数。
当S>N时,修正值D增大,使定时器T1的时间值增大,随之S值减小。
当S<N时,修正值D减小,使定时器T1的时间值减小,随之S值增大。
当S=N时,修正值不产生变化。
函数中N值是按需设定的常数。
4.2 数字显示模式
时钟的上半部分和下半部分的显示是相反的,故需要对数字显示进行调整。故分2步显示,第一步显示上半部分,正常显示。程序如下:
if(ii<16)
{
P2=0xf1;P1=~nAsciiDot1[jj*2+v[ii]*16]; //显示的上半圆部分(正显)
P2=0xf2;P1=~nAsciiDot1[1+jj*2+v[ii]*16];
}
16表示显示16个字
第二步显示下半部分,因需和上半部现实相反,故下半部反向显示,程序如下:
else if(ii<32)
{
P2=0xf2;P1=~nAsciiDot2[14-jj*2+v[ii]*16]; //显示的下半圆部分(反显)
P2=0xf1;P1=~nAsciiDot2[15-jj*2+v[ii]*16];
}
4.3 指针显示模式
显示时,由于要将12点的位置和数字模式统一显示到正上方,而不是中断发生处,故需进行转换:
P2=0xf1;P1=(0xfe<<3*(ii%5==4))&(0xff>>2*(ii==Tme0))&(0xfe<<5*(ii%15==14));
P2=0xf2;P1=(0xff*(ii!=Tme0)*(ii!=Tme1))&(0xff>>4*(ii==Tme2));
P2=0xf4;P1=(0xff*(ii!=Tme0)*(ii!=Tme1)*(ii!=Tme2));
P2=0xf8;P1=(0xff*(ii!=Tme0)*(ii!=Tme1)*(ii!=Tme2));
4.4 红外遥控处理程序
红外发射采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,生的遥控编码是连续的32位二进制码组,其中前16位为用户识别码,能区别不同的电器设备,防止不同机种遥控码互相干扰。后16位为8位操作码(功能码)及其反码。当单片机检测到引导码时便开始接收32位2进制码组。
设置Imax和Imin两个量,作为对引导码时长的上限和下限。若接收的引导码在此范围内,开始读码。取长脉冲的上线值为Inum3,短脉冲的下限值为Inum2,长短脉冲中间值为Inum1。读出的脉宽在Inum1和Inum3之间,取值“1”,在Inum1和Inum2之间,取值“0”。而识别码长为8位,正好可用一字符变量保存取值。用Im[]数组的4个变量存放32位串码。
解码流程图如下。
图4.1 红外遥控解码流程图
解码程序如下:
#define Imax 28000 //此处为晶振为11.0592时的取值,
#define Imin 25000 //如用其它频率的晶振时,
#define Inum1 2300 //要改变相应的取值。
#define Inum2 2000
#define Inum3 4600
uchar Im[4]={0x00,0x00,0x00,0x00}; //存放4个字节32位编码
unsigned long m,Tc; //两脉冲间隔
uchar flag,IrOK;
unsigned char ii,jj; //循环变量,用于显示LED
unsigned char Tme0,Tme1,Tme2; //时间中间变量
unsigned int Ti0; //定时器T1预设值
unsigned char v[32]; //显示缓冲区
unsigned char TZ; //调整项
unsigned char ST; //调整标志
unsigned char DM;
unsigned int S;
unsigned long D; //显示模式
//外部中断解码程序
void INT_1(void) interrupt 2 using 1
{//ET1=0; P1=0xff;P2=0xff;
Tc=TH0*256+TL0; //提取中断时间间隔时长
TH0=0;// 09oik
TL0=0; //定时中断重新置零
if((Tc>Imin)&&(Tc<Imax)) //9.12ms+4.5ms
{
m=0;
flag=1;
return;
} //找到启始码
if(flag==1)
{
if(Tc>Inum1&&Tc<Inum3) //2.25ms
{
Im[m/8]=Im[m/8]>>1|0x80; m++; //1
}
if(Tc>Inum2&&Tc<Inum1) //1.125ms
{
Im[m/8]=Im[m/8]>>1; m++; //0 取码
}
if(m==32)
{
m=0;
flag=0;
if(Im[2]==~Im[3])
{
IrOK=1;
}
else IrOK=0; //取码完成后判断读码是否正确
}
//准备读下一码
}
}
五丶系统操作说明
连上电源,分别接通电机和驱动板,待驱动板显示稳定时,便可用红外遥控进行模式转换、调时等操作。其中红外遥控按键及各按键功能如下图所示:
图5.1 红外遥控器按键及功能
六丶结束语
结束了2周的课程设计,我跟深入地了解了单片机的内部结构和工作原理,更好地掌握了单片机这门课程的知识。虽然在制作作品的过程中遇到重重困难,但是在坚持努力调试下,各种问题都能够引刃而解。解决每一个问题,都能够有一分收获和一份知识的积累,随着最后作品的完成,我们体会的不仅是成功的过程,得到的更是分工与合作的真谛。
这次课程设计过程中,我们遇到了硬件及软件的多方面问题。
(1)电刷供电给电机轴带来较大摩擦力,产生较大噪音。解决方法是改用弹性较好较细的铁丝,在电刷接触处擦上石墨润滑。
(2)作品制作过程中,出现始终显示乱码,且显示内容稳定不变,没有计时的现象。经仔细分析是时钟芯片坏了,换个新的时钟芯片就解决问题了。
(3)自适应转速方面,开始时显示正常,过一段时间会产生显示误差积累使显示不正常。解决方法是改用自适应转速程序。
参考文献:
[1] 吕明,张捷. 数字音频播放系统的设计与实现[J].荆楚理工学院学报,2017,24(5): 20-22.
[2] 李广弟.单片机基础[M].北京:北京航空航天大学出版社,2016:6-28.
[3] 龚之春.数字电路[M].成都:电子科技大学出版,1999:123-127.
[4] W.G.荣格.集成运算放大器应用手册[M].北京:世界图书出版公司,1990:3-72.
[5] 李伯成编著. 基于MS-51单片机的嵌入式系统设计. 电子工业出版社. 2014.8
[6] 周立功等.PDIUSBD12USB固件编程与驱动开发.北京:北京航空航天大学出版社,2013.2:54-89.
[7] 丛伟波, 杨勇, 韩清凯. 低功耗数据采集系统的USB接口设计[J].单片机与嵌入式系统运用,2015,(5):56-60.
[8]宋戈等.51单片机应用开发范例大全.人民邮电出版社.2010.2:81-86
[9] 胡汉才.单片机原理及接口技术.清华大学出版社,1996.
[10]刘振文,邓毅华,彭友斌.基于CC2500的2.4GHZ RFID系统设计[J].电子技术应用,2018年第7期:78-81.
[11]罗光平,尤一鸣.利用PWM给单片机应用增加语音功能[J].单片机与嵌入式系统应用,2007年第1期:36-38.
[12]张义和.Protel DXP电路设计大全[M].北京:中国铁道出版社,2014.
[13]陈惠开,徐守义.无源与有源滤波器—原理与应用.人民邮电出版社,1989.12.
[14]TI's new MSP430 wireless development tool [J]. Electronics Today, 2007, 40 (2) _1 .
[15]Michele Magno.An energy efficient multimodal Wireless Video Sensor Network with eZ430–RF2500 modules[R].Pervasive Computing and Applications (ICPCA), 2011 5th International Conference on.
[16]CC2500,pdf. Chipcon Products from Texas Instruments,2016.
[17] 马忠梅,单片机的C语言应用程序设计.北京:北京航空航天大学出版社,1997。
[18]王天曦 ,李洪儒.电子技术工艺基础.北京:清华大学出版社,2019。
[19] 弘道工作室,融会贯通 Protel99电路设计.北京: 人民交通出版设,2018。
[20] 张伟,王力,赵晶,ProtelDXP 入门与提高.北京:人民邮电出版社, 2013.2。
[21] 李广弟,朱月秀,王秀山.单片机基础[M]. 北京:北京航空航天大学出版社,
2001.7。
[22] 谭浩强,C程序设计(第二版)[M].北京:清华大学出版社,2013。
[23] 付家才,单片机控制工程实践技术[M]. 北京:化学工业出版
附录A 实物图
附录B 原理图
侧板
基板
附录C 源程序
主函数
#include <intrins.h>
#include <string.h>
#include "STC15Fxxxx.h"
#include "74HC595.H"
#include "Time.h"
#include "povdat.h"
#include "display.H"
#include "UART.h"
#include "IR_KEY.H"
#include "IR.h"
#include "Exti.h"
#include "display_hand.h"
#include "display_figure.h"
#include "EEPROM.H"
#include "ds1302.h"
//系统主函数
void main (void)
{
uint i;
OEB=1;
Exti_init(); //中断初始化
Time_init(); //两个定时器初始化
Init_1302(); //设置1302的初始时间
time_first_read(); //第一次读取时间
InitUART(); //串口初始化
PCA_INT(); //PCA初始化
Picaddrint();
//SendOneByte(0xff); // 调试用的
//UART_Send_Str("知趣电子欢迎你!");
//IapReadByte(uint addr); //从EEPROM读1字节数据;
//IapProgramByte(0x0000, 0x00); //写1字节数据到EEPROM;
//IapEraseSector(0x0ff1); //擦除扇区
for(i=0;i<19;i++)
{
disp_time[i]=0x00;
}
while(1)
{
/*
display_hand();
dis_open( );
LED0=0;
LED2=0;
Send_data_A(0);
Send_data_B(0);
Send_data_C(0,0);
P0=0;
*/
OEC=1;
key_cont();
switch (DIS_MODE)
{
case 0:
//display_hand();
display_pic();
break;
case 1:
display_hand();
//display_pic();
break;
case 2:
//display_hand();
display_figure();
break;
default:
break;
}
}
}
显示函数
//图片显示函数
void display_pic()
{
uchar k,i,j;
uint temp;
dis_close();
if(Time0_flag)
{
Time0_flag=0;
{
temp=(T0_S-1);
temp+=135; //调节显示位置
if(temp>179)
temp=temp-180;
if((temp)<=180)
{
/*
dat1=~pov_pic[temp][0];
dat2=~pov_pic[temp][1];
dat3=~pov_pic[temp][2];
dat4=~pov_pic[temp][3];
*/
/*
dat4=~pov_pic[temp][0];
dat3=~pov_pic[temp][1];
dat2=~pov_pic[temp][2];
dat1=~pov_pic[temp][3];
dat3=~pic2[temp][0];
dat4=~pic2[temp][1];
dat1=~pic2[temp][2];
dat2=~pic2[temp][3];
*/
if(temp>=L||((wordaddr_start-(2*temp))>=wordaddr_end))
{
dat6=0xff; //从EEPROM读
dat5=0xff; //从EEPROM读
}
else
{
dat6=~IapReadByte(wordaddr_start-(2*temp)); //从EEPROM读
dat5=~IapReadByte(wordaddr_start-(2*temp-1)); //从EEPROM读
}
i++;
if(i>179)
{
i=0;
L++;
wordaddr_start+=2; //开始地址移位,控制流动的快慢
if(wordaddr_start>wordaddr_end+360)
{
L=0;
dat5=dat6=0xFF;
wordaddr_start=IapReadByte(0X0010)*256+IapReadByte(0X0011); //文字EEPROM结束地址
}
}
switch (PIC_MODE)
{
case 0:
dat4=~IapReadByte(picaddr_start+(4*temp)+0); //从EEPROM读
dat3=~IapReadByte(picaddr_start+(4*temp)+1); //从EEPROM读
dat2=~IapReadByte(picaddr_start+(4*temp)+2); //从EEPROM读
dat1=~IapReadByte(picaddr_start+(4*temp)+3); //从EEPROM读
k++;
if(k>179)
{
k=0;
j++;
if(j>100) //图片快慢调节
{
j=0;
picaddr_start+=720;
if(picaddr_start>picaddr_end)
picaddr_start=IapReadByte(0X0000)*256+IapReadByte(0X0001); //图片EEPROM开始地址
}
}
break;
case 1:
dat4=~IapReadByte(filmaddr_start+(4*temp)+0); //从EEPROM读
dat3=~IapReadByte(filmaddr_start+(4*temp)+1); //从EEPROM读
dat2=~IapReadByte(filmaddr_start+(4*temp)+2); //从EEPROM读
dat1=~IapReadByte(filmaddr_start+(4*temp)+3); //从EEPROM读
k++;;
if(k>179)
{
k=0;
j++;
if(j>0) //调节动画1快慢
{
j=0;
filmaddr_start+=720;
if(filmaddr_start>filmaddr_end)
filmaddr_start=IapReadByte(0X0004)*256+IapReadByte(0X0005); //动画1开始地址
}
}
break;
case 2:
dat4=~IapReadByte(filmaddr_start_1+(4*temp)+0); //从EEPROM读
dat3=~IapReadByte(filmaddr_start_1+(4*temp)+1); //从EEPROM读
dat2=~IapReadByte(filmaddr_start_1+(4*temp)+2); //从EEPROM读
dat1=~IapReadByte(filmaddr_start_1+(4*temp)+3); //从EEPROM读
k++;
if(k>179)
{
k=0;
j++;
if(j>1) //调节动画2快慢
{
j=0;
filmaddr_start_1+=720;
if(filmaddr_start_1>filmaddr_end_1)
filmaddr_start_1=IapReadByte(0X0008)*256+IapReadByte(0X0009); //动画2开始地址
}
}
break;
case 3:
dat4=~IapReadByte(filmaddr_start_2+(4*temp)+0); //从EEPROM读
dat3=~IapReadByte(filmaddr_start_2+(4*temp)+1); //从EEPROM读
dat2=~IapReadByte(filmaddr_start_2+(4*temp)+2); //从EEPROM读
dat1=~IapReadByte(filmaddr_start_2+(4*temp)+3); //从EEPROM读
k++;
if(k>179)
{
k=0;
j++;
if(j>1) //调节动画3快慢
{
j=0;
filmaddr_start_2+=720;
if(filmaddr_start_2>filmaddr_end_2)
filmaddr_start_2=IapReadByte(0X000C)*256+IapReadByte(0X000D); //动画3开始地址
}
}
break;
default:
break;
}
LED0=0;
LED2=0;
Send_data_A(dat2);
Send_data_B(dat1);
Send_data_C(dat5,dat6);
P0=dat4;
dis_open();
delay();
LED0=1;
LED2=1;
dis_close();
P0=P2=0xff;
}
}
}
}
/**********************************************************************************************
函数名:指针模式函数
调 用:无
参 数:无
返回值:无
结 果:
备 注:
/*********************************************************************************************/
void display_hand(void)
{
uchar Temp,datmp1,datmp2,datmp3,datmp4 ,i;
//P1=P0=0xff;
//dis_close();
time_refurbish();
datmp1=0xff;
datmp2=0xff;
datmp3=0xff;
datmp4=0xff;
if(Time0_flag )
{
Time0_flag=0;
Temp=(T0_S-1);
if(hour>11)
hour=hour-12;
if(Temp<=180)
{
if(Temp>=L||((wordaddr_start-(2*Temp))>=wordaddr_end))
{
dat6=0xff; //从EEPROM读
dat5=0xff; //从EEPROM读
}
else
{
dat6=~IapReadByte(wordaddr_start-(2*Temp)); //从EEPROM读
dat5=~IapReadByte(wordaddr_start-(2*Temp-1)); //从EEPROM读
}
i++;
if(i>179)
{
i=0;
L++;
wordaddr_start+=2; //开始地址移位,控制流动的快慢
if(wordaddr_start>wordaddr_end+360)
{L=0;
wordaddr_start=IapReadByte(0X0010)*256+IapReadByte(0X0011); //文字EEPROM结束地址
}
}
//datmp1=0;datmp2=0;datmp3=0;datmp4=0;
if(t_mode==0) //为0时显示数字指针表盘模式 ,为1时显示指针式表盘模式
{
//标12点
if(Temp==1) {datmp1=0xcd;}
if(Temp==2) {datmp1=0xb6;}
if(Temp==3) {datmp1=0xb6;}
if(Temp==4) {datmp1=0xb6;}
if(Temp==5) {datmp1=0xb9;}
if(Temp==179){datmp1=0x80;}
//标3点
if(Temp==42){datmp1=0xf1;}
if(Temp==43){datmp1=0xee;}
if(Temp==44){datmp1=0xfe;}
if(Temp==45){datmp1=0xf1;}
if(Temp==46){datmp1=0xfe;}
if(Temp==47){datmp1=0xee;}
if(Temp==48){datmp1=0xf1;}
//标6点
if(Temp==88){datmp1=0xd9;}
if(Temp==89){datmp1=0xb6;}
if(Temp==90){datmp1=0xb6;}
if(Temp==91){datmp1=0xb6;}
if(Temp==92){datmp1=0xc1;}
//标9点
if(Temp==132){datmp1=0xf1;}
if(Temp==133){datmp1=0xee;}
if(Temp==134){datmp1=0xef;}
if(Temp==135){datmp1=0xe1;}
if(Temp==136){datmp1=0xee;}
if(Temp==137){datmp1=0xee;}
if(Temp==138){datmp1=0xf1;}
//标1、2、4、5、7、8、10、11整点
if((Temp==15)||(Temp==30)||(Temp==60)||(Temp==75)||(Temp==105)||(Temp==120)||(Temp==150)||(Temp==165)){datmp1=0xfc;}
}
else //否则
{
if(Temp%3==0){datmp1=0xfe;datmp2=0xff;datmp3=0xff;datmp4=0xff; } //画分刻度
if((Temp%15==0)||(Temp==179)||(Temp==1)){LED1=0;datmp1= datmp1&0xfc;}
}
switch(Temp%3)
{
case 0:
{
//datmp1=0xfe;datmp2=0xff;datmp3=0xff;datmp4=0xff; //画分刻度
//if(Temp%15==0){LED1=0;datmp1= datmp1&0xfc;}
if((Temp==second*3)||(Temp==179)){datmp1= datmp1&0x07;datmp2= datmp2&0x00; datmp3= datmp3&0x00;datmp4= datmp4&0x00;}
if(Temp==minute*3){datmp1= datmp1&0xff;datmp2= datmp2&0x00; datmp3= datmp3&0x00;datmp4= datmp4&0x00;}
if(Temp==(15*hour+minute/12*3)) {datmp1= datmp1&0xff;datmp2= datmp2&0x0f; datmp3= datmp3&0x00;datmp4= datmp4&0x00;}
break;
}
default:
{
if(Temp==1||Temp==179)
{datmp1= datmp1&0xfc;datmp2=datmp2&0xff;datmp3=0xff;datmp4=0xff;}
if(minute>0||second>=0)
{
if(Temp==(3*minute-1) || Temp==(3*minute+1))
{datmp1= datmp1&0xff;datmp2= datmp2&0x01; datmp3= datmp3&0x00;datmp4= datmp4&0x00;}
if(Temp==(15*hour+minute/12*3-1)|| Temp==(15*hour+minute/12*3+1))
{datmp1= datmp1&0xff;datmp2= datmp2&0x1f; datmp3= datmp3&0x00;datmp4= datmp4&0x00;}
if(Temp==(15*hour+minute/12*3-2)|| Temp==(15*hour+minute/12*3+2))
{datmp1= datmp1&0xff;datmp2= datmp2&0x3f; datmp3= datmp3&0x00;datmp4= datmp4&0x00;}
}
if(minute==0)
{
if(Temp==(180-3*minute-1) || (Temp==3*minute+1))
{datmp1= datmp1&0xff;datmp2= datmp2&0x01; datmp3= datmp3&0x00;datmp4= datmp4&0x00;}
}
if(hour==0)
{
if(Temp==(180-15*hour+minute/12*3)-1|| Temp==(15*hour+minute/12*3+1))
{datmp1= datmp1&0xff;datmp2= datmp2&0x1f; datmp3= datmp3&0x00;datmp4= datmp4&0x00;}
if(Temp==(180-15*hour+minute/12*3)-2|| Temp==(15*hour+minute/12*3+2))
{datmp1= datmp1&0xff;datmp2= datmp2&0x3f; datmp3= datmp3&0x00;datmp4= datmp4&0x00;}
}
break;
}
}
dat1=datmp1;dat2=datmp2;dat3=datmp3;dat4=datmp4;
//dat5=pov_hz[line_now][0]; dat6=pov_hz[line_now][1];
}
Send_data_A(dat2);
Send_data_B(dat1);
Send_data_C(dat5,dat6);
dis_open();
P0=dat4;
LED1=0;
LED0=0;
LED2=0;
delay();
//LED0=1;
LED1=1;
LED2=1;
P0=P2=0xff;
dis_close();
}
}
/**********************************************************************************************
函数名:数字模式函数
调 用:无
参 数:无
返回值:无
结 果:
备 注:
/*********************************************************************************************/
void display_figure()
{
uchar temp ,i;
time_refurbish();
if(Time0_flag)
{
Time0_flag=0;
temp=T0_S-1;
dis_close();
if(temp<=180)
{
if(temp>=L||((wordaddr_start-(2*temp))>=wordaddr_end))
{
dat6=0xff; //从EEPROM读
dat5=0xff; //从EEPROM读
}
else
{
dat6=~IapReadByte(wordaddr_start-(2*temp)); //从EEPROM读
dat5=~IapReadByte(wordaddr_start-(2*temp-1)); //从EEPROM读
}
i++;
if(i>179)
{
i=0;
L++;
wordaddr_start+=2; //开始地址移位,控制流动的快慢
if(wordaddr_start>wordaddr_end+360)
{L=0;
wordaddr_start=IapReadByte(0X0010)*256+IapReadByte(0X0011); //文字EEPROM结束地址
}
}
if((temp>=0)&&(temp<32)) //显示5678位置;
{ //OE_595=1;
//dat1=~zhifu[disp_time[(4-(T0_S/8))]][(7-(T0_S%8))*2];
//dat2=~zhifu[disp_time[(4-(T0_S/8))]][(7-(T0_S%8))*2+1];
dat1=~zhifu[disp_time[(5+(temp/8))]][((temp%8))*2];
dat2=~zhifu[disp_time[(5+(temp/8))]][((temp%8))*2+1];
dat3=0xff;dat4=0xff;
//display(dat1,dat2,dat3,dat4); display_CLR();
}
else if((temp>147)&&(temp<180))// 显示1234位置;
{
dat1=~zhifu[disp_time[(4-(179-temp)/8)]][(7-((179-temp)%8))*2];
dat2=~zhifu[disp_time[(4-(179-temp)/8)]][(7-((179-temp)%8))*2+1];
dat3=0xff;dat4=0xff;
//display(dat1,dat2,dat3,dat4); display_CLR();
}
else if((temp>57)&&(temp<90)) //显示9、10、11、12位置;
{
dat2=~zhifu1[disp_time[(12-((89-temp)/8))]][(((89-temp)%8))*2];
dat1=~zhifu1[disp_time[(12-((89-temp)/8))]][(((89-temp)%8))*2+1];
dat3=0xff;dat4=0xff;
//display(dat1,dat2,0xdf,dat4); display_CLR();
}
else if((temp>=90)&&(temp<122))//显示13、14、15、16位置;
{
dat2=~zhifu1[disp_time[(16-((121-temp)/8))]][(((121-temp)%8))*2]; //必须拆分成两个32字符
dat1=~zhifu1[disp_time[(16-((121-temp)/8))]][(((121-temp)%8))*2+1];
dat3=0xff;dat4=0xff;
//display(dat1,dat2,0xdf,dat4); display_CLR();
}
else if((temp>=32)&&(temp<=57))//显示13、14、15、16位置;
{
dat1=0xff;dat2=0xff;dat3=0xff;dat4=0xff;
//display(0xff,0xff,0xdf,0xff); display_CLR();
}
else if((temp>=122)&&(temp<=147))//显示13、14、15、16位置;
{
dat1=0xff;dat2=0xff;dat3=0xff;dat4=0xff;
//display(0xff,0xff,0xdf,0xff); display_CLR();
}
//dat5=pov_hz[line_now][0]; dat6=pov_hz[line_now][1];
LED0=0;LED2=0;
Send_data_A(dat2);
Send_data_B(dat1);
Send_data_C(dat5,dat6);
dis_open();
P0=dat4;
delay();
LED0=1; LED2=1;
P0=P2=0xff;
dis_close();
}
}
}
/****************************************************************
函数名:定时器1中断服务函数 中断号:3 ;
调 用:无
参 数:无
返回值:无
功 能:提供显示位置所需的时间;
备 注:定时器1,工作方式0,16位自动重装载 1T模式,
/****************************************************************/
void timer1(void) interrupt 3 using 2
{
uint s,j,k,n,c;
//xdata uchar i;
s++; irtime++; j++; n++;c++; // second,minute,hour
//if(j==2) {j=0; pic_ch_flag=1;} //动画刷屏时间
//if(n==600){n=0; pic_red_ok=1; }
if(j == 10 ) {j=0; pic_ch_flag=1;
} //图片刷新间隔时间
if(n == 64000 ) {n=0; pic_red_ok=1; }
if(c == 600 )
{
c=0;
time_refurbish_flag=1;
//picaddr=0x050b;
//for(i=0;i<180;i++)
//line_vol++;
/* if(flag_hz)
{
flag_hz=0;
for(i=0;i<180;i++)
{
pov_hz[i][0]=pov_hz_1[i][0];
pov_hz[i][1]=pov_hz_1[i][1];
}
LED2=0;LED0=0;
}
*/
if(line_vol>179)
{
line_vol=0;
}
//if((flag_hz==1)&&(flag_hz_qh==1))
}
if(s==8000)
{
s=0;
speedsum=speed;
speed=0;
//second++;
k++;
//
/*if(k==5)
{
k=0;
PIC_MODE++;
if(PIC_MODE>3)
PIC_MODE=0;
}*/
if(speedsum>10)
{
speedsum=0;
open_close_flang=1;
TR0=1;
}
else {TR0=0;}
/*
if(k==5)
{
k=0;
ES=0;
CR=0;
}
*/
if(second==60)
{
second=0;
//minute++;
if(minute==60)
{
minute=0;
// hour++;
if(hour>11)
{
hour=0;
}
}
}
}
}