与传统时钟相比,智能时钟具有实时时钟、实时日期、光照和温度显示、闹钟设置、闹钟关闭密码设置功能。同时,闹钟需要满足一定的打开条件,通过光传感器限制闹钟打开条件,需要光条件达到一定值以上,防止夜间闹钟意外响铃,同时关闭闹钟需要准确输入设计八密码,否则音乐会播放,更有效地实现闹钟自动化、智能化。其次,电子时钟增加了光照和温度显示功能,有助于区分冷暖,适当增减衣物。
2)限制闹钟通过光传感器打开;
3)闹钟关闭功能通过输入密码实现,其他方式无法关闭;
4)实时时间、日期、温度、亮度、闹钟时间、密码设置、密码验证可显示在数字管上K1、K2键显示切换;
5)设置实时时间、日期、闹钟时间、密码和关闭闹钟所需的密码。
:电子时钟、智能闹钟、密码控制
(一) 主题和选题... 1
(二) 题目背景... 1
(三) 面临问题... 1
(一) 总体方案设计... 2
1.设计目的... 2
2.设计思路图... 2
3.时间计划,内容... 3
(二) 详细设计各部分... 3
1. STC-BSP头文件选择... 3
2. 设置事件回调函数... 3
3. 基本原理... 4
4. 函数设计... 11
(三) 硬件测试结果... 14
1. 编译结果... 14
2. 下载结果... 14
3. 实操测试... 15
4. 实现功能... 16
(一) 问题及解决... 17
(二) 完成进度... 17
首先,项目的主题是智能电子时钟。与传统时钟相比,智能时钟具有实时时钟、实时日期、光和温度显示、闹钟设置和闹钟关闭密码设置功能。同时,闹钟需要满足一定的打开条件,通过光传感器限制闹钟打开条件,需要光条件达到一定值以上,防止夜间闹钟意外响铃,同时关闭闹钟需要准确输入设计八密码,否则音乐会播放,更有效地实现闹钟自动化、智能化。其次,电子时钟增加了光照和温度显示功能,有助于区分冷暖,适当增减衣物。
随着社会的发展和科学技术的进步,人们对生活质量的要求越来越高,计算机时代的到来和物联网技术的发展也给我们的生活带来了巨大的变化。因此,我想通过电子时钟的创造性设计,解决闹钟可以随意关闭的问题,帮助人们快速起床,保持清醒,然后提高生活质量,有一个美好的早晨。
1.如何将密码和其他信息同时存储在非易失存储器中,如何判断输入的密码是否与存储的密码正确匹配?
2.如何正确显示数显示多级界面,如何根据当前状态判断按键?
3.如何通过导航按钮修改密码和信息?
4.按钮是否需要抖动,闹钟是否可以随意关闭?
5.由于使用了导航按键,K注意使用3个按钮。
6.音乐播放线程会影响密码输入吗?
a) 学习STC-BSP编程方法;
c) 利用好STC学习板导航键;
d) 电子时钟通过调用光传感器和温度传感器来丰富。
a) 9月7日 确定选题
b) 9月8日-9月12日 编写代码
c) 9月13日-9月15日 测试优化并完善设计报告
根据项目功能选择BSP包中所需的头文件:
#include "STC15F2K60S2.H" //必须
#include "sys.H" //必须
#include "displayer.H" //display(显示模块)
#include "key.h" //key(按键模块)
#include "beep.h" //beep(蜂鸣器模块)
#include "music.h" //music(音乐播放)
#include "adc.h" //adc(热敏、光敏、导航按键、扩展接口ADC功能)
#include "DS1302.h" //DS1302实时时钟
#include "M24C02.h" //24C02非易失性存储器
并加载需要用的模块(由各模块提供加载函数):
Key_Init(); //按键模块加载函数
DisplayerInit(); //加载显示模块工作
BeepInit(); //蜂鸣器初始化
MusicPlayerInit(); //驱动music模块
AdcInit(ADCexpEXT); //ADC模块初始化,不含对扩展接口EXT设置ADC功能
DS1302Init(t); //DS1302初始化
MySTC_Init(); //MySTC_OS初始化
SetEventCallBack(enumEventKey, mykey_callback); //K1、K2按键
SetEventCallBack(enumEventSys100mS, my100mS_callback); //显示
SetEventCallBack(enumEventNav, myKN_callback); //导航按键
SetEventCallBack(enumEventSys1S, my1S_callback); //闹钟判断
在每一级数码管显示界面,都会检测按键和导航按键的使用,故需要K1、K2按键和导航按键的回调函数,而数码管本身显示需要一个系统100mS事件回调函数,闹钟判断需要一个系统1S事件回调函数。
P0口的8位输出分别连接了8个发光二极管L0~L7的阳极, P2.3经过一个反相器连接到8个发光二极管L0~L7的阴极(共阴极):
P0口的8位输出分别控制1个LED数码管的7段和一个小数点:
根据二极管的单向导通性(当阳极为高(对应P0口位为1)、阴极为低时,二极管导通,否则不导通),若P2.3输出信号为低电平“0”,则二极管的阴极都为高电平,二极管都不会导通,也就不会发光。因此想要发光二极管导通,必须先设置P2.3输出信号为“1”,再通过设置P0,点亮想要点亮的发光二极管。
而P2.3经反相器U4C控制74HC138的使能信号E3,结合P2.0、P2.1、P2.2这3个位选控制信号确定8个LED数码管中的哪个被点亮;电阻R15~R22为限流电阻。当段选为高、使能信号有效时,对应的LED管将会发光。通过以一定频率扫描位选信号,修改段选信号进行数码管点亮一段时间,从而给人视觉上几个数码管几乎同时显示的效果。
如果去掉DP小数点位就是七段。A在8位中的最低位,DP位为最高位。组合成的八位对应P0口的八位。数码管又分为共阴极和共阳极两种类型;其中共阴极就是将八个LED阴极连在一起,让其接地,这样给任何一个LED发光管另一端高电平,它便能点亮。而共阳极就是将八个LED的阳极连在一起。
- 非易失存储器24C02
非易失性存储器(nonvolatile memory)是掉电后数据能够保存的存储器,它不用定期地刷新存储器内容。这包括所有形式的只读存储器(ROM),像是可编程只读存储器(PROM)、可擦可编程只读存储器(EPROM)、电可擦除只读存储器(EEPROM)和闪存。在许多常见的应用中,微处理器要求非易失存储器来存放其可执行代码、变量和其他暂态数据(例如采集到的温度、光照等数据)。
24C02工作电路及其工作原理:
管脚配置:
管脚描述:
寻址方式:
寻址信号由一个字节构成,高7位为地址位,最低位为方向位,用以表明主机与从器件的数据传送方向。方向位位0,表明主机接下来对从器件进行写操作;方向位位1,表明主机接下来对从器件进行读操作。
A0,A1和A2对应器件的管脚1,2和3;
a8,a9和a10对应存储阵列地址字地址;
读/写时序:
写一个字节时序
读一个字节时序
如图,写一个字节时序,第一个DEV SEL是器件选择信号,器件选择的范围为(000~111),总共可以选择8个24C02芯片器件。但是本实验只用到了1个24C02芯片,所以对应的器件管脚地址A2A1A0为000。第二个信号BYTEADDR是地址信号,表示要对哪一个地址进行操作,第三个DATA IN则是写入的数据。而读操作则是多了一步,DEV SEL和BYTE ADDR后,还有一个DEV SEL,但此信号的最后一位为高,表示是读操作,随后从机会把相应地址的数据发送给主机。
I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛采用的一种总线标准。它是同步通信的一种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。I2C总线硬件结构图如下:
SCL是时钟线,SDA是数据线
I2C总线信号包括有,启始信号,停止信号和应答信号,在程序用分别用函数void start()、void stop()、void respons()表示。24C02的存储空间为2K,每一次写和读操作都只能操作已选定的对应24C02芯片的地址数据。要切换操作的芯片,需要重新发送寻址信号,在void write_add(uchar addr,uchar date)函数中,第一个寻址信号writebyte(0xa0),已经固定了本程序只能在第0个芯片进行操作(注:0xa0化为二进制为1010000,其中,前4位1010是固定不能改变的,最后一位0代表写操作,1代表读操作,而中间三位则是代表不同芯片地址的编号),若要改变需要操作的芯片,则只需改变中间三位即可。
数据传输:SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit;若SDA发生跳变,则用来表示一个会话的开始或结束
数据改变:SCL为低电平时,SDA线才能改变传输的bit
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据
主设备每发送完8bit数据后等待从设备的ACK。
即在第9个clock,从IC发ACK,SDA会被拉低。
若没有ACK,SDA会被置高,这会引起Master发生RESTART或STOP流程。
- 按键与导航按键
按键电路示意图(三个按键分别是K1、K2、K3)
当按键被按下的时候,电路导通接地,I/O口为低电平;当按键未被按下时,电路断开,I/O口保持高电平。
LED灯以及数码管电路连接示意图
P0口的8位输出分别控制8个发光二极管L0~L7的阳极;而P2.3经反相器U4C控制8个发光管阴极E3;当阳极为高(对应P0口位为1)、阴极为低时,对应的二极管将会发光。
导航按键在上图的标注为MINI_KEY5,导航按键的每一个方向被按下,都会引起实际电压的改变,从而可以根据这个原理,与A/D转换器配合,可以判断哪个方位被按下,获取按下后A/D转换的结果。
AD数据采集电路及采集步骤说明
ADC数据采集的步骤:
将ADC0~7的模拟量送到比较器中,用DAC(数/模转换器)转换的模拟量与输入的模拟量通过比较器进行比较。
转换结束后,将比较结果放入转换结果寄存器(ADC_RES和ADC_RESL)。
同时,需要将ADC_FLAG软件清零。
注意硬件会自动将ADC_START清零,如果需要进行下一次转换,则需要将ADC_START置位。
特别说明:
(1)数码管所显示的ADC转换结果并不是电压值,而是电压进行转换后所得的一个值。如果需要实际的电压值可以参照STC15F2K60S2数据手册的760页上面的公式进行计算得出。
(2)ADC转换结果是一个10位数据,若ADRJ=0,则ADC_RES存放高八位,ADC_RESL存放低两位。若ADRJ=1,则ADC_RESL存放高八位,ADC_RES存放低两位。本案例采用的是ADRJ=0,而且只取了高八位结果。
- 蜂鸣器
蜂鸣器分为有源蜂鸣器和无源蜂鸣器,这里的源特指振荡源;有源蜂鸣器直接加电就可以响起,无源蜂鸣器需要我们给提供振荡源。理想的振荡源为一定频率的方波。
本实验板采用的是无源蜂鸣器,相比与有源蜂鸣器,无源蜂鸣器的优点在于价格便宜,可以通过控制其振动频率来改变发出的声音,因此,无源蜂鸣器可以用于音乐的播放。而有源蜂鸣器的优点在于使用简单,不需要编写“乐谱”。本实验板使用的无源蜂鸣器是电磁式蜂鸣器,电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,接收到的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
无源蜂鸣器电路原理图
按键控制电路
- 温度传感器与光照传感器
LED数码管电路:
光敏和热敏电阻电路:
工作原理:
热敏电阻随温度呈线性变化,光敏电阻电流随光强线性变化。通过AD采集光敏电阻和热敏电阻的输出值,输出对应的AD值,光照值直接为AD值,而采集的温度AD值,首先把10位转换成8位AD值,然后再通过查找对应的表来获取温度。
unsigned char funcmode; //定义显示、按键功能模式
enum funcname
{
RTC_HMS = 1, //实时时钟:时分秒
RTC_YMD, //实时时钟:年月日
Rt_Rop, //热敏光敏测量
Alarm, //闹钟
Code, //设置密码
Code_test //密码测试
};
- K1、K2按键回调函数设计
void dealwithmykey() //k1、k2按键效果
{
if (GetKeyAct(enumKey2) == enumKeyPress) //按键2按下时切换显示和按键功能模式
{
if (funcmode == Code_test) //闹钟状态下,K2键按下不会改变状态
{
funcmode == Code_test;
}
else
{
SetBeep(1000, 10); //蜂鸣器鸣叫一声
tempadj = 0;
if (++funcmode > Code) //状态合理切换
funcmode = RTC_HMS;
LedPrint(funcmode);
M24C02_Write(0x00, funcmode);
}
}
if (GetKeyAct(enumKey1) == enumKeyPress) //K1按下时切换显示和按键功能模式
{
if (funcmode == Code_test) //闹钟状态下,K1键按下不会改变状态
{
funcmode == Code_test;
}
else
{
SetBeep(1000, 10); //蜂鸣器鸣叫一声
tempadj = 0;
if (--funcmode < RTC_HMS) //状态合理切换
funcmode = Code;
LedPrint(funcmode);
M24C02_Write(0x00, funcmode);
}
}
}
- 显示回调函数设计
void dealwithDisp()
{
unsigned char d0, d1, d2, d3, d4, d5, d6, d7; //8位数码管显示定义
static unsigned char ct100mS = 10;
if (--ct100mS == 0) //设置一个数码管闪烁的计时函数
ct100mS = 10;
switch (funcmode)
{
case RTC_YMD:
if (tempadj == 0)
t = RTC_Read(); //非调时钟时, 读RTC
.
.详见工程文件
case RTC_HMS:
.
.详见工程文件
case Rt_Rop:
ADCresult = GetADC();
ADCresult.Rt = tempdata[ADCresult.Rt - 330]; //根据转换表进行读取的温度与实际温度转换
d0 = ADCresult.Rop % 1000 / 100; //取出数据的百位
d1 = ADCresult.Rop % 100 / 10; //取出数据的十位
d2 = ADCresult.Rop % 10; //取出数据的个位
.
.详见工程文件
case Alarm:
if (tempadj == 0)
{
m.hour = NVM_Read(0x01); //将闹钟的小时数存入DS1302中的0x01地址中
m.minute = NVM_Read(0x02); //将闹钟的分钟数存入DS1302中的0x02地址中
m.second = NVM_Read(0x03); //将闹钟的秒钟数存入DS1302中的0x03地址中
}
d0 = m.hour >> 4; //取8 bit数据中的高四位
d1 = m.hour & 0xf; //取8 bit数据中的低四位
.
.详见工程文件
case Code:
if (tempadj == 0)
{
c.a1 = NVM_Read(0x04); //将8位闹钟密码分别存入DS1302中的0x04~0x11地址中
.
.详见工程文件
case Code_test:
.
.详见工程文件
}
if (funcmode == RTC_YMD || funcmode == RTC_HMS || funcmode == Alarm)
//时间、日期、闹钟显示界面时数码管如何闪烁
{
if (ct100mS >= 8)
switch (tempadj)
{
.
.详见工程文件
}
}
if (funcmode == Code || funcmode == Code_test)
//设置密码、密码输入界面时数码管如何闪烁
{
.
.详见工程文件
}
Seg7Print(d0, d1, d2, d3, d4, d5, d6, d7); //8位数码管显示
}
- 导航按键回调函数设计
在时间、日期、闹钟状态下导航中键选择开始更改或者确认更改,导航左右键更改选择时、分、秒 或 年、月、日,上下键数字加减一。在设置密码状态下,按下导航中键更改密码,导航左右键更改选择密码位数,上下键数字加减一。在闹钟响起后输入密码状态下,上下键数字加减一,右键按下判断密码是否正确,其余键无法改变闹钟状态。
- 闹钟回调函数
void dealwithAlarm() //闹钟回调函数
{
if (m.hour == t.hour && m.minute == t.minute && m.second == t.second)
//判断条件
{
ADCresult = GetADC();
if (ADCresult.Rop >= 20)
{
funcmode = Code_test;
SetMusic(Music_PM, Music_tone, &song, sizeof(song), 0xF0);
//初始化音乐
SetPlayerMode(enumModePlay); //播放音乐
}
}
}