单片机的红外遥控报警器单片机的红外遥控报警器
第一章 系统设计
1.1题目要求
设计并制作一个红外遥控温度报警器,用12864显示,红外遥控修改温度报警上限和下限等,并可以自由发挥扩展功能,比如增加万年历,增加闹钟等。 1.2 系统组成
1、本系统主要由控制器模块、红外遥控模块、温度报警模块、电源模块、时钟模块、显示模块部分组成如图1.1所示。
红外遥温度报警 控
AT89S52 时钟模 12864 块
液晶显示 电源和
复位
图1.1 温度监控报警系统方框图
2、系统的软件设计采用C语言,对单片机进行编程实现各项功能。系统流程图...
单片机的红外遥控报警器
第一章 系统
1.1
目要求
设计并制作一个红外遥控温度报警器,用12864显示,红外遥控修改温度报警上限和下限等,并可以自由发挥扩展功能,比如增加万年历,增加闹钟等。 1.2 系统组成
1、本系统主要由控制器模块、红外遥控模块、温度报警模块、电源模块、时钟模块、显示模块部分组成如图1.1所示。
红外遥温度报警 控
AT89S52 时钟模 12864 块
液晶显示 电源和
复位
图1.1 温度监控报警系统方框图
2、系统的软件设计采用C语言,对单片机进行编程实现各项功能。系统流程图如图1.2所示:
1
开始
设备初始化
串行接口
红外遥控
K=,
K=0
K=EK=10 K=1K=17 E
F 设定3 保存设定
温度闹铃 调整数据 上、
报警 时间 下限
温度
图1.2 系统程序流程图 1.3 方案论证与比较
1.3.1 控制器模块
方案一:采用FPGA作为系统控制器。FPGA功能强大,可以实现各种复杂
的逻辑功能,规模大,密度高,它将所有器件集成在一块芯片上,减少了体积,
2
提高了稳定性,并且可应用EDA软件仿真、调试,易于进行功能扩展。FPGA采用并行的I/O口方式,提高了系统的处理速度,适合作为大规模实时系统控制核心。但由于本设计对数据处理的速度要求不高,FPGA的高速处理的优势得不到充分体现,并且其成本偏高,引脚较多,硬件电路布线复杂。
方案二:采用ATMEL公司的AT89S52作为系统控制器。单片机算术运算功能强,软件编程灵活、自由度大,可用软件编程实现各种算法和逻辑控制,并且其功耗低、体积小、技术成熟和成本低等优点。
基于以上分析拟订方案二,由AT89S52作为控制核心,对温度和万年历的显示及调整进行控制。
1.3.2 显示模块
方案一:采用LED数码管显示。颜色鲜艳,经济实惠,由于本设计显示的内容较多,过多地增加数码管显然不行,进行轮流显示则控制复杂,占用较多的I/O资源,加上数码管需要较多连线,使得电路复杂,功耗较大。若采用Max7219驱动,可以减少占用的接口数目,但是数码管只能显示有限的数字和符号,不能直观地显示出设计的内容。
方案二:采用12864液晶显示。其内置8192个16*16点汉字,和128个16*8点ASCII字符集,可以直观地显示出较多内容,利用该模块灵活的接口方式和简单、方便的操作指令,可构成全中文人机交互图形界面,使得显示内容丰富,易于人机交流,并且节约了I/O口资源。
由于本设计要求用12864显示,在本设计中采用12864作为显示模块,可以同时显示温度及时间,不仅能达到设计要求,还具有明显的优越性,所以系统采用方案二。
1.3.3 温度采集模块
方案一:采用温度传感器AD590K。AD590K具有较高精度和重复性,良好的非线性保证?0.1?的测量精度。加上软件非线性补偿可以实现高精度测量。AD590将温度转化为电流信号,因此要加相应的调理电路,将电流信号转化为电压信号。送入8位A/D转换器,可以获得255级的精度,基本满足题目要求。
方案二:用热电偶测温。有优点是测温范围宽,缺点是电动势低,对运放的要求高,重要的是热电偶测温需要冷端温度补偿,来消除冷端温度变化所产生
3
的影响,对于电路补偿温度要求精度高,且准确,否则会给系统带来反作用,而且成本高,操作复杂。
方案三:采用数字温度传感器DS18B20。DS18B20为数字式温度传感器,无需其他外加电路,直接输出数字量。可直接与单片机通信,读取测温数据,电路简单。
基于以上分析和现有器件所限,温度采集模块选用方案二。DS18B20能够直接读出被测温度并且可根据实际要求通过简单的编程实现9,12位的数字值读数方式。并且从DS18B20读出的信息或写入DS18B20的信息仅需要一根口线(单线接口)读写,因而使用DS18B20可使系统结构更趋简单,可靠性更高。他在测温精度、转换时间、传输距离、分辨率等方面带来了令人满意的效果。
4
第二章 单元电路设计
2.1 控制器部分
本设计采用AT89S52单片机为核心控制器件,通过单片编程控制红外遥控,温度芯片18B20,,时钟芯片DS1302,从而实现红外遥控温度报警及红外遥控时钟,原理图如图2.1所示。
图2.1单片机最小系统电路图
2.1.1 AT89S52芯片简介
AT89S52是一个低功耗,高性能CMOS 8位单片机,片内含8k Bytes ISP(In-system programmable)的可反复擦写1000次的Flash只读程序存储器,器件采用ATMEL公司的高密度、非易失性存储技术制造,兼容标准 MCS-51指令系统及80C51引脚结构,芯片内集成了通用8位中央处理器和ISP Flash存储单元,功能强大的微型计算机的AT89S52可为许多嵌入式控制应用系统提供高性价比的解决方案。
2.1.2 AT89S52的引脚特点
5
AT89S52有40个引脚,8k Bytes Flash片内程序存储器,256 bytes的随机存取数据存储器(RAM),32个外部双向输入/输出(I/O)口,5个中断优先级2层中断嵌套中断,2个16位可编程定时计数器,2个全双工串行通信口,看门狗(WDT)电路,片内时钟振荡器
2.1.3 AT89S52的主要特性:
l 与MCS-51单片机产品兼容
l 8K字节在系统可编程Flash存储器
l 1000次擦写周期
l 全静态操作:0Hz,33Hz
l 三级加密程序存储器
l 32个可编程I/O口线
l 三个16位定时器/计数器
l 八个中断源
l 全双工UART串行通道
l 低功耗空闲和掉电模式
l 掉电后中断可唤醒
l 看门狗定时器
l 双数据指针
l 掉电标识符
2.2 温度采集部分
系统温度检测是由数字温度传感器DS18B20实现的。DS18B20具有测温系统简单、测温精度高、连接方便、占用口线少等优点,特别适用于多点测温。硬件接口电路如图2.2所示。
6
图2.2 DS18B20的硬件接口电路
(1) DSI8B20的测温功能的实现
其测温电路的实现是依靠单片机软件的编程上。 当DSI8B20接收到温度转换命令后,开始启动转换。转换完成后的温度值就以16位带符号扩展的二进制补码形式存储在高速暂存存储器的0,1字节。单片机可通过单线接口读到该数据,读取时低位在前,高位在后,数据格式以0(062 5?,LSB形式表示。温度值格式如表2.2.1所示,其中“S”为标志位,对应的温度计算:当符号位S=0时,直接将二进制位转换为十进制;当S=1时,先将补码变换为原码,再计算十进制值。DSI8B20完成温度转换后,就把测得的温度值与 TH做比较,若T>TH或T
RoM操作命令 -> 存储器操作命令-> 处理数据
? 初始化 单总线上的所有处理均从初始化开始
? ROM操作命令 总线主机检测到DSl8B20的存在便可以发出ROM操作命令之一。这些命令如表2.2.2所示
7
表2.2.2 ROM操作命令表
指令 代码
33H Read ROM(读ROM)
55H Match ROM(匹配ROM)
CCH Skip ROM(跳过ROM]
F0H Search ROM(搜索ROM)
ECH Alarm search(告警搜索)
? 存储器操作命令如表2.2.3所示
表2.2.3 存储器操作命令表
指令 代码
4EH Write Scratchpad(写暂存存储器)
BEH Read Scratchpad(读暂存存储器)
48H Copy Scratchpad(复制暂存存储器)
44H Convert Temperature(温度变换)
B8H Recall EPROM(重新调出)
B4H Read Power supply(读电源)
(3)温度转换算法及分析
由于DS18B20转换后的代码并不是实际的温度值,所以要进行计算转换。温度高字节(MS Byte)高5位是用来保存温度的正负(标志为S的bit11,bit15),高字节(MS Byte)低3位和低字节来保存温度值(bit0 ~ bit10)。其中低字节(LS Byte)的低4位来保存温度的小数位(bit0 ~ bit 3)。由于本程序采用的是0.0625的精度,小数部分的值,可以用后四位代表的实际数值乘以0.0625,得到真正的数值,数值可能带几个小数位,所以采取小数舍入,保留一位小数即可。也就说,本系统的温度精确到了0.1度。
算法核心:首先程序判断温度是否是零下,如果是,则DS18B20保存的是温度的补码值,需要对其低8位(LS Byte)取反加一变成原码。处理过后把
8
DS18B20的温度Copy到单片机的RAM中,里面已经是温度值的Hex码了,然后转换Hex码到BCD码,分别把小数位,个位,十位的BCD码存入RAM中。 2 .3 报警电路部分
本系统采用蜂鸣器作为报警电路,原理图如图2.3所示,当温度超过上限值或低于下限值,或者闹钟时间到时,通过软件控制使P35=0,从而三极管8550导通,蜂鸣器发出声音报警或闹钟响。
图2.3 报警电路图
2.4 红外遥控部分
遥控接收使用红外接收模块1838,该接收模块是一个三端元件,具有功耗低、抗干扰能力强、输入灵敏度高的特点。如图2.4所示,1838接AT89C2051的P3.2(外中断0).当1838V接收到遥控信号时(产生中断,处理遥控数据(处理完后返回。
图2.4 1838硬件接口电路图
9
2.5 时钟电路部分
采用时钟芯片DS1302,其内含一个实时时钟/日历和31字节静态RAM,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、星期、月、年的信息,每月的天数和闰年的天数可自动调整,时钟操作可通过 AM/PM 指示决定采用24或12小时格式。DS1302与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线RES( 复位); I/O (数据线);SCLK(串行时钟),时钟/RAM 的读/写数据以一个字节或多达31 个字节的字符组方式通信。
2.5.1 DS1302的概念
DS1302 是DALLAS 公司推出的涓流充电时钟芯片,内含有一个实时时钟/日历和31 字节静态RAM,通过简单的串行接口与单片机进行通信实时时钟/日历电路.提供秒分时日日期.月年的信息,每月的天数和闰年的天数可自动调整时钟操作可通过AM/PM 指示决定采用24 或12 小时格式.DS1302 与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线:1 RES 复位,2 I/O 数据线,3 SCLK串行时钟.时钟/RAM 的读/写数据以一个字节或多达31 个字节的字符组方式通信.DS1302 工作时功耗很低,保持数据和时钟信息时功率小于1mW.DS1302 是由DS1202 改进而来,增加了以下的特性.双电源管脚用于主电源和备份电源供应Vcc1,为可编程涓流充电电源附加七个字节存储器.它广泛应用于电话传真便携式仪器以及电池供电的仪器仪表等产品领域。 2.5.2 DS1302 的性能指标
1.实时时钟具有能计算2100 年之前的秒分时日日期星期月年的能力还有闰
年调整的能力。
2. 31 8 位暂存数据存储RAM。
3. 串行I/O 口方式使得管脚数量最少。
4. 宽范围工作电压2.0 —5.5V。
5.工作电流2.0V 时,小于300nA。
6.读/写时钟或RAM 数据时有两种传送方式单字节传送和多字节传送字符组方式。
7.8 脚DIP 封装或可选的8 脚SOIC 封装根据表面装配。
10
8. 简单3 线接口。
9. 与TTL 兼容Vcc=5V。
10. 可选工业级温度范围-40 +85。
11. 与DS1202 兼容。
12. 在DS1202 基础上增加的特性。
如图2.5所示:8脚接一个备份电源,保证在断电的情况下时钟仍在工作。
图2.5 时钟电路
2.6 电源部分
原理图如2.6所示,采用变压器参数都为30W,?18V,经过整流滤波后再通过三端稳压器7805得到+5V电压,提供给单片机及其他各模块。
11
图2.6 电源原理图
2.7 显示部分
本设计采用12864液晶显示器,它是一种具有4位/8位并行、2线或3线串行多种接口方式,内部含有国标一级、二级简体中文字库的点阵图形液晶显示模块;其显示分辨率为128×64, 内置8192个16*16点汉字,和128个16*8点ASCII字符集,利用该模块灵活的接口方式和简单、方便的操作指令,可构成全中文人机交互图形界面。可以显示8×4行16×16点阵的汉字,还可完成图形显示,低电压低功耗是其又一显著特点。由该模块构成的液晶显示方案与同类型的图形点阵液晶显示模块相比,不论硬件电路结构或显示程序都要简洁得多。 2.7.1 12864液晶显示模块概述
12864液晶显示模块是128×64点阵型液晶显示模块,可显示各种字符及图形,可与CPU直接接口,具有8位标准数据总线、6条控制线及电源线。采用KS0107控制IC。
2.7.2 指令描述
1、显示开/关设置
功能:设置屏幕显示开/关。DB0=H,开显示;DB0=L,关显示。不影
响显示RAM(DD RAM)中的内容。
2、设置显示起始行
12
功能:执行该命令后,所设置的行将显示在屏幕的第一行。显示起始行是由Z地址计数器控制的,该命令自动将A0-A5位地址送入Z地址计数器,起始地址可以是0-63范围内任意一行。Z地址计数器具有循环计数功能,用于显示行扫描同步,当扫描完一行后自动加一。
3、设置页地址
功能:执行本指令后,下面的读写操作将在指定页内,直到重新设置。页地址就是DD RAM 的行地址,页地址存储在X地址计数器中,A2-A0可表示8页,读写数据对页地址没有影响,除本指令可改变页地址外,复位信号(RST)可把页地址计数器内容清零。DD RAM地址映像表
4、设置列地址
功能:DD RAM 的列地址存储在Y地址计数器中,读写数据对列地址有影响,在对DD RAM进行读写操作后,Y地址自动加一。
5、状态检测
功能:读忙信号标志位(BF)、复位标志位(RST)以及显示状态位(ON/OFF)。
BF=H:内部正在执行操作; BF=L:空闲状态。 RST=H:正处于复位初始化状态; RST=L:正常状态。 ON/OFF=H:表示显示关闭; ON/OFF=L:表示显示开。
6、写显示数据
功能:写数据到DD RAM,DD RAM是存储图形显示数据的,写指令执行后Y地址计数器自动加1。D7-D0位数据为1表示显示,数据为0表示不显示。写数据到DD RAM前,要先执行“设置页地址”及“设置列地址”命令。
7、读显示数据
13
功能:从DD RAM读数据,读指令执行后Y地址计数器自动加1。从DD RAM读数据前要先执行“设置页地址” 及“设置列地址”命令
通过单片机控制,同时显示出温度和时间。
图2.7 显示部分电路图
14
结 论
是学习阶段一次非常难得的理论与实际相结合的机会,通过这次对基于单片机的遥控温度报警器的系统设计,使我对单片机的了解更深了一层,我摆脱了单纯的理论知识学习状态,和实际设计的结合锻炼了我的综合运用所学的专业基础知识,解决实际问题的能力,同时也提高我查阅文献资料、设计以及电脑制图等其他专业能力水平,而且通过对整体的掌控,对局部的取舍,以及对细节的斟酌处理,锻炼了我的能力,认识带自己的不足;也得到了丰富的经验。这是我们希望看到的,也是我们进行毕业设计的目的所在。
提高是有限的但提高也是全面的,正是这一次设计让我积累了在平时的学习中无法学习到实际经验,使我的头脑更好的被知识武装了起来,也形成自己的一种学习理论与实践相结合的方法与途径,必然会让我在未来的工作学习中表现出更高的应变能力,更强的沟通力和理解力。
15
致 谢
大学三年学习时光已经接近尾声,在此我想对我的母校,我的父母、亲人们,我的老师和同学们表达我由衷的谢意。感谢我的家人对我大学三年学习的默默支持;感谢我的母校安阳工学院给了我在大学三年深造的机会,让我能继续学习和提高;感谢安阳工学院电子信息与电气工程系的各位老师和同学们三年来的关心和鼓励。老师们课堂上的激情洋溢,课堂下的谆谆教诲;同学们在学习中的认真热情,生活上的热心主动,所有这些都让我的三年充满了感动。 这次毕业论文设计我得到了很多老师和同学的帮助,其中我的论文指导老师丁莹亮老师对我的关心和支持尤为重要。尽管丁老师平日里工作繁多,但我做毕业设计过程中给予我悉心的指导。丁老师带了我们班两年的课程,在这两年中,丁老师在学习上给了我很大的帮助,使我明白实践比理论更为重要,在此谨向丁老师致以诚挚的谢意和崇高的敬意 。同时,本篇毕业论文的写作也得到了潘永山等同学的热情帮助。感谢在整个毕业设计期间在各个方面给予过我帮助的伙伴们,在此,我再一次真诚地向帮助过我的老师和同学表示感谢~
16
参考文献
[1] 华成英,童诗白.模拟电子技术基础(第四版).高等教育出版社,2004 [2] 康华光.电子技术基础 数字部分(第五版).高等教育出版社,2006
[3] 黄智伟,王彦,陈文光,朱卫华. 全国大学生电子设计竞赛训练教程.北京:电子工业出版社,2007
北京:航天航空大学出版社.1999 [4] 王福瑞.单片微机检测系统大全.
[5] 徐惠民.单片机微型机原理接口及应用.北京:北京邮电大学出版社.1999.2 [6] 于海生.微型计算机控制技术.北京:清华大学出版社.1999 [7] 周杭慈.单片机程序设计基础.北京:航天航空大学出版社.2003.5 [8] 赵 亮.单片机C语言编程与实例.北京:人民邮电出版社.2002.9 [9] 江晓安.模拟电子技术.西安:西安电子科技大学出版社.2000 [10] 张毅刚.MCS—51单片机原理及应用.哈尔滨:哈尔滨工业大学出版社.2001
[11] 赵茂泰.智能仪器原理及应用.北京:电子工业出版社.1999
17
附录A 系统原理图
图A1 系统原理图
18
附录B PCB制版图
图B1 PCB制版图
19
附录C 硬件电路实物图
图C1硬件电路实物图
20
附录D 源程序
#include
#include
#ifndef uchar
#define uchar unsigned char #endif
#ifndef uint
#define uint unsigned int
#endif
sbit IR_GET = P3^2; //红外接收数据输入端
sbit BEEP=P3^5 ; //蜂鸣器驱动线
/********************************************************/
uchar code
menu_time_index[7][3]={{2,2,13},{4,2,9},{6,2,7},{2,3,5},{4,3,3},{6,3,1},{5,4,11}};
//设置时间导航
uchar code menu_temp_index[2][2]={{5,2},{5,3}}; //设置时间导航index code menu_time_index[7]={{2,2},{4,2},{6,2},{2,3},{4,3},{6,3},{5,4}}; //设置时间导航
uchar code menu_clock_index[3][2]={{2,3},{4,3},{6,3}}; //设置时间导航 bit sendtempflag=0;
uchar hdata=0x10,ldata=0x10; //分别存放数码管显示的红外编码的高四位和低四位。
uchar RXDDATA[]={0x00,0x00,0x00,0x00}; //存放接收到的四组红外编码 bit ircodeflag=0;
volatile bit kong=1; //全局变量,用于数码管上的两杠进行闪烁 volatile bit alarmflag=0;
volatile uchar workcode; //经过处理返回的纯数字红外编码 uchar uart_num[17]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','-'};
21
uchar times[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //1302振荡得到的准确
时间:秒分时日月星期年
uchar disptimes[14]; //准确时间:(秒分时日月星期年)分开的个位和十位
uchar xdata olddisptimes[14]={1,1,1,1,1,1,1,1,1,1,1,1,1,1}; #include "uart.h"
#include "ds1302.h"
#include "ds18b20.h"
#include "LCD12864P.C"
/********************************************************
** 函数声明专区 ********************************************************/ void DelayUS(uint num);
void delay100us(uchar x);
void beep();
uchar workIRcode(uchar *pp) ;
void sendircode();
void displaytime();
void GDRAMCLOCK(uchar row,uchar *time);
void set1616pic(uchar x,uchar y,uchar sign,uchar tt); void set1616num(uchar curr,uchar *ptime);
void gudidisptime(uchar *time); //time指针是处理好的十位个位分开的时间数组。
void gudidisptemp(uchar *tempc);
void gudidispclock(uchar *clock);
uchar checktemp(uchar *pp);
uchar time_compare();
/********************************************************/ void main()
{
bit one_sign;
bit onealarmflag=0;
22
uchar run_sign,tempdata,timenum,timelcdxy,we;
uchar i,curr_menu_id,checkflag;
uchar *pmenu;
uchar stimetopc;
uchar outflag;
delayms(500);
alarmflag=0; //默认闹铃关闭
TMOD|=0X01; //选择定时器0工作在模式1,即16位定时器
TH0=(65536-49000)/256; //装49ms的初值
TL0=(65536-49000)%256;
ET0=1; //开定时器0中断
EX0= 1; //使能 INT0 外部中断
IT0 =1; //外中断0下降沿触发
IR_GET=1; //I/O口初始化
UART_Init(); //初始化串口
EA=1; //开总中断
init_12864();
init_1302();
Clean_12864_GDRAM();
// settime_1302(inittime);
gettime_1302(times);
for(i=0;i<6;i++)
{
olddisptimes[i]=disptimes[i]+65; //为了使下面出现比较不合,写入首次的
所有图形
}
// write_12864_cmd(0x30); //非常重要~上面关显示是扩展指令,再写成基本
指令
// delayms(5);
23
// photodisplay(Photo2); //显示图片Photo2-- // delayms(2000); //显示图片2S
Clean_12864_GDRAM();
// TR0=1; //启动定时器0
while(1)
{
gettime_1302(times);
worktime_1302(disptimes,times);
EA=0;
read_temp(); //读出DS18B20温度数据
work_temp(); //处理温度数据
EA=1;
sendircode();
displaytime(); //显示非绘图的日期和温度。
GDRAMCLOCK(2,disptimes); //绘图时钟显示在第二行第三行。
for(i=0;i<6;i++)
olddisptimes[i]=disptimes[i];
set1616pic(4,4,1,2); //在第4列第4行反白的显示温度标记
/******************************************************************/
if(alarmflag==1)
{
set1616pic(8,1,0,0); //在第8列第1行反白的显示闹铃标记
}
else if(alarmflag==0)
{
setwordbkcolor(8,1,16,0); //16X16/消白
)
//进入菜单选择
if(workcode==12)
{
24
outflag=1;
workcode=14;
Clean_12864_GDRAM(); //先清完所有显示,再把菜单项目显示出来供选择。
Clean_12864();
write_12864_cmd(0x80);
for(i=0;i<12;i++)
write_12864_data(initmenu[i]);
write_12864_cmd(0x90);
for(i=48;i<60;i++)
write_12864_data(sonmenu1[i]);
write_12864_cmd(0x88);
for(i=48;i<60;i++)
write_12864_data(sonmenu2[i]);
write_12864_cmd(0x98);
for(i=48;i<60;i++)
write_12864_data(sonmenu3[i]);
setrowbkcolor(2,1); //初始是第二行反白
curr_menu_id=2;
while(outflag==1) //选择父菜单
{
if(workcode==16)
{
workcode=14;
if(curr_menu_id<4) //向上选择菜单
{
setrowbkcolor(curr_menu_id,0);
curr_menu_id++;
setrowbkcolor(curr_menu_id,1);
}
25
}
if(workcode==15) //向下选择菜单
{
workcode=14;
if(curr_menu_id>2)
{
setrowbkcolor(curr_menu_id,0);
curr_menu_id--;
setrowbkcolor(curr_menu_id,1);
}
}
if(workcode==13) //选择之后进入
{
outflag=0;
workcode=14;
}
}
outflag=1;
Clean_12864_GDRAM(); //先清完所有显示再把相应的内容显示出来
Clean_12864();
write_12864_cmd(0x80);
for(i=48;i<60;i++)
write_12864_data(pmenu[i]);
setrowbkcolor(1,1); //第一行为父菜单名称,反白处理。
write_12864_cmd(0x30);
write_12864_cmd(0x90);
for(i=0;i<16;i++)
write_12864_data(pmenu[i]);
write_12864_cmd(0x88);
26
for(i=16;i<31;i++)
write_12864_data(pmenu[i]);
write_12864_cmd(0x98);
for(i=32;i<48;i++)
write_12864_data(pmenu[i]);
if(curr_menu_id==2)
{
write_1302(0x8e,0x00); //关写保护,可以写
write_1302(0x80,0x80); //让1302停震
gettime_1302(times);
worktime_1302(disptimes,times);
}
one_sign=1;
run_sign=1;
while(outflag==1) //设置菜单
{
/**************************************************/
// 选择的是调整时间菜单项
if(curr_menu_id==2)
{
TR0=1;
switch(run_sign)
{
case 1:
timelcdxy=0x91;tempdata=times[6]/16*10+times[6]%16; timenum=6; break; //年
case 2:
timelcdxy=0x93;tempdata=times[4]/16*10+times[4]%16; timenum=4; break; //月
case 3:
timelcdxy=0x95;tempdata=times[3]/16*10+times[3]%16; timenum=3; break;
27
case 4:
timelcdxy=0x89;tempdata=times[2]/16*10+times[2]%16; timenum=2; break;
case 5:
timelcdxy=0x8b;tempdata=times[1]/16*10+times[1]%16; timenum=1; break;
case 6:
timelcdxy=0x8d;tempdata=times[0]/16*10+times[0]%16; timenum=0; break;
case 7:
timelcdxy=0x9c;tempdata=times[5]/16*10+times[5]%16; timenum=5; break;
}
if(one_sign==1)
{
one_sign=0;
gudidisptime(times);
}
write_12864_cmd(timelcdxy);
if(run_sign<7)
{
if(kong)
{
write_12864_data(((times[timenum]&0xf0)>>4)+0x30);
write_12864_data('0'+(times[timenum]&0x0f)); //小时
}
else
{
write_12864_data(' ');
write_12864_data(' ');
}
}
28
else
{
if(kong)
{
switch(times[5]&0x0f) //通过times[5]的值来确定显示汉字
{
case 1: we=0; break;
case 2: we=2; break;
case 3: we=4; break;
case 4: we=6; break;
case 5: we=8; break;
case 6: we=10; break;
case 7: we=12; break;
}
write_12864_cmd(0x9c);
write_12864_data(xq[we]); //周
write_12864_data(xq[we+1]); //周
}
else
{
write_12864_data(' ');
write_12864_data(' ');
}
}
if(workcode==11)
{
workcode=14;
tempdata++;
one_sign=1;
29
}
if(workcode==10)
{
workcode=14;
tempdata--;
one_sign=1;
}
times[timenum]=tempdata/10*16+tempdata%10;
settime_1302(times);
gettime_1302(times);
if(workcode==13)
{
if(run_sign==7)//说明时间已经全部设置完成,可以
退出了。
{
outflag=0; //退出标记
TR0=0; //关闭定时器0
Clean_12864_GDRAM();
Clean_12864();
for(i=0;i<6;i++)
{
olddisptimes[i]=disptimes[i]+65; //为了使下
面出现比较不合,写入首次的所有图形
}
}
else
{
workcode=14;
run_sign++;
write_12864_cmd(timelcdxy);
30
write_12864_data(((times[timenum]&0xf0)>>4)+0x30);
write_12864_data('0'+(times[timenum]&0x0f)); //
}
}//if(workcode==13) 在此完了
}//第一项菜单项在此设置完了
/**************************************************/
// 选择的是报警温度菜单项
if(curr_menu_id==3)
{
TR0=1;
switch(run_sign)
{
case 1: timelcdxy=0x94;tempdata=alarm_temp[1]; timenum=1; break; //年
case 2: timelcdxy=0x8c;tempdata=alarm_temp[0]; timenum=0; break; //月
}
if(one_sign==1)
{
one_sign=0;
gudidisptemp(alarm_temp);
}
write_12864_cmd(timelcdxy);
if(kong)
{
write_12864_data(alarm_temp[timenum]/10+0x30);
write_12864_data('0'+alarm_temp[timenum]%10); //
31
}
else
{
write_12864_data(' ');
write_12864_data(' ');
}
if(workcode==11)
{
workcode=14;
tempdata++;
one_sign=1;
}
if(workcode==10)
{
workcode=14;
tempdata--;
one_sign=1;
}
alarm_temp[timenum]=tempdata; //再把设置好的报警值
存起来
if(workcode==13)
{
if(run_sign==2)//说明已经全部设置完成,可以退出
了。
{
outflag=0; //退出标记
TR0=0; //关闭定时器0
Clean_12864_GDRAM();
Clean_12864();
for(i=0;i<6;i++)
32
{
olddisptimes[i]=disptimes[i]+65; //为了使下面出现比较不合,写入首次的所有图形
}
}
else
{
workcode=14;
run_sign++;
write_12864_cmd(timelcdxy);
write_12864_data(alarm_temp[timenum]/10+0x30);
write_12864_data('0'+alarm_temp[timenum]%10);
//小时
}
}//if(workcode==13) 在此完了
}//报警温度菜单项设置完成
/**************************************************/
// 选择的是设置闹铃菜单项
if(curr_menu_id==4)
{
TR0=1;
switch(run_sign)
{
case 1: timelcdxy=0x8a;tempdata=alarm_clock[0];
timenum=0; break; //闹铃小时
case 2: timelcdxy=0x8c;tempdata=alarm_clock[1];
timenum=1; break;//闹铃分钟
}
if(one_sign==1)
33
{
one_sign=0;
gudidispclock(alarm_clock);
}
write_12864_cmd(timelcdxy);
if(kong)
{
write_12864_data(alarm_clock[timenum]/10+0x30);
write_12864_data('0'+alarm_clock[timenum]%10); //
}
else
{
write_12864_data(' ');
write_12864_data(' ');
}
if(workcode==11)
{
workcode=14;
tempdata++;
one_sign=1;
}
if(workcode==10)
{
workcode=14;
tempdata--;
one_sign=1;
}
alarm_clock[timenum]=tempdata; //再把设置好的报警值存起来
if(workcode==13)
34
{
if(run_sign==2)//说明已经全部设置完成,可以退出了。
{
outflag=0; //退出标记
TR0=0; //关闭定时器0
Clean_12864_GDRAM();
Clean_12864();
for(i=0;i<6;i++)
{
olddisptimes[i]=disptimes[i]+65; //为了使下面出现比较不合,写入首次的所有图形
}
}
else
{
workcode=14;
run_sign++;
write_12864_cmd(timelcdxy);
write_12864_data(alarm_clock[timenum]/10+0x30);
write_12864_data('0'+alarm_clock[timenum]%10);
//小时
}
}//if(workcode==13) 在此完了
}//报警温度菜单项设置完成
}
} //以上是菜单设置的代码。
/**************************************************/
/**************************************************/
35
/******************************************************************/
//
checkflag=checktemp(alarm_temp); //检查报警标志
if(alarmflag&&(checkflag==1))
if(alarmflag&&checkflag)
{
alarmbeep();
}
checkflag=time_compare(); //检查报警标志
if(alarmflag&&checkflag)
if(alarmflag&&(checkflag==1))
{
alarmbeep();
}
}
}
/*******************************************************************/
/* us级延时函数 //蜂鸣器使用 */ /*******************************************************************/
void DelayUS(uint num)
{
while(--num);
}
/************************************************************
约0.1ms延时函数 (11.0592MHZ)
*************************************************************/ void delay100us(uchar x)
{
unsigned char i;
while(x--)
36
{
for (i = 0; i<10; i++);
}
}
/*******************************************************************/
/* 蜂鸣器响一声 */
/*******************************************************************/
void beep()
{
uchar y;
for (y=0;y<50;y++)
{
DelayUS(50);
BEEP=0; //有源蜂鸣器给电就响
}
BEEP=1; //关闭蜂鸣器
DelayUS(90);
}
/************************************************************
温度报警检查函数
超过限制值就返回1,温度正常则返回0;
*************************************************************/
uchar checktemp(uchar *pp)
{
uchar aa;
aa=temp_check[1]*10+temp_check[0]; //先取出当前温度值
if(pp[1]<=aa) //比较,成立则超过上限
返回1 return 2; //
if(pp[0]>=aa) //比较,成立则超过下限
37
return 1; //返回1
return 0; //返回0
}
/*********************************************************/
// 时间比较
/*********************************************************/
uchar time_compare()
{
uchar temp1,temp2;
temp1=alarm_clock[0]%10;
temp1+=(alarm_clock[0]/10)*16;
if(temp1==times[2])
{
temp2=alarm_clock[1]%10;
temp2+=(alarm_clock[1]/10)*16;
if(temp2==times[1])
return 1;
else
return 0;
}
else
return 0;
}
/*************************************************************
* 红外编码数据处理函数函数 * **************************************************************/
uchar workIRcode(uchar *pp) {
uchar result;
38
beep();
switch(pp[2])
{
case 0x00: result=0; break; //0
case 0x01: result=1; break; //1
case 0x02: result=2; break; //2
case 0x03: result=3; break; //3
case 0x04: result=4; break; //4
case 0x05: result=5; break; //5
case 0x06: result=6; break; //6
case 0x07: result=7; break; //7
case 0x08: result=8; break; //8
case 0x09: result=9; break; //9
case 0x13: result=11; break; //-
case 0x12: result=10; break; //+
case 0x1E: result=1E break; //菜单
case 0x17: result=17; break; //OK
case 0x10: result=12; break; //上
case 0x11: result=13 break; //下
case 0xFF: result=0F; break; //丽音
case 0x15: result=0E; break; //静音(关闹钟)
case 0x07: result=19; break; //声音(开闹钟)
default: result=14; break; //别的键按下返回14
}
return result;
}
/*************************************************************
* 红外编码发送到上位机函数 * ************************************************************* */
void sendircode()
39
{
uchar i;
if(ircodeflag==1)
{
ircodeflag=0;
UART_Send_Byte('I');
for(i=0;i<4;i++)
{
ldata=RXDDATA[i]&0x0F; //取键码的低四位
hdata=RXDDATA[i]>>4; //取键码的高四位
UART_Send_Byte(uart_num[hdata]);
UART_Send_Byte(uart_num[ldata]);
}
UART_Send_Byte('*');
}
}
void displaytime()
{
char k;
write_12864_cmd(0x30); //基本指令
write_12864_cmd(0x06); //设置指针地址等一些基本参数
write_12864_cmd(0x81); //写坐标
write_12864_data(disptimes[13]+0x30); //把1302读出来的年数据显示
write_12864_data(disptimes[12]+0x30); //
write_12864_data(table1[0]); //汉字“年”
write_12864_data(table1[1]);
write_12864_data(disptimes[9]+0x30); //把1302读出来的年数据显示
write_12864_data(disptimes[8]+0x30);
write_12864_data(table1[2]); //汉字“月”
write_12864_data(table1[3]);
40
write_12864_data(disptimes[7]+0x30); //把1302读出来的年数据显示
write_12864_data(disptimes[6]+0x30);
write_12864_data(table1[4]); //汉字“日”
write_12864_data(table1[5]);
write_12864_cmd(0x98); //星期的显示坐标
write_12864_data(table1[6]);
write_12864_data(table1[7]);
write_12864_data(table1[8]);
write_12864_data(table1[9]); // write_12864_data(table2[(disptimes[10]-1)*2]); //星期 // write_12864_data(table2[(disptimes[10]-1)*2+1]);
switch(disptimes[10]) //通过times[5](星期)的值来确定显示汉字
{
case 1: k=0; break;
case 2: k=2; break;
case 3: k=4; break;
case 4: k=6; break;
case 5: k=8; break;
case 6: k=10; break;
case 7: k=12; break;
}
write_12864_data(xq[k]); //星期
write_12864_data(xq[k+1]); //星期
/**********************************************************/
// 以下显示温度在第4行
write_12864_cmd(0x9c); //温度显示的坐标
write_12864_data(' '); //补上空格
write_12864_data(tempdisdata[3]); //显示温度的最高位:百位
41
write_12864_data(tempdisdata[2]); //显示温度的十位
write_12864_data(tempdisdata[1]); //显示温度的个位
write_12864_data('.'); //显示小数点
write_12864_data(tempdisdata[0]); //显示温度值小数位
write_12864_data(0xa1); //温度的符号“C”
write_12864_data(0xe6); //
/**********************************************************/
}
void GDRAMCLOCK(uchar row,uchar *time) {
uchar (*pp)[64]; //定义指针
pp=num1632; //二维数组首地址给指针,故指针指向下一个数组地址应为加64
if(olddisptimes[5]!=time[5]) //只有时间有更新时才生新写入,这样做可避免显示闪烁。
{
write1632GDRAM(1,row,pp[time[5]]); //写入小时的十位
write1632GDRAM(3,row,pp[10]); //时钟分隔符“:”
}
if(olddisptimes[4]!=time[4]) //只有时间有更新时才生新写入,这样做可避免显示闪烁。
{
write1632GDRAM(2,row,pp[time[4]]); //写入小时的个位
}
if(olddisptimes[3]!=time[3]) //只有时间有更新时才生新写入,这样做可避免显示闪烁。
{
write1632GDRAM(4,row,pp[time[3]]); //写入分钟的十位
42
write1632GDRAM(6,row,pp[10]); ////时钟分隔符“:”
}
if(olddisptimes[2]!=time[2]) //只有时间有更新时才生新写入,这样做可避免显示闪烁。
{
write1632GDRAM(5,row,pp[time[2]]); //写入分钟的个位
}
if(olddisptimes[1]!=time[1]) //只有时间有更新时才生新写入,这样做可避免显示闪烁。
{
write1632GDRAM(7,row,pp[time[1]]); //写入秒的十位
}
if(olddisptimes[0]!=time[0]) //只有时间有更新时才生新写入,这样做可避免显示闪烁。
{
write1632GDRAM(8,row,pp[time[0]]); //写入秒的个位
}
}
void set1616pic(uchar x,uchar y,uchar sign,uchar tt) //固定数组了,只有TT选择是哪一个显示
{
uchar (*pp)[32]; //定义指针
pp=bmp1616; //二维数组首地址给指针,故指针指向下一个数组地址应为加64
write1616GDRAM(x,y,sign,pp[tt]); //写入小时的十位 }
void gudidisptime(uchar *time) //time指针是处理好的十位个位分开的时间数组。 {
uchar k;
43
write_12864_cmd(0x91); // 液晶坐标定位
write_12864_data('0'+((time[6]&0xf0)>>4));
write_12864_data('0'+(time[6]&0x0f)); //年 //写入年
write_12864_cmd(0x93); // 液晶坐标定位
write_12864_data('0'+((time[4]&0xf0)>>4));
write_12864_data('0'+(time[4]&0x0f)); //写入月
write_12864_cmd(0x95); // 液晶坐标定位
write_12864_data('0'+((time[3]&0xf0)>>4));
write_12864_data('0'+(time[3]&0x0f)); //写入日
write_12864_cmd(0x89); // 液晶坐标定位
write_12864_data('0'+((time[2]&0xf0)>>4));
write_12864_data('0'+(time[2]&0x0f)); //写入时
write_12864_cmd(0x8b); // 液晶坐标定位
write_12864_data('0'+((time[1]&0xf0)>>4));
write_12864_data('0'+(time[1]&0x0f)); //写入分
write_12864_cmd(0x8d); // 液晶坐标定位
write_12864_data('0'+((time[0]&0xf0)>>4));
write_12864_data('0'+(time[0]&0x0f)); //写入秒
write_12864_cmd(0x9c); // 液晶坐标定位
switch(time[5]&0x0f) //通过times[5](星期)的值来确定显示汉字
{
case 1: k=0; break;
case 2: k=2; break;
case 3: k=4; break;
case 4: k=6; break;
case 5: k=8; break;
case 6: k=10; break;
case 7: k=12; break;
}
44
write_12864_data(xq[k]); //星期
write_12864_data(xq[k+1]); //星期
}
void gudidisptemp(uchar *tempc) {
write_12864_cmd(0x94); // 液晶坐标定位
write_12864_data('0'+((tempc[1]&0xf0)>>4));
write_12864_data('0'+(tempc[1]&0x0f)); //写入上限值
液晶坐标定位 write_12864_cmd(0x8c); //
write_12864_data('0'+((tempc[0]&0xf0)>>4));
write_12864_data('0'+(tempc[0]&0x0f)); //写入下限值
}
void gudidispclock(uchar *clock) {
write_12864_cmd(0x8a); // 液晶坐标定位
write_12864_data('0'+clock[0]/10);
write_12864_data('0'+clock[0]%10); //写入闹铃小时位
write_12864_cmd(0x8c); // 液晶坐标定位
write_12864_data('0'+clock[1]/10);
write_12864_data('0'+clock[1]%10); //写入闹铃分钟位
}
/************************************************************
外部中断0服务函数
*************************************************************/
void intt_0() interrupt 0 //下降沿触发:接收不到红外时OUT高电平,接收到红外时OUT低电平。
{
uchar four,one,num=0;
EX0 = 0; //关中断0使能,防止处理过程中再接收红外信号
45
delayms(2); //稍延时2ms,防干扰
if (IR_GET) //再检测红外接收脚(9ms的前导低电平),为高电平说明是干扰
{
EX0 =1; //使能中断0
return; //退出中断程序
}
while(!IR_GET); //等IR变为高电平,跳过9ms的前导低电平信号。
while (IR_GET); //等 IR 变为低电平,跳过4.5ms的前导高电平信号。
for (four=0;four<4;four++) //四组数据
{
for (one=0;one<8;one++) //每组数据8位
{
while (!IR_GET); //等 IR 变为高电平
while (IR_GET) //计算IR高电平时长(低电平时长是一样的,不用计)
{
delay100us(1); //计时
num++; //计时N次
if (num>=20) //17*0.1ms=1.7ms
{ //数据“1”的时长最长也就1.685ms,计数超过则数据错误,退出中断
EX0=1; //使能中断0
return; //退出中断
}
} //高电平计数完毕
RXDDATA[four]>>=1; //从低位读出,随着one的循环8次刚好读出一字节
// if(num<6) //6*0.1ms=0.6ms, 0.565ms<0.6ms<1.685ms
// RXDDATA[j]|=0x00; //数据“0”
if(num>6&&num<20) // 17*0.1ms=1.7ms>1.685ms
46
RXDDATA[four]|=0x80; //数据“1”
num=0; //计时值清0,为下一位数据的计时做准备
}//一组数据接收结束
}//全部四组数据接收结束
if(RXDDATA[0]!=0x38) //比较用户码
{
使能中断0 EX0=1; //
return; //退出中断
}
if(RXDDATA[1]!=0xc7) //比较用户码
{
EX0=1; //使能中断0
return; //退出中断
}
if (RXDDATA[2]!=~RXDDATA[3]) //检测接收到的数据是否正确
{ //不正确则
EX0=1; //使能中断0
return; //退出中断
}
ircodeflag=1;
workcode=workIRcode(RXDDATA); //处理红外编码,返回键值
if(workcode==18) //静音键按下
alarmflag=0; //闹铃标志置0,关闹铃
if(workcode==19) //声音键按下
alarmflag=1; //闹铃标志置1,使能闹铃
EX0 = 1; //处理完红外接收,使能中断0,退出中断0 }
/**************************************************
* 定时器0服务函数 ***************************************************/
47
void timer0() interrupt 1 {
uchar count;
TH0=(65536-49000)/256; //重装49ms的初值
TL0=(65536-49000)%256;
count++; //计数
if(count>9) //计到10次时
{
count=0; //计数清0
kong=~kong; //变量,取反,用于显示中时间的冒号闪烁
}
48
本文档为【单片机的红外遥控报警器】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。