多功能调光台灯详解
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
吉林大学
电子设计大赛豪华版调光台灯
申报者信息:
队员1:
队员2:
队员3:
第一章引言
引言:LED 是利用电子移动来发光,直接把电能转换成光能。
因此,LED 的耗电量仅为传统灯泡的1/10,使用寿命却100 倍于传统灯泡。
本设
计应“环保、节能”的主题,切合了当代学生的需求,采用低功耗单片机mega16 做系统的控制,LED可根据外界光强自动调光,也可根据人体感应自动关闭或打开,同时可旋钮控制。
旋钮是用电位器做成旋转就可以调节光的亮度。
LED 的亮度是通过单片机输出不同的PWM 波控制开关电源的输出电压,使LED 两端电压改变来完成的。
PWM占空比不一样,则电压的平均值不一样。
同时考虑到实用性,我们又在系统中加入了ds1302时钟模块,使之具有了走时,设定闹钟的功能。
第二章方案选择
1、以高亮度LED 为光源,以mega16单片机为主控芯片;
2、系统分为控制模块,时钟模块,1602显示模块,感光模块,人体感应
模块,各模块分别实现各自功能。
3、时钟模块上有五个按键,按键1,2,3,4,5,分别实现时钟模块的调时,定时闹钟功能。
4、主机也含有转换开关,通过拨动开关可以切换感应模式和调光模式。
1.主控IC芯片的选择
方案一:采用51系列芯片。
市场上流通很多种类的单片机,在性能上一般都可以达到要求,例如stc89C51、AT89C52等都可以用于控制感光灯,它们的缺点在于没有内置的AD转换,同时在控制速度方面也不是很理想,总体性价比较低。
方案二:采用AVR系列芯片。
AVR单片机比51系列有更快的指令执行速度;程序存储器与数据存储器有分开的总线;内置上电复位电路和看门狗电路,在提高产品可靠性的同时降低了电路的成本;并且mega16内含AD转换减少了外围电路的复杂程度,节省了空间。
并且通过定时器设置可直接输出PWM波。
因此本此设计采用ATmega16作为主控制芯片
2、LED 亮度控制方案
LED 的亮度和流过LED 的电流成正比,改变LED 的电流大小来改
变LED 的亮度。
方案一:该方案是通过改变和LED 串联的电阻的阻值来改变流过
LED 的电流。
电源提供恒压给串联的LED 和可调电阻,当改变电阻值时,电流变化导致亮度的变化。
可调电阻采用DAC,可程控改变阻值。
方案二:该方案是通过改变LED 两端的电压来改变LED 的电流。
将恒压做成可变的电压,当输入电压改变,则流过LED 的电流也变化。
方案论述:方案一提供的恒压,LED 亮度不高时,大部分能量消
耗在电阻上。
方案二,输入的电压和LED 的亮度成正比,通过优化电源,可以达到耗能小。
综上所述,采用方案二。
第三章总体设计
图1
第四章单元电路设计
1.主控芯片的选择——Atmega16的特点
•高性能,低功耗的AVR 8位单片机
• 先进的RISC架构
– 131条强大的指令——大多都可单周期执行
– 32 x 8通用工作寄存器
–全静态操作
–16MHz下高达16 MIPS的吞吐量
–片上2周期乘法器
•强耐力费易失存储单元
– 16KB在系统自我编程的flash程序存储
– 512B的EEPROM
– 1KB的内部SRAM
–写/擦寿命:10,000 次Flash/100,000次EEPROM
–数据保持: 85℃下20年/25℃下100年
–带有独立锁位的可选启动代码单元
•使用片上启动程序实现在系统编程
•真正的同时读写操作
–为软件安全设计的可编程锁位
• JTAG (兼容IEEE 1149.1) 接口
–依据JTAG标准的边界扫描兼容性
–扩展的片上调试支持
–通过JTAG接口进行Flash, EEPROM, 熔丝位和锁位的编程
• 外设特点
–两个带有分离与预定标器和比较模式的8位定时/计数器
–一个带有分离预定标器、比较模式和捕捉模块的16位定时/计数器–带有分离晶振的实时计数器
–四个PWM 通道
– 10位8通道ADC
-8个单端通道
-7的差分通道(仅限于TQFP封装)
- 2个带可编程增益(1x, 10x, 200x)的差分通道
–基于字节的双线串口接口
–可编程串行USART
–主/从SPI串行接口
–有独立片上振荡源的可编程看门狗定时器
–片上模拟比较器
• 特有的特性
–上电复位和可编程掉电检测
–内部已校准的RC振荡器
–内部和外部中断源
–六个睡眠模式:空闲,ADC噪声衰减,节能,关机,待命和扩展待命
• I/O和封装
– 32个可编程I/O口
– 40-pin PDIP, 44-lead TQFP和44-pad QFN/MLF
• 运行电压
– 2.7 - 5.5V
• 速度等级
– 0 - 16 MHz
•能耗(在1 MHz, 3V, 25℃下)
–正常工作模式: 0.6 mA
–空闲模式: 0.2 mA
–断点模式: < 1μA
总体来看,ATmega16是一款性价比很高的单片机,能够胜任很广领域的控制需求。
控制模块电路图如下
图2
2.ds1302时钟模块
图3
各引脚的功能
Vcc1:主电源;Vcc2:备份电源。
当Vcc2>Vcc1+0.2V时,由Vcc2向DS1302供电,当Vcc2< Vcc1时,由Vcc1向DS1302供电。
SCLK:串行时钟,输入;I/O:三线接口时的双向数据线;CE:输入信号,在读、写数据期间,必须为高。
该引脚有两个功能:第一,CE开始控制字访问移位寄存器的控制逻辑;其次,CE提供结束单
字节或多字节数据传输的方法。
其主要性能如下
实时时钟具有能计算2100 年之前的秒分时日日期星期月年的能力还有闰年调整的能力
31 8 位暂存数据存储RAM
串行I/O 口方式使得管脚数量最少
宽范围工作电压2.0 5.5V
工作电流2.0V 时,小于300nA
读/写时钟或RAM 数据时有两种传送方式单字节传送和多字节传送字符组方式8 脚DIP 封装或可选的8 脚SOIC 封装根据表面装配
简单3 线接口
与TTL 兼容Vcc=5V
可选工业级温度范围-40 +85
与DS1202 兼容
在DS1202 基础上增加的特性
对Vcc1 有可选的涓流充电能力
双电源管用于主电源和备份电源供应
备份电源管脚可由电池或大容量电容输入
附加的7 字节暂存存储器
寄存器如下
图4 电路图如下
图5
3.感应模块
开关选择通过人体感应模块选择感应人体还是通过霍尔开关感应灯头是否张开
1)人体感应模块
人体感应模块与感光模块我们采用的是现成的模块使用,过程中我们学习了这些模块的使用方法······
1.全自动感应:当有人进入其感应范围则输入高电平,人离开感应范围则自动延时关闭高电平。
输出低电平。
2.光敏控制(可选):模块预留有位置,可设置光敏控制,白天或光线强时不感应。
光敏控制为可选功能,出厂时未安装光敏电阻。
3.两种触发方式:L不可重复,H可重复。
可跳线选择,默认为H。
A.不可重复触发方式:即感应输出高电平后,延时时间一结束,输出将自动从高电平变为低电平。
B.可重复触发方式:即感应输出高电平后,在延时时间段内,如果有人体在其感应范围内活动,其输出将一直保持高电平,直到人离开后才延时将高电平变为低电平(感应模块检测到人体的每一次活动后会自动顺延一个延时时间段,并且以最后一次活动的时间为延时时间的起始点)。
4.具有感应封锁时间(默认设置:0.2秒):感应模块在每一次感应输出后(高电平变为低电平),可以紧跟着设置一个封锁时间,在此时间段内感应器不接收任何感应信号。
此功能可以实现(感应输出时间和封锁时间)两者的间隔工作,可应用于间隔探测产品;同时此功能可有效抑制负载切换过程中产生的各种干扰。
5.工作电压范围宽:默认工作电压DC5V至20V
6.微功耗:静态电流65微安,特别适合干电池供电的电器产品。
7.输出高电平信号:可方便与各类电路实现对接。
其电路图如下
图6
2)霍尔开关
我们选择了霍尔开关作为感应灯头开合的传感器,灯头打开,磁铁离开霍尔开关,霍尔开关输出高电平;灯头闭合,磁铁靠近霍尔开关,霍尔开关输出低电平;这种模式正与人体感应模块相契合,所以程序上无需做改动,硬件上也十分简单。
使用十分方便。
4.控光模块
开关选择通过光敏模块模块自动控光还是通过电位器人为控光,控光模块或电位器输出的电压输入到单片机上,通过AD转换决定PWM波的占空比
1)光敏模块
(1)原理图如下
图7
(2)模块功能
1 可以检测周围环境的亮度和光强
2 灵敏度可调(图中蓝色数字电位器调节)
4 工作电压3.3V-5V
5 输出形式 a 模拟量电压输出
b 数字开关量输出(0和1)
6 设有固定螺栓孔,方便安装
7 小板PCB尺寸:3cm * 1.6cm
8 电源指示灯(红色)和数字开关量输出指示灯(绿色)
9 比较器采用LM393芯片,工作稳定
(3)小板接口说明(4线制)
1 VCC 外接3.3V-5V
2 GND 外接GND
3 DO 小板数字量输出接口(0和1)
4 AO 小板模拟量输出接口
2)电位器
电位器输出端通过开关接到单片机的PA0口,通过调节电位器来改变输入到单片机的电压,电位器两端接电容用来滤波,以防灯闪。
电路图如下
图8
5.测温模块
测温模块我们采用了ds18b20 体积小,使用方便
其特性如下
1.1、适应电压范围更宽,电压范围:3.0~5.5V,在寄生电源方式下可由数据线供电
1.2、独特的单线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实现微处理
器与DS18B20的双向通讯
1.3、DS18B20支持多点组网功能,多个DS18B20可以并联在唯一的三线上,实现组网多点测温 1.4、DS18B20在使用中不需要任何外围元件,全部传感元件及转换电路集成在形如一只三极管的集成电路内
1.5、温范围-55℃~+125℃,在-10~+85℃时精度为±0.5℃
1.6、可编程的分辨率为9~12位,对应的可分辨温度分别为0.5℃、0.25℃、0.125℃和
0.0625℃,可实现高精度测温
1.7、在9位分辨率时最多在93.75ms内把温度转换为数字,12位分辨率时最多在750ms内把温度值转换为数字,速度更快
1.8、测量结果直接输出数字温度信号,以"一线总线"串行传送给CPU,同时可传送CRC校验码,具有极强的抗干扰纠错能力
1.9、负压特性:电源极性接反时,芯片不会因发热而烧毁,但不能正常工作。
2、DS18B20的外形和内部结构
DS18B20内部结构主要由四部分组成:64位光刻ROM、温度传感器、非挥发的温度报警触发器TH和TL、配置寄存器。
DS18B20的外形及管脚排列如下图1: DS18B20引脚定义: (1)DQ 为数字信号输入/输出端; (2)GND为电源地;
(3)VDD为外接供电电源输入端(在寄生电源接线方式时接地)。
电路图如下
图9
6.显示模块
需要显示时间、温度,需要显示内容不是很多且没有汉字,1602价格优廉,使用方便,完全可以实现上述功能
电路图如下
图10
7.电源模块
此处电源采用的是废旧矿灯拆卸下来的锂电池,容量大,供电持久,适合作为台灯电源。
管理模块采用的是移动电源的电源管理模块,优点如下:
1,智能管理移动电源电路,单片机控制整个电路工作。
操作十分简单。
2,充放电电流大,充电输入电流高达1.6A,5200mA大容量锂电池仅需4-6H;对手机,平板等充电电流大:5V/1.5A,短时间可输出2A(实际输出电流取决于模块的带载能力和负载需要电流中较小的,也就是瓶颈)。
3.带电池反接保护功能,当电池接反时反接保护电路启动,把电池的反接能量充分释放,从而避免烧毁电路板的可能;接反时发现引线发热及时断开,更正极性后即可使用。
4.静态电流特别低,在输出灯不亮,无输出时,静态电流仅0.01mA;几乎可以算得上无电流。
5.带LED应急照明系统,户外活动就能感到它的可贵.
6.本模块的锂电保护功能,在电池电压低于3时会自动关闭输出,保护电池,但为了保证大家紧急情况下应急照明,所以应急照明不会被关闭。
面板按钮功能描述:1,单按开关:打开输出,并显示电池电量3秒,如果2分钟左右内没接负载输出会自动关闭。
2.连按按钮2次:应急照明LED灯打开,再连按2次关闭。
3.长按面板按钮:关闭移动电源,进入待机状态。
电量显示灯指示:1,一个蓝灯闪烁,表示电池电量剩余25%以下,电池电量不足,请尽快充电。
2.一个蓝灯常亮:表示电池电量剩余25%-50%。
3.两个蓝灯常亮:表示电池电量剩余50%-75%。
4.三个蓝灯常亮:表示电池电量剩余75%-100%。
5.四个蓝灯常亮:表示电池电量剩余100%。
6. 0-3个蓝灯常亮其它蓝灯轮流闪烁,常亮的灯显示充电进度。
7.充电时4个蓝灯常亮:表示电池充饱,充电结束。
第五章单元电路测试
1.调光测试
将灯头、单片机连接在一起,通过编程使单片机引脚输出占空比变化的PWM波,观察灯头亮度,亮度可以完美变化。
2.AD测试
将电位器、单片机连接在一起,通过编程使单片机引脚输出的PWM波占空比随AD转
换结果变化,观察示波器波形,占空比可以完美变化。
3.显示测试
将1602、单片机连接在一起,通过编程使1602显示123……,abc……,可以完美显示。
4.时钟测试
将DS1302、1602、按键、单片机连接在一起,写入程序,使按键可以调整时间,单片机可以读出时间并显示在1602上。
该功能可以实现,但发现时钟偶尔在重新上电时停止走时,经检测发现ds1302的32k 晶振不起振,需要更换晶振才可以正常走时。
猜测可能是晶振所需电容与ds1302内置电容不匹配造成。
但因为时间问题该问题没有得到很好解决,不过台灯做好后一般情况下不需要再重新上电,所以并不影响使用。
5.两路闹钟测试
设定好两路时钟,观察在到达改时间时闹钟是否会响,该功能可以完美实现。
5.测温模块测试
将DS18b20、1602与单片机连在一起,写入程序,测温可以完美实现。
6.感应模块测试
将人体感应模块、霍尔开关通过一个开关与单片机连在一起,灯头与单片机连在一起,观察灯是否在相应情况下打开。
经过观察,霍尔开关模式运行完美,灯头张开即亮,闭合即灭。
人体感应模块由于其局限性,不能探测静止的人,人必须在其探测到人之后的延时时间里有动作,不然该灯就会关闭。
不过可以延长其延时时间,人一般不会在很长时间里都没有任何动作,这样就可以使灯在有人时常亮。
当然,当人离去时,灯也需要同样长的延时时间才会熄灭,这是人体感应模式的缺点。
7.电源模块测试
电源模块只需要测试能否正常充电放电。
经检测电源可以正常给系统供电,也可以正常充电,也可以正常给手机等设备供电。
第六章整体测试
先将各部分程序整合到一起。
主程序中只有不断检测按键的函数,用来调整时间和设置闹钟,AD转换,时间、温度的获取和显示,感应控制灯的开关等都写在中断中。
也许因为限于作者编程能力,总体程序显得比较复杂(不过原理很简单),也限于作者word应用能力,程序在此就不用流程图赘述,源程序见附件。
将程序写入单片机后,将所有部件连接起来,经过程序和硬件上的多次调试后,所有功能顺利实现。
最后将部件装配成整体,做成一台完整的台灯。
第七章结论
这是作者第一次做这样精细和复杂的作品,过程中出现不少问题和挫折,不过经过努力大部分都已经解决。
余下最后两个问题在第五章已经列出,作者限于知识水平和时间,目前还没有解决。
另外,作者本希望在作品中加入红外遥控功能,可以通过红外遥控器来开启或关闭台灯并且调光,甚至可以用遥控器调整时间和闹钟,并可以用遥控器来关闭闹钟。
可惜由于时间问题未能实现。
希望可以在以后的学习中不断改进改进。
第八章附录(源程序)
(限于篇幅各部分驱动包并未列出)
Main.c
#include <iom16v.h>
#include <macros.h>
#include "LCD1602.h"
#include "Delay.h"
#include "ad.h"
#include "DS1302_drive.h"
#include "DS18B20_drive.h"
#define uchar unsigned char
#define uint unsigned int
uchar i=0; //ad 数据
uchar x=0; //按键中断用
uchar z=0; //判断该调哪位时间
uchar q=0; //判断该调闹钟哪一位
uchar a=0; //判断是否进入闹钟1
uchar b=0; //判断是否进入闹钟2
uchar y=0; //限制年个位
uchar m=0; //限制分个位
uchar mm=0; //限制闹钟1分个位
uchar mmm=0; //限制闹钟2分个位
uchar disp_buf[18] ={0x00}; //定义显示缓冲区
uchar disp_buf2[6] ={0x00}; //闹钟1
uchar disp_buf1[5] ={0x00}; //闹钟2
uchar time_buf[7] ={0,0,0,0,0,0,0}; //DS1302时间缓冲区,存放秒、分、时、日、月、星期、年
uchar temp [2]={0}; //用来存放设置时的小时、分钟的中间值
uchar alarm[2]={0}; //闹钟1 时、分
uchar alarm1[2]={0}; //闹钟2 时、分
uchar temp_data[2] = {0x00,0x00}; //用来存放温度高8位和低8位uchar tdisp_buf[5] ={0x00,0x00,0x00,0x00,0x00};//显示缓冲区
void PORT_init(void) {
PORTA = 0x7f;
DDRA = 0x80;
PORTB = 0x00;
DDRB = 0xff;
PORTC = 0x00;
DDRC = 0x07;
PORTD = 0x84;
DDRD = 0x80;
}
void beep0(void)
{
PORTA=PORTA|0x80;
Delay_xms(100);
PORTA=PORTA&0x7f;
}
void beep(void)
{
uchar k;
for(k=0;k<4;k++)
{
PORTA=PORTA|0x80;
Delay_xms(100);
PORTA=PORTA&0x7f;
Delay_xms(100);
}
Delay_xms(500);
}
void conv(uchar hour,uchar min,uchar sec,uchar year,uchar mon,uchar day,uchar week)
{
disp_buf[0] = hour/10; // 小时十位
disp_buf[1] = hour%10; // 小时个位
disp_buf[2] = ':'; // 显示":"
disp_buf[3] = min/10; // 分钟十位
disp_buf[4] = min%10; // 分钟个位
disp_buf[5] = ':'; // 显示":"
disp_buf[6] = sec/10; // 秒十位
disp_buf[7] = sec%10; // 秒个位
disp_buf[8] = year/10;
disp_buf[9] = year%10;
disp_buf[10] = '.';
disp_buf[11] = mon/10;
disp_buf[12] = mon%10;
disp_buf[13] = '.';
disp_buf[14] = day/10;
disp_buf[15] = day%10;
disp_buf[16] = '';
disp_buf[17] = week;
}
void displaytime(void)
{
LCD_write_char(0,1,'0'+disp_buf[0]);
LCD_write_char(1,1,'0'+disp_buf[1]);
LCD_write_char(2,1, disp_buf[2]);
LCD_write_char(3,1,'0'+disp_buf[3]);
LCD_write_char(4,1,'0'+disp_buf[4]);
LCD_write_char(5,1,disp_buf[5]);
LCD_write_char(6,1,'0'+disp_buf[6]);
LCD_write_char(7,1,'0'+disp_buf[7]);
LCD_write_char(1,0,'0'+2);
LCD_write_char(2,0,'0'+0);
LCD_write_char(3,0,'0'+disp_buf[8]);
LCD_write_char(4,0,'0'+disp_buf[9]);
LCD_write_char(5,0,disp_buf[10]);
LCD_write_char(6,0,'0'+disp_buf[11]);
LCD_write_char(7,0,'0'+disp_buf[12]);
LCD_write_char(8,0,disp_buf[13]);
LCD_write_char(9,0,'0'+disp_buf[14]);
LCD_write_char(10,0,'0'+disp_buf[15]);
switch(disp_buf[17])
{
case
0:LCD_write_char(12,0,'M');LCD_write_char(13,0,'o');LCD_write_ char(14,0,'n');break;
case
1:LCD_write_char(12,0,'T');LCD_write_char(13,0,'u');LCD_write_ char(14,0,'e');break;
case
2:LCD_write_char(12,0,'W');LCD_write_char(13,0,'e');LCD_write_ char(14,0,'d');break;
case
3:LCD_write_char(12,0,'T');LCD_write_char(13,0,'h');LCD_write_ char(14,0,'u');break;
case
4:LCD_write_char(12,0,'F');LCD_write_char(13,0,'r');LCD_write_ char(14,0,'i');break;
case
5:LCD_write_char(12,0,'S');LCD_write_char(13,0,'a');LCD_write_ char(14,0,'t');break;
case
6:LCD_write_char(12,0,'S');LCD_write_char(13,0,'u');LCD_write_ char(14,0,'n');break;
}
}
/********读取时间函数,负责读取当前的时间,并将读取到的时间转换为10进制数********/
void get_time(void)
{
uchar sec,min,hour,day,mon,year,week; //定义秒、分和小时变量
write_ds1302(0x8e,0x00); //控制命令,WP=0,允许写操作
write_ds1302(0x90,0xa6); //涓流充电控制
sec=read_ds1302(0x81); //读取秒
min=read_ds1302(0x83); //读取分
hour=read_ds1302(0x85); //读取时
day=read_ds1302(0x87);
mon=read_ds1302(0x89);
year=read_ds1302(0x8d);
week=read_ds1302(0x8b);
time_buf[0]=sec/16*10+sec%16; //将读取到的16进制数转化为10进制
time_buf[1]=min/16*10+min%16; //将读取到的16进制数转化为10进制
time_buf[2]=hour/16*10+hour%16;
time_buf[3]=day/16*10+day%16; //将读取到的16进制数转化为10进制
time_buf[4]=mon/16*10+mon%16;
time_buf[5]=week/16*10+week%16;
time_buf[6]=year/16*10+year%16;
}
void displaytem(void)
{
//LCD_write_char(9,1,'0'+tdisp_buf[3]); //百位
LCD_write_char(10,1,'0'+tdisp_buf[2]);
LCD_write_char(11,1,'0'+tdisp_buf[1]);
LCD_write_char(12,1,'.');
LCD_write_char(13,1,'0'+tdisp_buf[0]);
LCD_write_char(14,1,0xdf);
LCD_write_char(15,1,'C');
}
/********读取温度值函数********/
void GetTemperture(void)
{
uchar q;
Init_DS18B20(); //DS18B20初始化
if(yes0==0) //若yes0为0,说明DS18B20正常
{
WriteOneByte(0xCC); // 跳过读序号列号的操作
WriteOneByte(0x44); // 启动温度转换
for(q=0;q<10;q++){displaytem();}//调用显示函数延时,等待A/D转换结束,分辨率为12位时需延时750ms以上
Init_DS18B20();
WriteOneByte(0xCC); //跳过读序号列号的操作
WriteOneByte(0xBE); //读取温度寄存器
temp_data[0] = ReadOneByte(); //温度低8位
temp_data[1] = ReadOneByte(); //温度高8位
}
//else {beep();} //若DS18B20不正常,蜂鸣器报警
}
/********温度数据转换函数,将温度数据转换为适合LED数码管显示的数据********/
void TempConv(void)
{
uchar temp; //定义温度数据暂存
temp=temp_data[0]&0x0f; //取出低4位的小数
tdisp_buf[0]= (temp *10/16); //求出小数位的值
temp=((temp_data[0]&0xf0)>>4)|((temp_data[1]&0x0f)<<4);// temp_data[0]高4位与temp_data[1]低4位组合成1字节整数
tdisp_buf[3]=temp/100; //分离出整数部分的百位
temp=temp%100; //十位和个位部分存放在temp
tdisp_buf[2]=temp/10; //分离出整数部分十位
tdisp_buf[1]=temp%10; //个位部分
/*
if(!tdisp_buf[3]) //若百位为0时,不显示百位,seg_data[]表的第10位为熄灭符
{
tdisp_buf[3]=10;
if(!tdisp_buf[2]) //若十高位为0,不显示十位
tdisp_buf[2]=10;
}
*/
}
void KeyProcess(void)
{
uchar hour16,min16,sec16,year16,mon16,day16,week16; if((PINA&0x10)==0)
Delay_xms(10);
if((PINA&0x10)==0)
{
while(!(PINA&0x10));
beep0();
z=z+1;
}
switch(z)
{
case 0:LCD_SET_XY(3,0);break; //年十位
case 1:LCD_SET_XY(4,0);break; //年个位
case 2:LCD_SET_XY(7,0);break; //月个位
case 3:LCD_SET_XY(10,0);break; //日个位
case 4:LCD_SET_XY(12,0);break; //周
case 5:LCD_SET_XY(1,1);break; //时个位
case 6:LCD_SET_XY(3,1);break; //分十位
case 7:LCD_SET_XY(4,1);break; //分个位
default:z=0;
}
if((PINA&0x20)==0)
Delay_xms(10);
if((PINA&0x20)==0)
{
while(!(PINA&0x20));
beep0();
switch(z)
{
case 0: //年十位{
time_buf[6]=time_buf[6]+10;
if(time_buf[6]>=100)
{
time_buf[6]=time_buf[6]%10;
}
year16=time_buf[6]/10*16+time_buf[6]%10;
write_ds1302(0x8c,year16);
disp_buf[8]=disp_buf[8]+1;
if(disp_buf[8]>=10)
{
disp_buf[8]=0;
}
LCD_write_char(3,0,'0'+disp_buf[8]);
break;
}
case 1: //年个位{
time_buf[6]=time_buf[6]+1;
if(time_buf[6]>=(y/10+10))
{
time_buf[6]=y/10*10+time_buf[6]%10;
}
year16=time_buf[6]/10*16+time_buf[6]%10;
write_ds1302(0x8c,year16);
disp_buf[9]=disp_buf[9]+1;
if(disp_buf[9]>=10)
{
disp_buf[9]=0;
}
LCD_write_char(4,0,'0'+disp_buf[9]);
break;
}
case 2: //月{
time_buf[4]=time_buf[4]+1;
if(time_buf[4]>12)
{
time_buf[4]=0;
}
mon16=time_buf[4]/10*16+time_buf[4]%10;
write_ds1302(0x88,mon16);
disp_buf[11]=time_buf[4]/10;
disp_buf[12]=time_buf[4]%10;
LCD_write_char(6,0,'0'+disp_buf[11]);
LCD_write_char(7,0,'0'+disp_buf[12]);
break;
}
case 3: //日
{
time_buf[3]=time_buf[3]+1;
if(time_buf[3]>31)
{
time_buf[3]=0;
}
day16=time_buf[3]/10*16+time_buf[3]%10;
write_ds1302(0x86,day16);
disp_buf[14]=time_buf[3]/10;
disp_buf[15]=time_buf[3]%10;
LCD_write_char(9,0,'0'+disp_buf[14]);
LCD_write_char(10,0,'0'+disp_buf[15]);
break;
}
case 4: //周
{
time_buf[5]=time_buf[5]+1;
if(time_buf[5]>7)
{
time_buf[5]=0;
}
week16=time_buf[5];
write_ds1302(0x8a,week16);
switch(time_buf[5])
{
case
0:LCD_write_char(12,0,'M');LCD_write_char(13,0,'o');LCD_write_ char(14,0,'n');break;
case
1:LCD_write_char(12,0,'T');LCD_write_char(13,0,'u');LCD_write_ char(14,0,'e');break;
case
2:LCD_write_char(12,0,'W');LCD_write_char(13,0,'e');LCD_write_ char(14,0,'d');break;
case
3:LCD_write_char(12,0,'T');LCD_write_char(13,0,'h');LCD_write_ char(14,0,'u');break;
case
4:LCD_write_char(12,0,'F');LCD_write_char(13,0,'r');LCD_write_ char(14,0,'i');break;
case
5:LCD_write_char(12,0,'S');LCD_write_char(13,0,'a');LCD_write_ char(14,0,'t');break;
case
6:LCD_write_char(12,0,'S');LCD_write_char(13,0,'u');LCD_write_ char(14,0,'n');break;
}
break;
}
case 5: //时
{
time_buf[2]=time_buf[2]+1;
if(time_buf[2]>23)
{
time_buf[2]=0;
}
hour16=time_buf[2]/10*16+time_buf[2]%10;
write_ds1302(0x84,hour16);
disp_buf[0]=time_buf[2]/10;
disp_buf[1]=time_buf[2]%10;
LCD_write_char(0,1,'0'+disp_buf[0]);
LCD_write_char(1,1,'0'+disp_buf[1]);
break;
}
case 6: //分十位{
time_buf[1]=time_buf[1]+10;
if(time_buf[1]>=60)
{
time_buf[1]=time_buf[1]%10;
}
min16=time_buf[1]/10*16+time_buf[1]%10;
write_ds1302(0x82,min16);
disp_buf[3]=disp_buf[3]+1;
if(disp_buf[3]>=6)
{
disp_buf[3]=0;
}
LCD_write_char(3,1,'0'+disp_buf[3]);
break;
}
case 7: //分个位{
time_buf[1]=time_buf[1]+1;
if(time_buf[1]>=(m/10+10))
{
time_buf[1]=m/10*10+time_buf[1]%10;
}
min16=time_buf[1]/10*16+time_buf[1]%10;
write_ds1302(0x82,min16);
disp_buf[4]=disp_buf[4]+1;
if(disp_buf[4]>=10)
{
disp_buf[4]=0;
}
LCD_write_char(4,1,'0'+disp_buf[4]);
break;
}
}
}
if((PINA&0x40)==0)
Delay_xms(10);
if((PINA&0x40)==0) //退出
{
beep0();
LCD_init();
write_ds1302(0x80,0x00); //写秒寄存器
x=0;
}
}
void KeyProcess2(void) //调闹钟
{
if((PINA&0x10)==0)
Delay_xms(10);
if((PINA&0x10)==0)
{
while(!(PINA&0x10));
beep0();
q=q+1;
}
switch(q)
{
case 0:LCD_SET_XY(0,0);break; //a
case 1:LCD_SET_XY(7,0);break; //闹钟1时 case 2:LCD_SET_XY(9,0);break; //闹钟1分十位 case 3:LCD_SET_XY(10,0);break; //闹钟1分个位 case 4:LCD_SET_XY(0,1);break; //b
case 5:LCD_SET_XY(7,1);break; //闹钟2时 case 6:LCD_SET_XY(9,1);break; //闹钟2分十位 case 7:LCD_SET_XY(10,1);break; //闹钟2分个位
default:q=0;
}
if((PINA&0x20)==0)
Delay_xms(10);
if((PINA&0x20)==0)
{
while(!(PINA&0x20));
beep0();
switch(q)
{
case 0: //a {
a=a+1;
if(a>1)
{
a=0;
}
LCD_write_char(0,0,'0'+a);
break;
}
case 1: //闹钟1 时 {
alarm[0]=alarm[0]+1;
if(alarm[0]>23)
{
alarm[0]=0;
}
disp_buf2[0]=alarm[0]/10;
disp_buf2[1]=alarm[0]%10;
LCD_write_char(6,0,'0'+disp_buf2[0]);
LCD_write_char(7,0,'0'+disp_buf2[1]);
break;
}
case 2: //闹钟1 分十位 {
alarm[1]=alarm[1]+10;
if(alarm[1]>=60)
{
alarm[1]=alarm[1]%10;
}
disp_buf2[3]=disp_buf2[3]+1;
if(disp_buf2[3]>=6)
{
disp_buf2[3]=0;
}
LCD_write_char(9,0,'0'+disp_buf2[3]);
break;
}
case 3: //闹钟1 分个位 {
alarm[1]=alarm[1]+1;
if(alarm[1]>=(mm/10+10))
{
alarm[1]=mm/10*10+alarm[1]%10;
}
disp_buf2[4]=disp_buf2[4]+1;
if(disp_buf2[4]>=10)
{
disp_buf2[4]=0;
}
LCD_write_char(10,0,'0'+disp_buf2[4]);
break;
}
case 4: //b
{
b=b+1;
if(b>1)
{
b=0;
}
LCD_write_char(0,1,'0'+b);
break;
}
case 5: //闹钟2 时 {
alarm1[0]=alarm1[0]+1;
if(alarm1[0]>23)
{
alarm1[0]=0;
}
disp_buf1[0]=alarm1[0]/10;
disp_buf1[1]=alarm1[0]%10;
LCD_write_char(6,1,'0'+disp_buf1[0]);
LCD_write_char(7,1,'0'+disp_buf1[1]);
break;
}
case 6: //闹钟2 分十位 {
alarm1[1]=alarm1[1]+10;
if(alarm1[1]>=60)
{
alarm1[1]=alarm1[1]%10;
}
disp_buf1[3]=disp_buf1[3]+1;
if(disp_buf1[3]>=6)
{
disp_buf1[3]=0;
}
LCD_write_char(9,1,'0'+disp_buf1[3]);
break;
}
case 7: //闹钟2 分个位 {
alarm1[1]=alarm1[1]+1;
if(alarm1[1]>=(mmm/10+10))
{
alarm1[1]=mmm/10*10+alarm1[1]%10;
}
disp_buf1[4]=disp_buf1[4]+1;
if(disp_buf1[4]>=10)
{
disp_buf1[4]=0;
}
LCD_write_char(10,1,'0'+disp_buf1[4]);
break;
}
default:z=0;
}
}
if((PINA&0x40)==0)
Delay_xms(10);
if((PINA&0x40)==0) //退出
{
beep0();
LCD_init();
x=0;
}
}
void timer0_init(void)
{
SREG=0x80; //开总中断
TIMSK=0x01; //使能定时器0溢出中断
TCCR0=(1<<CS00)|(1<<CS01); //开启定时器0 不经过分频器
TCNT0=0;
}
void timer2_init(void)
{
TCCR2=(1<<CS20)|(1<<WGM21)|(1<<WGM20)|(1<<COM21)|(1<<COM20); // T/C2不经分频器开始计时快速PWM模式
//比较匹配时OC2置位,计数到TOP时OC2清
TCNT2=0;
OCR2=0;
SREG=0x80; //开总中断
}
void timer2_end(void)
{
TCCR2=(0<<CS20)|(0<<CS21)|(0<<CS22);
}
void interrupt_init (void)
{
SREG=0x80;
GICR=0x80; //使能INT0,INT1
MCUCR=(0<<ISC01)|(0<<ISC00)|(0<<ISC11)|(1<<ISC10); //INT0上升沿中断 INT1引脚任何电平变化都引发中断
}
void main(void)
{
Delay_xms(50);
PORT_init();
interrupt_init ();
LCD_init();
LCD_clear();
Delay_xms(50);
// timer0_init(); //定时器0 溢出中断用于显示
init_ds1302(); //DS1302初始化
PortDS18B20_Init();
//timer2_init();
//ad_init();
//adc_a0 ();
//adstart ();
i=0;
x=0;
z=0;
while(1)
{
if((PINA&0x04)==0) //调时钟
{
// TIMSK=0x00; //关闭定时器0溢出中断
beep0();
Write_Instruction(0x0F); //开显示,光标、闪烁均显示
Write_Instruction(0x02); //光标归位
x=1;
y=time_buf[6]; //限制年个位
m=time_buf[1]; //限制分个位
while(x)
{
KeyProcess();
}
// TIMSK=0x01; //使能定时器0溢出中断
}
if((PINA&0x08)==0) //调闹钟
{
// TIMSK=0x00; //关闭定时器0溢出中断
beep0();
disp_buf2[0]=alarm[0]/10;
disp_buf2[1]=alarm[0]%10;
disp_buf2[2]=':';
disp_buf2[3]=alarm[1]/10;
disp_buf2[4]=alarm[1]%10;
disp_buf1[0]=alarm1[0]/10;
disp_buf1[1]=alarm1[0]%10;
disp_buf1[2]=':';
disp_buf1[3]=alarm1[1]/10;
disp_buf1[4]=alarm1[1]%10;
Write_Instruction(0x0F); //开显示,光标、闪烁均显示
Write_Instruction(0x02); //光标归位
LCD_clear();
LCD_write_char(0,0,'0'+a);
LCD_write_char(6,0,'0'+disp_buf2[0]);
LCD_write_char(7,0,'0'+disp_buf2[1]);
LCD_write_char(8,0, disp_buf2[2]);
LCD_write_char(9,0,'0'+disp_buf2[3]);
LCD_write_char(10,0,'0'+disp_buf2[4]);
LCD_write_char(0,1,'0'+b);
LCD_write_char(6,1,'0'+disp_buf1[0]);
LCD_write_char(7,1,'0'+disp_buf1[1]);
LCD_write_char(8,1, disp_buf1[2]);
LCD_write_char(9,1,'0'+disp_buf1[3]);
LCD_write_char(10,1,'0'+disp_buf1[4]);
mm=alarm[1];
x=1;
while(x)
{
KeyProcess2();
}
//TIMSK=0x01; //使能定时器0溢出中断
}
get_time(); //读取当前时间
conv(time_buf[2],time_buf[1],time_buf[0],time_buf[6],time_buf[ 4],time_buf[3],time_buf[5]); //将DS1302的小时/分/秒传送到转换函数
displaytime();
GetTemperture(); //读取温度值
TempConv(); //将温度转换为适合LED数码管显示的数据 displaytem();
if(a==1) //闹钟1响铃
{
if(alarm[0]==time_buf[2])
{
if(alarm[1]==time_buf[1])
{
if(time_buf[0]==0)
{
LCD_clear();
LCD_write_char(0,0,'0'+a);
LCD_write_char(6,0,'0'+disp_buf2[0]);
LCD_write_char(7,0,'0'+disp_buf2[1]);
LCD_write_char(8,0, disp_buf2[2]);
LCD_write_char(9,0,'0'+disp_buf2[3]);
LCD_write_char(10,0,'0'+disp_buf2[4]);
while(PINA&0x40)
{
beep();
}
LCD_write_char(0,0,' ');
}
}
}
}
if(b==1) //闹钟2响铃
{
if(alarm1[0]==time_buf[2])
{
if(alarm1[1]==time_buf[1])
{
if(time_buf[0]==0)
{
LCD_clear();
LCD_write_char(0,1,'0'+b);
LCD_write_char(6,1,'0'+disp_buf1[0]); LCD_write_char(7,1,'0'+disp_buf1[1]);
LCD_write_char(8,1, disp_buf1[2]);
LCD_write_char(9,1,'0'+disp_buf1[3]);
LCD_write_char(10,1,'0'+disp_buf1[4]);
while(PINA&0x40)
{
beep();
}
LCD_write_char(0,1,' ');
LCD_write_char(8,1, ' ');
LCD_write_char(9,1,' ');
LCD_write_char(10,1,' ');
}
}
}
}
}
}
#pragma interrupt_handler adc:15
void adc(void) //ad转换完成中断
{
i=ADCH; //8位精度
OCR2=i;
//Display(i);
}
#pragma interrupt_handler display:10
void display(void) //定时器0溢出中断{
}
#pragma interrupt_handler renti1:3
void renti1(void) //外中断1 用于探测人体
{
beep0();
Delay_xms(10);
if(PIND&0x08)
{
timer2_init();
ad_init();
adc_a0 ();
adstart ();
}
if(!(PIND&0x08))
{
timer2_end();
ad_end();
PORTD=PORTD|0x80;
}
}
第九章参考文献
陈亮《成都理工大学“格州电子”杯电子设计大赛——红外调光灯设计》。