摘要

本文介绍了设计的框架结构和组成模块以及各模块的原理,介绍了各部分硬件设和各部分软件设计以及软件流程图。该设计是以 STC89C52 单片机为控制核心的集多种功能于一体的智能 LED 台灯。结合语音识别模块、A/D转换模块、红外感应模块以及按键系统等,来实现语音控制开关、光敏感应亮度自动调节、人体红外感应开关以及定时报警等功能。单片机控制就用 STC89C52,显示模块用 LCD1602 液晶显示计时时间、亮度等级等内容,按键模块用来调整时间计时功能、LED 的亮度等,台灯模块用LED,用蜂鸣器发出闹铃声,检测人的用BIS0001 红外热释传感器。软件设计用单片机 C 语言编写,实现了全部控制功能。

关键词智能台灯;语音识别;STC89C52RC单片机;

引言

随着社会的不断进步,科技的高速发展,传统使用的台灯只有照明的功能,满足不了现代人希望在良好的环境下学习的愿望。随着智能控制理论和人工智能研究的深入,家用电器因为单片机的加入而走向智能化,并且随着人们生活水平的提高日益走向平民化,我们的生活也随着家用电器的发展越来越方便、舒适。随着家用电器的发展,作为家用电器当中的小台灯也要顺应科技的发展步伐走向智能化。所以我们的台灯的设计主要包括两个目的:

一是具有智能语音识别功能,通过口头语言来控制开关灯,提高亮度,降低亮度,从而解放你的双手,进而可以把更多的精力投入到你的工作和学习当中。

二是具有智能人体检测功能,可以通过红外线检测人体是否坐在台灯面前;因为我们发现很多人都没有随手关台灯的习惯,所以当人体感应模块感应到室内没人的时候会在延时后自动关闭台灯。并当检测到有人进入房间时,自动开灯。这使得生活更轻松智能的同时,也更有利的节约能源和环保。

一、设计目标

本设计以 STC89C52 为主控制芯片,选用合适的传感器及调光技术,并进行软硬件的合理设计,进而实现台灯的智能化功能。台灯主要包括自动模式和手动模式两种控制方式。

    1. 自动模式

(1)检测周围是否有人(可以调节检测距离),若检测到没人,则台灯会在延时后(可调节时间的长短)自动关闭,若检测到周围有人,则台灯会在延时后(可调节时间的长短)自动打开,以实现节能的目的。

(2)可以根据周围环境明暗程度,自动调节台灯的亮度,使环境灯光亮度达到最适宜人眼的亮度,以达到保护视力的目的。

(3)检测人体距离桌面的距离,若人体距离台灯过近的话,台灯则会发出嘀嘀嘀的报警声,以提示人及时矫正坐姿。

1.2 手动模式

(1)手动控制台灯的启动,使用时手动打开。

(2)手动调节台灯亮度,可将灯光亮度调至舒适的亮度。

(3)用灯时间提醒功能,可以设定用灯时间,时间到峰鸣提醒。可以防止长时间用灯导致视力下降问题。

二、方案设计

针对以上对功能要求分析,该台灯以 STC89C52 单片机作为主控制芯片,分为手动和自动两个控制模式。在手动控制模式下,光照强度分出5个档,进而实现对台灯光照亮度的手动调节。在自动控制模式下,台灯通过红外人体探测器检测周围是否有人,如果系统检测到有人,则通过检测光敏电阻的分布电压值,间接测量外界光照强度。然后信号经模拟——数字信号转换芯片传给单片机,单片机对LED显示模块进行控制。单片机通过将测得的电压和内部预设的阈值进行对比,然后调整 PWM 的占空比,从而对 LED 灯的内部电流强度进行控制,实现光照亮度的自动调节。与此同时,可通过红外接近传感器检测人体距桌面的距离,同样与内部预设值进行比较,当检测值不在预设值范围之内时,蜂鸣器发出报嘀嘀嘀警声响,提醒使用者注意坐姿,从而防止近视。如果台灯周围没有人,则台灯会自动熄灭,减少能源的不必要的浪费。通过光敏电阻感应外界光照,进而改变电阻阻值达到自动调节台灯亮度的目的。

综上所述,硬件电路主要由STC89C52单片机微处理控制单元、LCD液晶显示模块、语音识别模块、按键控制、实时时钟电路、晶振电路、AD转换等模块化电路组成。如下图所示(图一)

系统结构的组成图(图一)

三、主要原件设计介绍

3.1 单片机STC89C52RC

STC89C52单片机具有低功耗、高性能以及价格低廉抗、干扰能力强等特点。它具有8K的在系统可编程Flash存储器。并且它还使用经典的MCS-51做内核,既可以当作一般的51单片机使用。同时又可以实现51单片机不能实现的功能,为很多嵌入式控制应用系统提供了灵活有效的解决方案。

STC89C52的主要功能具有:8k字节Flash,512字节RAM,32位I/O口线,看门狗定时器,内置4KBEEPROM,MAX810复位电路,三个16位定时器/计数器,一个6向量2级中断结构,全双工串行口。另外STC89C52可降至0Hz静态逻辑操作,支持2种软件可选择节电模式。空闲模式下,CPU停止工作,允许RAM、定时器/计数器、串口、中断继续工作。掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。最高运作频率35Mhz,6T/12T可选。外形及引脚排列如下所示(图二)

STC89C52外部引脚图(图二)

3.1.1 STC89C52端口介绍

VCC(40引脚):电源电压。 

VSS(20引脚):接地。 

P0端口(P0.0~P0.7,39~32引脚):P0口是一个漏极开路8位双向I/O口。作为输出端,每个引脚能驱动8个TTL负载电路,当端口P0写入“1”时,可以作为高阻抗输入。在访问外部程序或数据存储器时,P0口可以提供低8位地址和8位数据的复用总线。此时,P0口内部上拉电阻有效。在Flash ROM编程时,P0端口接收指令字节;然而在校验程序时,P0端口输出指令字节。验证时,要求外接上拉电阻。 

P1端口(P1.0~P1.7,1~8引脚):P1口是一个有内部上拉电阻的8位双向I/O端口。P1的输出缓冲器可驱动(吸收或输出电流方式)4个TTL输入。对端口写入1时,通过内部的上拉电阻把端口拉到高电位,这是可用作输入口。P1口作输入口使用,因为内部有上拉电阻,所以那些被外部拉低的引脚会输出一个电流。此外,P1.0和P1.1还可以作为定时器/计数器。

P2端口(P2.0~P2.7,21~28引脚):P2口是一个带有内部上拉电阻的8位双向I/O端口。P2的输出缓冲器可以驱动(吸收或输出电流方式)4个TTL输入。对端口写入“1”时,通过内部上拉电阻把端口拉到高电平,此时可用作输入口。P2作为输入口使用时,因为P2口内部有上拉电阻,那些被外部信号拉低的引脚会输出一个电流。   

P3端口(P3.0~P3.7,10~17引脚):P3是一个内部带上拉电阻的8位双向I/O端口。P3的输出缓冲器可驱动(以吸收或输出电流方式)4个TTL输入。对端口写入“1”时,通过内部的上拉电阻把端口拉到高电位,这时可用作输入口。P3做输入口使用时,因为有内部的上拉电阻,那些被外部信号拉低的引脚会输入一个电流。

 RST(9引脚):复位输入。当输入连续两个机器周期以上高电平时有效,用来完成单片机的复位初始化操作。看门狗计时完成后,RST引脚输出96个晶振周期的高电平。特殊寄存器AUXR(地址8EH)上的DISRTO位可以使此功能无效。DISRTO默认状态下,复位高电平有效。  

VPP(31引脚):访问外部程序存储器控制信号。为使能从0000H到FFFFH的外部程序存储器读取指令,必须接GND。注意加密方式1时,将内部锁定位RESET。为了执行内部程序指令,应该接VCC。在Flash编程期间,也接收12伏VPP电压。

XTAL1(19引脚):振荡器反相放大器和内部时钟发生电路的输入端。 

XTAL2(18引脚):振荡器反相放大器的输入端。

3.2红外人体感应模块

红外人体感应模块采用BISS0001芯片,这是一款高效传感信号处理集成电路芯片,通过内部运算放大器、电压比较器、状态控制器、延时定时器、封锁时间定时器通过一系列数模混合运算对信号进行处理。其引脚功能及内部框图如下图所示(图三):

    BIS0001引脚功能及内部框图(图三)

A(1引脚):可重复触发和不可重复触发选择端。当A为“1”时,允许重复触发;当A为“0”时,不可重复触发。 

VO(2引脚):控制信号输出端,由VS的上跳前沿触发,使VO输出从低电平跳变到高电平时视为有效触发。在输出延迟时间TX之外和VS没有上跳变时,VO保持低电平状态。 

RR1(3引脚):输出延迟时间TX的调节端。

RC1(4引脚):输出延迟时间TX的调节端。 

RC2(5引脚):触发封锁时间Ti的调节端。 

RR2(6引脚):触发封锁时间Ti的调节端。 

VSS(7引脚):工作电源负端。 

VRF(8引脚):参考电压及复位输入端,通常接VDD,当接“0”时可使定时器复位。 

VC(9引脚):触发禁止端,当VC<VR时禁止触发;当VC>VR时允许触发。 

IB(10引脚):运算放大器偏置电流设置端。 

VDD(11引脚):工作电源正端。

人体红外感应模块电路图如下图所示(图四):

   人体红外感应模块

3.2.1 人体红外感应模块具有的特点

  1. 全自动感应:人进入其感应范围则输出高电平,人离开感应范围则自动延时关闭高电平,输出低电平。
  2. 光敏控制(可选择,出厂时未设)可设置光敏控制,白天或光线强时不感应。
  3. 温度补偿(可选择,出厂时未设):在夏天当环境温度升高至 30~32℃,探测距离稍变 短,温度补偿可作一定的性能补偿。
  4. 两种触发方式:(可跳线选择) a、不可重复触发方式:即感应输出高电平后,延时时间段一结束,输出将自动从高电 平变成低电平; b、可重复触发方式:即感应输出高电平后,在延时时间段内,如果有人体在其感应 范围活动,其输出将一直保持高电平,直到人离开后才延时将高电平变为低电平(感应模块检 测到人体的每一次活动后会自动顺延一个延时时间段,并且以最后一次活动的时间为延时 时间的起始点)。
  5. 具有感应封锁时间(默认设置:2.5S 封锁时间):感应模块在每一次感应输出后(高电平变成低电平),可以紧跟着设置一个封锁时间段,在此时间段内感应器不接受任何感应信 号。此功能可以实现“感应输出时间”和“封锁时间”两者的间隔工作,可应用于间隔探测产 品;同时此功能可有效抑制负载切换过程中产生的各种干扰。(此时间可设置在零点几秒 —几十秒钟)。
  6. 工作电压范围宽:默认工作电压 DC4.5V-20V。
  7. 微功耗:静态电流<50 微安,特别适合干电池供电的自动控制产品。
  8. 输出高电平信号:可方便与各类电路实现对接。

3.3光敏感应的设计

光敏感应自动调节亮度的功能我们通过光敏电阻和AD/DA转换芯片PCF8591来实现。光敏电阻产生的模拟量通过PCF8591转换成模拟量输入到STC89C52RC中做相应的处理。

我们特意把光敏电阻放在台灯板上方,可以避免光敏电阻受台灯的光线影响。

3.4显示模块电路设计

显示模块我们选用LCD1602。LCD1602工作电压为3.3V或5V,显示对比度可调,内含复位电路,提供各种控制命令,如:清屏、字符闪烁与否、光标闪烁与否、显示移位方向等多种功,能有80字节的显示数据存储器DDRAM,1602内建有192个5X7点阵的字型的字符发生器CGROM和8个可由用户自己定义的5X7的字符发生器CGRAM。 

LCD1602具有微功耗、体积小、显示内容丰富、超薄轻巧,常被用于袖珍式仪表和低功耗应用系统中。LCD1602液晶显示连接电路如下图(图五):

LCD1602液晶显示连接电路图(图五)

3.5 按键模块设计

按键用来调整时间和设置闹钟,调光亮度,各键功能在介绍硬件部分已描述。因为有亮度和定时几个参数要调,所以设置这几个参数的计数。当按下一个键时,单片机进行相应的操作。当调光亮度时,按下 降低 键变暗,按下 身高 键变亮,这是通过调 PWM 实现的。 

五、软件设计

程序设计流程如下图所示:

多功能智能台灯流程图(图6)

程序说明:

(1)初始化设置:初始化液晶、按键、时钟、定时器、语音识别模块。

(2)红外人体检测:通过人体红外模块检测人体信号,如果检测到人,则对应的人体感应指示灯亮,否则灭。

(3)语音识别检测:语音识别模块检测到“开灯”语音信号,驱动单片机点亮台灯,检测“升高”语音信号,则把亮度调高,检测到“降低”语音信号则把亮度降低。

(4)调光模式设置:本作品台灯亮度调节有按键手动和自动调节模式,自动调节是通过光敏电阻采集外界环境光线亮度,A/D芯片转换为数字量,单片机调节占空比来实现自动调节,手动按键控制则不受外界环境影响。

(5)防近视:通过红外探头,如果人体靠近则驱动蜂鸣器报警提醒调整坐姿,达到预防近视目的。

以下是自动调节亮度的函数(通过光敏电阻采集外界环境光线亮度,A/D芯片转换为数字量,单片机调节占空比来实现自动调节,手动按键控制则不受外界环境影响)

//自动调节亮度子函数
void zd_guangmin()
{
 if((PWM_zd==0)&&(yuyin_mode==1))//如果检测到设置为自动模式并且开灯的状态下
 {
  delay_K(2);//延时消痘
  if((PWM_zd==0)&&(yuyin_mode==1))
  { 
   PWM_mode0=1;//进入自动调光模式
  }
 } 
 if(PWM_mode0==1)
 {
  adc_num=ReadADC(2);//AD转换通道为第二通道
  WriteDAC(adc_num);//读取AD转换光敏电阻的数值
  if((0<adc_num)&&(adc_num<=10))//根据阻值参数,变换PWM_T的值
   {
	PWM_T=0;zd_xs();			
   }
  if((10<adc_num)&&(adc_num<=100))//根据阻值参数,变换PWM_T的值
   {
	PWM_T=10;zd_xs();
   }
  if((100<adc_num)&&(adc_num<=120))//根据阻值参数,变换PWM_T的值
   {
	PWM_T=20;zd_xs();
   }
  if((120<adc_num)&&(adc_num<=135))//根据阻值参数,变换PWM_T的值
   {
    PWM_T=30;zd_xs();
   }
  if((135<adc_num)&&(adc_num<=160))//根据阻值参数,变换PWM_T的值
   {
    PWM_T=40;zd_xs();
   }
  if((160<adc_num)&&(adc_num<=200))//根据阻值参数,变换PWM_T的值
   {
    PWM_T=80;zd_xs();
   }
  if((200<adc_num)&&(adc_num<=254))//根据阻值参数,变换PWM_T的值
   {
    PWM_T=100;zd_xs();
   }
 }
 if(yuyin_mode==0)//如果语音识别关灯,则台灯灭

六、系统调试

6.1多功能智能台灯整体电路图

硬件组成:52单片机+光线检查模块+电压比较模块+人体感应模块+LED指示灯模块+按键+语言识别模块+电源模块+液晶显示模块,整体电路图如下图所示(图7):

多功能智能台灯整体电路图 (图7)

6.2台灯亮度调节测试

台灯的亮度调节可以通过光敏感应自动调节和手动调节这两种方式实现,光敏感应自动亮度调节是通过光敏电阻感应外界环境中光线强度来自动调节台灯亮度,使台灯产生合理的亮度。手动调节是通过按键来人为的是台灯产生人们想要达到的亮度。

调试步骤如下:

使用亮度挡位不同的手电筒关来照着光敏电阻,以测试光敏电阻是否会根据当前环境光的强弱来自动调节台灯的亮度

由上图亮度指数显示为1,说明台灯所处环境中光线较好,台灯产生的光线较暗。

当我们把手电筒灯调弱了之后时,亮度指数显示为4,台灯产生较强的光线。

当我们把手电筒灯稍微提高了一点时,亮度指数显示为3,台灯产生较强的光线。

七、结论

本文的设计实现了台灯的智能化和多功能化。采用微处理器来控制感应模块、显示模块、语音识别模块以及峰鸣提醒等多个模块,使我们日常生活中不可缺少的台灯相比传统台灯更加智能和多功能化,更加节能环保,使用更加方便简单。结论如下:

语音口令控制开关:避免了黑暗中摸不到开关的烦恼,也解决了其他声控开关只要有声音就开灯,从而造成资源浪费的麻烦。使得台灯的使用更加方便和智能化。

红外人体感应开关:感应到人离开房间,延时一段之间自动关灯。避免了人离开忘记关灯从而造成资源的浪费。使台灯在日常的使用中比传统台灯更加节约资源,减少浪费。

自动感应亮度调节和手动亮度调节:自动感应亮度调节可以根据环境中光强调节台灯照明亮度,使台灯时刻产生合理亮度,手动亮度调节可以通过人为调节控制使台灯保持合适亮度。两种亮度调节方式能够起到保护视力的作用。

台灯设有1602液晶显示屏和用灯时间提醒功能,1602显示屏可以显示亮度指数信息;用灯时间两个等级,可通过按键调节,时间到蜂鸣器提醒关灯。能够更好地对视力进行人性化的保护。

致谢

本论文是在张锐老师的悉心指导下完成的。在完成整个设计阶段,在完成整个设计阶段,张锐老师渊博细心的的指导和严谨的作风与要求使我获益良多。思想深邃,视野雄阔的精神氛围。平易近人的人格魅力对我影响深远。本论文从选题到完成,几易其稿,每一步都是在导师中肯的建议和悉心的指导下完成的,倾注了导师大量的心血,在此我向我的张锐导师表示深切的谢意!

同时,也要感谢我的小组成员们,感谢你们在学习和生活中给我的帮助!

参考文献

  1. 《基于AT89S52单片机控制的智能台灯设计》张仁朝、刘锦龙2011年06期ISSN:2095-073X
  2. 《C语言程序设计课程中的计算思维探析》汪红兵、姚琳、武航星、张敏2014年09期ISSN:1005-0450
  3. 《基于单片机设计的智能LED台灯设计》魏星、张锐、陈云翰、高鑫2015年20期ISSN:1673-9957
  4. 《多区域多控红外人体感应开关》李志、城吴宁2014年11期ISSN:1671-7597
  5. 《基于51单片机的智能台灯设计》郭鹏程、王新元、叶其忠2016年11期ISSN:1672-8289

6、《C语言程序设计》  蒋晶、赵卫滨、余永红  ISBN:9787121316760

7《51单片机综合学习系统——蜂鸣器、继电器篇》徐玮2007年12期ISSN:1006-5059

8《基于STM32和HC-SR501智能家居的智能照明系统设计》 王东、莫先2016年06期ISSN:1674-8425

9、《C语言程序设计教程》  王剑峰、马涛、刘浪  ISBN:9787516515556

程序

#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char 
#define uint unsigned int

#include "Ds1302.h"//时钟函数调用
#include "key.h"   //键盘函数调用
#include "Lcd.h"   //屏幕函数调用
#include "I2C.h"   //AD转换函数调用

typedef unsigned char BYTE;
typedef unsigned int WORD;
#define uint unsigned int 
#define uchar unsigned char 
#define RdCommand 0x01 //定义ISP的操作命令
#define PrgCommand 0x02
#define EraseCommand 0x03 
#define Error 1
#define Ok 0
#define WaitTime 0x01 //定义CPU的等待时间

sfr ISP_DATA=0xe2;  //寄存器申明
sfr ISP_ADDRH=0xe3;
sfr ISP_ADDRL=0xe4;
sfr ISP_CMD=0xe5;
sfr ISP_TRIG=0xe6;
sfr ISP_CONTR=0xe7;
char a_a; 

sbit RT_HW=P2^5;//人体红外检测模块管脚定义
sbit RT_HW_LED=P3^3;//人体红外信号采集指示灯管脚定义(检测到人亮,否则灭)

uchar k=0;   //键盘变量
uchar mode=0;//屏幕状态变量
uchar temp=0;//是否调整时钟变量
uchar nz_shi,nz_fen;//闹钟的时和分变量定义

sbit pwm_led=P2^6;//台灯接口
sbit alarm=P2^4;//蜂鸣器管脚定义
sbit Ce_Ju=P2^3;//防止近视模块管脚定义
uchar ZD_mode;   //自动模式状态参数
uchar SD_mode;	 //手动模式状态参数
uchar YY_mode;   //语音模式状态参数
int adc_num;     //ADC采集亮度参数
uchar ld_dc1;    //台灯自动控制亮度档次参数
uchar ld_dc2;    //台灯手动控制亮度档次参数
int PWM_T;       //PWM占空比控制变量
int PWM_F;       //PWM占空比控制变量
uchar SD_YY_num;    //手动和语音模式调节亮度参数统计(0-5档)

sbit YY_SB_KD=P1^3;//语音识别控制管脚(打开台灯)
sbit YY_SB_GD=P1^4;//语音识别控制管脚(关闭台灯)
sbit YY_SB_SG=P1^5;//语音识别控制管脚(升高台灯亮度)
sbit YY_SB_JD=P1^6;//语音识别控制管脚(降低台灯亮度)

void DS1302_sz();//万年历时钟和闹钟设置函数声明
void ms_get();//手动自动控制模式函数声明
/* ================ 打开 ISP,IAP 功能 ================= */
void ISP_IAP_enable(void)
{
 EA = 0;       /* 关中断   */
 ISP_CONTR = ISP_CONTR & 0x18;       /* 0001,1000 */
 ISP_CONTR = ISP_CONTR | WaitTime; /* 写入硬件延时 */
 ISP_CONTR = ISP_CONTR | 0x80;       /* ISPEN=1  */
}
/* =============== 关闭 ISP,IAP 功能 ================== */
void ISP_IAP_disable(void)
{
 ISP_CONTR = ISP_CONTR & 0x7f; /* ISPEN = 0 */
 ISP_TRIG = 0x00;
 EA   =   1;   /* 开中断 */
}
/* ================ 公用的触发代码 ==================== */
void ISPgoon(void)
{
 ISP_IAP_enable();   /* 打开 ISP,IAP 功能 */
 ISP_TRIG = 0x46;  /* 触发ISP_IAP命令字节1 */
 ISP_TRIG = 0xb9;  /* 触发ISP_IAP命令字节2 */
 _nop_();
}
/* ==================== 字节读 ======================== */
unsigned char byte_read(unsigned int byte_addr)
{
 EA = 0;
 ISP_ADDRH = (unsigned char)(byte_addr >> 8);/* 地址赋值 */
 ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
 ISP_CMD   = ISP_CMD & 0xf8;   /* 清除低3位  */
 ISP_CMD   = ISP_CMD | RdCommand; /* 写入读命令 */
 ISPgoon();       /* 触发执行  */
 ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
 EA  = 1;
 return (ISP_DATA);    /* 返回读到的数据 */
}
/* ================== 扇区擦除 ======================== */
void SectorErase(unsigned int sector_addr)
{
 unsigned int iSectorAddr;
 iSectorAddr = (sector_addr & 0xfe00); /* 取扇区地址 */
 ISP_ADDRH = (unsigned char)(iSectorAddr >> 8);
 ISP_ADDRL = 0x00;
 ISP_CMD = ISP_CMD & 0xf8;   /* 清空低3位  */
 ISP_CMD = ISP_CMD | EraseCommand; /* 擦除命令3  */
 ISPgoon();       /* 触发执行  */
 ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
}
/* ==================== 字节写 ======================== */
void byte_write(unsigned int byte_addr, unsigned char original_data)
{
 EA  = 0;
 ISP_ADDRH = (unsigned char)(byte_addr >> 8);  /* 取地址  */
 ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
 ISP_CMD  = ISP_CMD & 0xf8;    /* 清低3位 */
 ISP_CMD  = ISP_CMD | PrgCommand;  /* 写命令2 */
 ISP_DATA = original_data;   /* 写入数据准备 */
 ISPgoon();       /* 触发执行  */
 ISP_IAP_disable();     /* 关闭IAP功能 */
 EA =1;
}
void write_eeprom()			   //内部eeprom写函数
{
 SectorErase(0x2000);		   //清空
 SectorErase(0x2c00);
 SectorErase(0x2e00);
 byte_write(0x2c00,nz_shi);	
 byte_write(0x2c01,nz_fen);	   		
 byte_write(0x2060,0x01);
}
/******************把数据从单片机内部eeprom中读出来*****************/
void read_eeprom()																  
{
 nz_shi=byte_read(0x2c00);
 nz_fen=byte_read(0x2c01);
 a_a=byte_read(0x2060);	 
}
/**************开机自检eeprom初始化*****************/
void init_eeprom() 
{
 a_a=byte_read(0x2060);
 if(a_a!=1)		//新的单片机初始单片机内问eeprom
 {
  a_a=1;
  write_eeprom();	   //保存数据
 }	
}
//定时器初始化设置
void dingshi_init()
{
 TMOD=0x21;                    
 EA=1;                                   
 TH1=210;     
 TL1=210;   
 TR1=1;       
 ET1=0;       
}
void main()
{
 Ds1302_Init();//时钟初始化
 delay_K(10);  //延时等待初始化完成
 Init_LCD();   //液晶初始化
 delay_K(10);  //延时等待初始化完成
 init_eeprom();//初始化eeprom
 delay_K(10);  //延时
 read_eeprom();//读取eeprom数据
 delay_K(10);  //延时
 write_sfm(9,nz_shi,2);	//显示闹钟的时
 write_sfm(12,nz_fen,2);//显示闹钟的分 
 delay_K(1);  //延时
 dingshi_init();//定时器设置
 delay_K(1);  //延时
 write_sfm1(15,ld_dc1,2);//初始化显示台灯亮度档次(关灯状态)
 while(1)
 {
  k=Key();//实时扫描按键值(判断按键是否按下)
  Ds1302_Read_Time();//读取1302时钟
  LCD_Write_Time(time_buf1);//实时显示实时时钟
  DS1302_sz();//调用万年历修改函数
  ms_get();	//手动自动模式控制台灯亮灭和亮度
 }
}
void ms_get()
{
 //自动模式
 if(k==4)//自动模式按键按下
 {
  ZD_mode=1;//跳出手动模式进入自动模式
  SD_mode=0;
  YY_mode=0;
  SD_YY_num=0;
 }
 //手动模式
 if(k==5)//手动模式按键按下
 {
  ZD_mode=0;//跳出自动模式进入手动模式
  SD_mode=1;
  YY_mode=0;
  SD_YY_num++;//手动模式下按一次加一次
  if(SD_YY_num>5)//如果超过5次置0
  {
   SD_YY_num=0;
  }
 }
 //语音识别模式(开灯)
 if(YY_SB_KD==0)//语音识别开灯
 {
  delay_K(2);//延时消抖
  if(YY_SB_KD==0)//再次判断
  {
   ZD_mode=0; //退出自动模式
   SD_mode=0; //退出手动模式
   YY_mode=1;
   SD_YY_num=1; 
  }
  while(!YY_SB_KD);//释放有效
 }
 //语音识别模式(关灯)
 if(YY_SB_GD==0) //语音识别关灯
 {
  delay_K(2);
  if(YY_SB_GD==0)
  {
   ZD_mode=0;
   SD_mode=0;
   YY_mode=1;
   SD_YY_num=0;
  }
  while(!YY_SB_GD);
 }
 //语音识别模式(升高亮度)
 if(YY_SB_SG==0) //语音识别升高亮度
 {
  delay_K(2); //延时消抖
  if(YY_SB_SG==0) //再次判断
  {
   ZD_mode=0;
   SD_mode=0;
   YY_mode=1;//进入语音识别模式
   SD_YY_num++;
   if(SD_YY_num>5)
   {
    SD_YY_num=5;
   }
  }
  while(!YY_SB_SG);
 }
 //语音识别模式(降低亮度)
 if(YY_SB_JD==0) //语音识别降低亮度
 {
  delay_K(2);//延时消抖
  if(YY_SB_JD==0)//再次判断
  {
   ZD_mode=0;
   SD_mode=0;
   YY_mode=1;//进入语音识别模式
   SD_YY_num--;//档次降低
   if(SD_YY_num==0xff)
   {
    SD_YY_num=0;
   }
  }
  while(!YY_SB_JD);//释放有效
 }
 if((SD_mode==1)||(YY_mode==1))//手动模式//或者语音模式
 {
  switch(SD_YY_num)
  {
   case 0:{ld_dc2=0;ET1=0;pwm_led=1;break;}
   case 1:{ld_dc2=1;ET1=1;break;}
   case 2:{ld_dc2=2;ET1=1;break;}
   case 3:{ld_dc2=3;ET1=1;break;}
   case 4:{ld_dc2=4;ET1=1;break;}
   case 5:{ld_dc2=5;ET1=1;break;}
  }
  PWM_F=ld_dc2*20;//调节PWM亮度系数
  write_sfm1(15,ld_dc2,2);//显示语音/手动控制模式亮度档次	
 }
 //自动模式
 if((ZD_mode==1)&&(RT_HW==1))//自动模式下如果检测到有人则亮灯,且根据环境自动调节亮度
 {
  adc_num=ad0832read(1,0);//采集实时光线亮度指数
  if((0<adc_num)&&(adc_num<=100))//根据阻值参数,变换PWM_T的值
   {
	PWM_T=0;ET1=0;pwm_led=1;			  
   }
  if((100<adc_num)&&(adc_num<=120))//根据阻值参数,变换PWM_T的值
   {
	PWM_T=20;ET1=1;
   }
  if((120<adc_num)&&(adc_num<=160))//根据阻值参数,变换PWM_T的值
   {
	PWM_T=40;ET1=1;
   }
  if((160<adc_num)&&(adc_num<=200))//根据阻值参数,变换PWM_T的值
   {
    PWM_T=60;ET1=1;
   }
  if((200<adc_num)&&(adc_num<=245))//根据阻值参数,变换PWM_T的值
   {
    PWM_T=80;ET1=1;
   }
  if((245<adc_num)&&(adc_num<=280))//根据阻值参数,变换PWM_T的值
   {
    PWM_T=100;ET1=1;
   }
   ld_dc1=PWM_T/20;
   write_sfm1(15,ld_dc1,2);//液晶显示自动调节亮度指数
 }
 if((ZD_mode==1)&&(RT_HW==0))//自动模式下如果人离开则自动关灯
 {
  delay_K(10);
  if((ZD_mode==1)&&(RT_HW==0))
  {
   ET1=0;
   PWM_T=0;
   pwm_led=1;
   ld_dc1=PWM_T/20;			  
   write_sfm1(15,ld_dc1,2);//关灯
  }
 }
 //闹钟报警
 if((time_buf1[4]==nz_shi&&time_buf1[5]==nz_fen)||(Ce_Ju==0))
 {
  alarm=1;
  delay_K(80);
  alarm=0;
  delay_K(80);
 }
 else
 {
  alarm=1;
 }
 //红外人体检测
 if(RT_HW==1)
 {
  RT_HW_LED=0;//检测到有人,点亮指示灯
 }
 else
 {
  RT_HW_LED=1;//检测不到有人,指示灯灭
 }

}
//DS1302日历调整函数
void DS1302_sz()
{
 if(k==3)
 {
   mode++;
   write_com(0x0d);//打开闪烁
   write_com(0x80+0x40+7);//停留在秒闪烁
 }
 //进入时间调整循环
 while(mode!=0)
 {
   temp=1;  //调整时间
   k=Key(); //是否有键按下
   if(k==3) //功能键按下
    {
	  mode++;
	  if(mode==9)
	  mode=0;
	  switch(mode)//光标闪烁
	   {
		case 2:{write_com(0x80+0x40+4);//停留在分闪烁					
				break;
			   }	
		case 3:{write_com(0x80+0x40+1);//停留在时闪烁					
				break;
			   }	
		case 4:{write_com(0x80+0x03);//停留在年闪烁					
				break;
			   }	
		case 5:{write_com(0x80+0x06);//停留在月闪烁					
				break;
			   }	
		case 6:{write_com(0x80+0x09);//停留在日闪烁					
				break;
			   }
	    case 7:{write_com(0x80+0x40+10);//停留在闹钟的时闪烁					
				break;
			   }
		case 8:{write_com(0x80+0x40+13);//停留在闹钟的分闪烁					
				break;
			   }		
	   }
	  k=0;			
	}
   if(k==2) //时间++
	{
	  switch(mode)
	   {
		case 1:{time_buf1[6]++;
				if(time_buf1[6]==60)
				 {
				  time_buf1[6]=0;
				 }
				//显示
				 write_com(0x80+0x40+0x06);					
				 write_date(0x30+time_buf1[6]/10);
				 write_date(0x30+time_buf1[6]%10);
				 write_com(0x80+0x40+0x07);//停留在秒闪烁
				 break;
				}
		case 2://分
				{time_buf1[5]++;
				 if(time_buf1[5]==60)
				 {
				  time_buf1[5]=0;
				 }
				//显示
				 write_com(0x80+0x40+0x03);						
				 write_date(0x30+time_buf1[5]/10);
				 write_date(0x30+time_buf1[5]%10);
				 write_com(0x80+0x40+0x04);//停留在秒闪烁
				 break;
				}
		case 3://时
				{time_buf1[4]++;
				 if(time_buf1[4]==24)
				 {
				  time_buf1[4]=0;
				 }
				//显示
				 write_com(0x80+0x40+0x00);					
				 write_date(0x30+time_buf1[4]/10);
				 write_date(0x30+time_buf1[4]%10);
				 write_com(0x80+0x40+0x01);//停留在秒闪烁
				 break;
			    }
		 case 4://年
				{time_buf1[1]++;
				 if(time_buf1[1]==100)
				 {
				  time_buf1[1]=0;
				 }
				//显示
				 write_com(0x80+0x00);
				 write_date(0x30+time_buf1[0]/10);
				 write_date(0x30+time_buf1[0]%10);					
				 write_date(0x30+time_buf1[1]/10);
				 write_date(0x30+time_buf1[1]%10);
				//计算日期	
				 time_buf1[7]=Conver_week(time_buf1[1],time_buf1[2],time_buf1[3]);
				//显示日期
				 Data_XS(time_buf1[7]);
				 write_com(0x80+0x03);//停留在秒闪烁
				 break;
				}
		  case 5://月
				{
				 time_buf1[2]++;
				 if(time_buf1[2]==13)
				 {
				  time_buf1[2]=1;
				 }
				//显示
				 write_com(0x80+0x05);						
				 write_date(0x30+time_buf1[2]/10);
				 write_date(0x30+time_buf1[2]%10);
				//计算日期	
				 time_buf1[7]=Conver_week(time_buf1[1],time_buf1[2],time_buf1[3]);
				//显示日期
				 Data_XS(time_buf1[7]);
				 write_com(0x80+0x06);//停留在秒闪烁
				 break;
				}
		  case 6://日
				{time_buf1[3]++;
				 if(time_buf1[3]==32)
				  {
					time_buf1[3]=1;
				  }		 
				//显示
				 write_com(0x80+0x08);					
				 write_date(0x30+time_buf1[3]/10);
				 write_date(0x30+time_buf1[3]%10);
				//计算日期	
				 time_buf1[7]=Conver_week(time_buf1[1],time_buf1[2],time_buf1[3]);
				//显示日期
				 Data_XS(time_buf1[7]);
				 write_com(0x80+0x09);//停留在秒闪烁
				 break;
				}
		   case 7://闹钟的时
				{nz_shi++;
				 if(nz_shi==24)
				 {
				  nz_shi=0;
				 }
				 write_eeprom();
				 delay_K(1);
				//显示
				 write_sfm(9,nz_shi,2);
				 write_com(0x80+0x40+10);//停留在秒闪烁
				 break;
			    }
		   case 8://闹钟的分
				{nz_fen++;
				 if(nz_fen==60)
				 {
				  nz_fen=0;
				 }
				 write_eeprom();
				 delay_K(1);
				//显示
				 write_sfm(12,nz_fen,2);
				 write_com(0x80+0x40+13);//停留在秒闪烁
				 break;
				}
		}
	 k=0;
    }
   if(k==1) //时间--
	{
	  switch(mode)
	  {
		case 1://秒
               {
				time_buf1[6]--;
				if(time_buf1[6]==0xff)
				{
				 time_buf1[6]=59;
				}
				//显示
				write_com(0x80+0x40+0x06);					
				write_date(0x30+time_buf1[6]/10);
				write_date(0x30+time_buf1[6]%10);
				write_com(0x80+0x40+0x07);//停留在秒闪烁
				break;
			   }
		 case 2://分
			   {time_buf1[5]--;
				if(time_buf1[5]==0xff)
				{
				 time_buf1[5]=59;
				}
						//显示
				write_com(0x80+0x40+0x03);						
				write_date(0x30+time_buf1[5]/10);
				write_date(0x30+time_buf1[5]%10);
				write_com(0x80+0x40+0x04);//停留在秒闪烁
				break;
			   }
		 case 3://时
			   {
				time_buf1[4]--;
				if(time_buf1[4]==0xff)
				{
				 time_buf1[4]=23;
				}
				//显示
				write_com(0x80+0x40+0x00);					
				write_date(0x30+time_buf1[4]/10);
				write_date(0x30+time_buf1[4]%10);
				write_com(0x80+0x40+0x01);//停留在秒闪烁
				break;
			   }
		 case 4://年
			   {time_buf1[1]--;
				if(time_buf1[1]==0xff)
				{
				 time_buf1[1]=99;
				}
				//显示
				write_com(0x80+0x00);
				write_date(0x30+time_buf1[0]/10);
				write_date(0x30+time_buf1[0]%10);					
				write_date(0x30+time_buf1[1]/10);
				write_date(0x30+time_buf1[1]%10);
				//计算日期	
				time_buf1[7]=Conver_week(time_buf1[1],time_buf1[2],time_buf1[3]);
				//显示日期
				Data_XS(time_buf1[7]);
				write_com(0x80+0x03);//停留在秒闪烁
				break;
			   }
		  case 5://月
				{time_buf1[2]--;
				if(time_buf1[2]==0)
				{
				  time_buf1[2]=12;
				}
				//显示
				write_com(0x80+0x05);						
				write_date(0x30+time_buf1[2]/10);
				write_date(0x30+time_buf1[2]%10);
				//计算日期	
				time_buf1[7]=Conver_week(time_buf1[1],time_buf1[2],time_buf1[3]);
				//显示日期
				Data_XS(time_buf1[7]);
				write_com(0x80+0x06);//停留在秒闪烁
				break;
			   }
		  case 6://日
			   {
				time_buf1[3]--;
				if(time_buf1[3]==0)
				{
				 time_buf1[3]=31;
				}
				//显示
				write_com(0x80+0x08);					
				write_date(0x30+time_buf1[3]/10);
				write_date(0x30+time_buf1[3]%10);
				//计算日期	
				time_buf1[7]=Conver_week(time_buf1[1],time_buf1[2],time_buf1[3]);
				//显示日期
				Data_XS(time_buf1[7]);
				write_com(0x80+0x09);//停留在秒闪烁
				break;
			   }
		  case 7://闹钟的时
			   {
				nz_shi--;
				if(nz_shi==0xff)
				{
				 nz_shi=23;
				}
				write_eeprom();
				delay_K(1);
				//显示
				write_sfm(9,nz_shi,2);
				write_com(0x80+0x40+10);//停留在秒闪烁
				break;
			   }
		  case 8://闹钟的分
			   {nz_fen--;
				if(nz_fen==0xff)
				{
				 nz_fen=59;
				}
				write_eeprom();
				delay_K(1);
				//显示
				write_sfm(12,nz_fen,2);
				write_com(0x80+0x40+13);//停留在秒闪烁
				break;
			   }
		}
	   k=0;
	 }
    }
	if(temp==1)//存入新时间
	{
	 write_com(0x0c);//关闭闪烁
	 Ds1302_Write_Time();
	 temp=0;
	}
}

timer0() interrupt 3 using 3
{
 int t;   //PWM计数
 t++;    //每次定时器溢出加1 
 if(t==100)   //PWM周期 100个单位
 {
  t=0;  //使t=0,开始新的PWM周期
  pwm_led=0;  //使LED灯亮  ,输出端口
 }
 if(ZD_mode==0)
 {
  if(PWM_F==t)  //按照当前占空比切换输出为高电平
  {   
   pwm_led=1;        //使LED灯灭  
  }
 }
 if(ZD_mode==1)
 {
  if(PWM_T==t)  //按照当前占空比切换输出为高电平
  {   
   pwm_led=1;        //使LED灯灭 
  }
 }
} 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐