通过数码管显示“时-分-秒”,并可通过矩阵键盘进行调整
主要任务
1、初始状态:
DS1302时间初始化为12:00:00,数码管显示从DS1302获取的时间
2、调节模式;
按下S4进入调节模式,可通过S1、S5控制“时”的加减;S2、S6控制“分”的加减;S3、S7控制“秒”的加减
在调节某一位时,对应位置的显示应闪烁
3、计时模式
从调整后的时间开始显示,“-”以1s为周期闪烁
附加任务
1、将当前显示的时间通过串口发送
2、制作一个闹钟
1、初始状态
创建一个数组,按顺序、位置将数组中的事件数据经转换后输入DS1302即可
2、调节模式
通过按键对数组中的数字进行操作
通过定时器控制一个标志位以1s为周期进行转换,用于标记数码管的亮暗,实现闪烁效果
通过一个标记变量,达到实现控制模式的效果,当按下S4时,进入调节模式
再创建一个标记变量,当控制加减的按键被按下时,切换到对应的显示模式,实现闪烁效果
在对时间进行加减时,注意合法性检查
3、计时模式
当按下S8时,将数组中的数字写入DS1302并在数码管显示
1、将当前显示的时间通过数码管发送
添加数码管模块,在ASCII表中找到0的ASCII值,将需要发送的数据加上0的值进行发送,即可以文本形式在串口助手端进行接收
(注:可以通过连续发送的方式在接收端显示出 “xx:xx:xx” 的效果)
2、制作闹钟
为了方便对函数进行复用,可以将之前的数组转换为二维数组,并在修改时间的函数的参数中添加一个标记位,用于标记是对时钟的修改还是对闹钟的修改
此外,还需一个标记位用于标记闹钟是否开启,以及一个单独的数码管显示模式用于表示闹钟响起(数码管和LED以及点阵屏有管脚冲突,目前我没有找到好的方法来防止这种冲突)
另:可以通过上一次按键的输入值和当前按键输入值的比较实现按键不干扰程序工作的效果
以下是这次任务的主要源码
main.c
#include <STC89C5xRC.H>
#include "DS1302.h"
#include "NixieTube_Display.h"
#include "Delay.h"
#include "Uart.h"
#include "Timer0.h"
#include "MatrixKey.h"
#include "MatrixLED.h"
void main()
{
unsigned char Current_Time = 0;
unsigned char Mode = 0;
unsigned char Is_Clock_On = 0;//闹钟是否开启
unsigned char Clock_Mark = 0;//闹钟是否响起
unsigned char Key_Num = 0;
unsigned char last_Key_Num = 0;
unsigned char i = 0;
int j = 0;
DS1302_Init();
DS1302_SetTime();//设定初始时间
Uart_Init();
Timer0_Init();
while(1)
{
Key_Num = MatrixKey();
if(Key_Num == 8 && last_Key_Num == 0)
//在矩阵键盘的输入中不添加死循环,通过对上一次按键值和这一次的按键值的判断来实现按键不影响程序运行的效果
{
if(Mode = 1) DS1302_SetTime(); //设置调整后的时间
Mode = 0;//计时模式
//Time_Select = 0;//重置时间选择
}
if(Key_Num == 4 && last_Key_Num == 0)
{
Mode = 1;//调整时间
}
if(Key_Num == 16 && last_Key_Num == 0)
{
Uart_SentTime();//发送时间
}
if(Key_Num == 9 && last_Key_Num == 0)
{
Mode = 3;//调整闹钟时间
}
if(Key_Num == 10 && last_Key_Num == 0)
{
Is_Clock_On++;
Is_Clock_On %= 2;//开关闹钟
}
last_Key_Num = Key_Num;
switch(Mode)
{
case 0:
//计时模式
if(Is_Clock_On == 1)
{
NixieTube_Display(8, 11);
if(Clock_Mark == 0) Clock_Mark = Is_Clock_Alarm();
if(Clock_Mark == 1)
{
j++;
if(j < 2000) NixieTube_DisplayAlarm();//显示CLOC模拟闹钟响起
DS1302_ReadTime();
if(j == 2000)
{
j = 0;
Clock_Mark = 0;
}
}
else
{
DS1302_ReadTime();
NixieTube_DisplayTime(0, 0);
}
}
else
{
DS1302_ReadTime();
NixieTube_DisplayTime(0, 0);
}
break;
case 1:
//调节模式
DS1302_TimeChange(0);
//第一个参数置0时调整时钟,置1调整闹钟
if(Is_Clock_On == 1) NixieTube_Display(8, 11);
break;
case 3:
//调整闹钟
DS1302_TimeChange(1);
if(Is_Clock_On == 1) NixieTube_Display(8, 11);
break;
}
}
}
unsigned char Flicker_Mark = 0;//用于标记亮灭
void Timer0_Isr(void) interrupt 1
{
static unsigned int T0_count = 0;
TL0 = 0x66;
TH0 = 0xFC;
T0_count++;
if(T0_count >= 500)
{
T0_count = 0;
Flicker_Mark = ~Flicker_Mark;
}
}
DS1302.c
#include <STC89C5xRC.H>
#include "MatrixKey.h"
#include "NixieTube_Display.h"
//对端口重定义
sbit DS1302_SCLK = P3^6;
sbit DS1302_IO = P3^4;
sbit DS1302_CE = P3^5;
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8a
#define DS1302_YEAR 0x8c
#define DS1302_WP 0x8e
void DS1302_Init();
void DS1302_Write(unsigned char Command, unsigned char Data);
unsigned char DS1302_Read(unsigned char Command);
void DS1302_SetTime();
void DS1302_ReadTime();
unsigned char DEC_To_BCD(unsigned char DEC_Num);
unsigned char BCD_To_DEC(unsigned char BCD_Num);
void DS1302_TimeChange(unsigned char Change_Mode);
char DS1302_Time[2][3] = {{12, 0, 0}, {12, 0, 0}};
//分别用于计时、闹钟
void DS1302_Init()
//初始化DS1302
{
DS1302_CE = 0;//使能
DS1302_SCLK = 0;//串行时钟置0
}
void DS1302_Write(unsigned char Command, unsigned char Data)
//写入数据
{
unsigned char i = 0;
DS1302_CE = 1;//停止运行
for(i = 0; i < 8; i++)
{
DS1302_IO = Command & (0x01 << i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;
}
for(i = 0; i < 8; i++)
{
DS1302_IO = Data & (0x01 << i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;
}
DS1302_CE = 0;//恢复运行
DS1302_SCLK = 0;
}
unsigned char DS1302_Read(unsigned char Command)
//读取当前时间
{
unsigned char Data = 0x00;
unsigned char i = 0;
Command |= 0x01;//保证第一位置1
DS1302_CE = 1;
for(i = 0; i < 8; i++)
{
DS1302_CE = 1;
DS1302_IO = Command & (0x01 << i);
DS1302_SCLK = 0;
DS1302_SCLK = 1;
//在最后一个循环时为上升沿
}
for(i = 0; i < 8; i++)
{
DS1302_SCLK = 1;
//重复置1,减去一个脉冲周期
DS1302_SCLK = 0;
//读取一位,同时使执行结束时为0
if(DS1302_IO)
{
Data |= (0x01 << i);
//从最低位开始读取,应存到最低位
}
}
DS1302_CE = 0;
DS1302_SCLK = 0;
DS1302_IO = 0;//这里需要将IO重置,否则读取的数据会在输入数据和ff之间横跳
return Data;
}
void DS1302_SetTime()
//将时间写入DS1302
{
DS1302_Write(DS1302_WP, 0x00);//关闭写保护
DS1302_Write(DS1302_HOUR, DEC_To_BCD(DS1302_Time[0][0]));
DS1302_Write(DS1302_MINUTE, DEC_To_BCD(DS1302_Time[0][1]));
DS1302_Write(DS1302_SECOND, DEC_To_BCD(DS1302_Time[0][2]));
DS1302_Write(DS1302_WP, 0x81);//打开写保护
}
void DS1302_ReadTime()
//读取当前时间
{
unsigned char Temp = 0;
Temp = DS1302_Read(DS1302_HOUR);
DS1302_Time[0][0] = BCD_To_DEC(Temp);
Temp = DS1302_Read(DS1302_MINUTE);
DS1302_Time[0][1] = BCD_To_DEC(Temp);
Temp = DS1302_Read(DS1302_SECOND);
DS1302_Time[0][2] = BCD_To_DEC(Temp);
}
unsigned char DEC_To_BCD(unsigned char DEC_Num)
//十进制转BCD
{
return DEC_Num / 10 * 16 + DEC_Num % 10;
}
unsigned char BCD_To_DEC(unsigned char BCD_Num)
{
return BCD_Num / 16 * 10 + BCD_Num % 16;
}
void DS1302_TimeChange(unsigned char Change_Mode)
//Change_Mode为0时调整时钟,为1时调整闹钟
{
static unsigned char Time_Select = 0;//保证变量不被删除,防止在每一次调用函数的时候都置0
unsigned char Key_Num = 0;
static unsigned char last_Key_num = 0;
Key_Num = MatrixKey();
if((Key_Num == 1 && last_Key_num == 0) || (Key_Num == 5 && last_Key_num == 0)) Time_Select = 0;
if((Key_Num == 2 && last_Key_num == 0) || (Key_Num == 6 && last_Key_num == 0)) Time_Select = 1;
if((Key_Num == 3 && last_Key_num == 0) || (Key_Num == 7 && last_Key_num == 0)) Time_Select = 2;
switch(Time_Select)
{
case 0:
NixieTube_DisplayTime(Change_Mode, 1);
//合法性检查
if(Key_Num == 1 && last_Key_num == 0)
{
DS1302_Time[Change_Mode][0]++;
if(DS1302_Time[Change_Mode][0] > 23) DS1302_Time[Change_Mode][0] = 0;
}
if(Key_Num == 5 && last_Key_num == 0)
{
DS1302_Time[Change_Mode][0]--;
if(DS1302_Time[Change_Mode][0] < 0) DS1302_Time[Change_Mode][0] = 23;
}
last_Key_num = Key_Num;
break;
case 1:
NixieTube_DisplayTime(Change_Mode, 2);
if(Key_Num == 2 && last_Key_num == 0)
{
DS1302_Time[Change_Mode][1]++;
if(DS1302_Time[Change_Mode][1] > 59) DS1302_Time[Change_Mode][1] = 0;
}
if(Key_Num == 6 && last_Key_num == 0)
{
DS1302_Time[Change_Mode][1]--;
if(DS1302_Time[Change_Mode][1] < 0) DS1302_Time[Change_Mode][1] = 59;
}
last_Key_num = Key_Num;
break;
case 2:
NixieTube_DisplayTime(Change_Mode, 3);
if(Key_Num == 3 && last_Key_num == 0)
{
DS1302_Time[Change_Mode][2]++;
if(DS1302_Time[Change_Mode][2] > 59) DS1302_Time[Change_Mode][2] = 0;
}
if(Key_Num == 7 && last_Key_num == 0)
{
DS1302_Time[Change_Mode][2]--;
if(DS1302_Time[Change_Mode][2] < 0) DS1302_Time[Change_Mode][2] = 59;
}
last_Key_num = Key_Num;
break;
default:
last_Key_num = Key_Num;
}
}
unsigned char Is_Clock_Alarm()
//判断闹钟是否响起
{
unsigned char i = 0;
for(i = 0; i < 3; i++)
{
if(DS1302_Time[0][i] != DS1302_Time[1][i]) return 0;
}
return 1;
}
NixieDisplay.c
#include<STC89C5xRC.H>
#include "Delay.h"
#include "DS1302.h"
#include "Uart.h"
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -, ., C, L, O对应的段码
unsigned char NixieTable[15] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x40, 0x80, 0x39, 0x38, 0x3F};
void NixieTube_Display(unsigned char location, unsigned char num)
//分别传入显示位置、显示内容
{
switch(location)
//选择显示位置
{
case 1:
P24 = 1;
P23 = 1;
P22 = 1;
break;
case 2:
P24 = 1;
P23 = 1;
P22 = 0;
break;
case 3:
P24 = 1;
P23 = 0;
P22 = 1;
break;
case 4:
P24 = 1;
P23 = 0;
P22 = 0;
break;
case 5:
P24 = 0;
P23 = 1;
P22 = 1;
break;
case 6:
P24 = 0;
P23 = 1;
P22 = 0;
break;
case 7:
P24 = 0;
P23 = 0;
P22 = 1;
break;
case 8:
P24 = 0;
P23 = 0;
P22 = 0;
break;
}
P0 = NixieTable[num];
Delay(1);
//让数码管稳定显示,否则亮度会变低
//注意STC89C52RC的晶振频率为11.0592MHz,在创建延时函数时应注意选择
P0 = 0x00;
//清零
}
extern unsigned char Flicker_Mark;//调用外部变量
extern unsigned char Is_Clock_On;
void NixieTube_DisplayTime(unsigned char Time, unsigned char Flicker_Select)
//数码管显示时间
{
if(Flicker_Select == 0)//正常显示,不闪烁
{
NixieTube_Display(1, DS1302_Time[Time][0] / 10);
NixieTube_Display(2, DS1302_Time[Time][0] % 10);
NixieTube_Display(4, DS1302_Time[Time][1] / 10);
NixieTube_Display(5, DS1302_Time[Time][1] % 10);
NixieTube_Display(7, DS1302_Time[Time][2] / 10);
NixieTube_Display(8, DS1302_Time[Time][2] % 10);
if(Flicker_Mark == 0)
{
NixieTube_Display(3, 10);
NixieTube_Display(6, 10);
}
}
if(Flicker_Select == 1)//时闪烁
{
if(Flicker_Mark == 0)
{
NixieTube_Display(1, DS1302_Time[Time][0] / 10);
NixieTube_Display(2, DS1302_Time[Time][0] % 10);
}
NixieTube_Display(4, DS1302_Time[Time][1] / 10);
NixieTube_Display(5, DS1302_Time[Time][1] % 10);
NixieTube_Display(7, DS1302_Time[Time][2] / 10);
NixieTube_Display(8, DS1302_Time[Time][2] % 10);
NixieTube_Display(3, 10);
NixieTube_Display(6, 10);
}
if(Flicker_Select == 2)//分闪烁
{
NixieTube_Display(1, DS1302_Time[Time][0] / 10);
NixieTube_Display(2, DS1302_Time[Time][0] % 10);
if(Flicker_Mark == 0)
{
NixieTube_Display(4, DS1302_Time[Time][1] / 10);
NixieTube_Display(5, DS1302_Time[Time][1] % 10);
}
NixieTube_Display(7, DS1302_Time[Time][2] / 10);
NixieTube_Display(8, DS1302_Time[Time][2] % 10);
NixieTube_Display(3, 10);
NixieTube_Display(6, 10);
}
if(Flicker_Select == 3)//秒闪烁
{
NixieTube_Display(1, DS1302_Time[Time][0] / 10);
NixieTube_Display(2, DS1302_Time[Time][0] % 10);
NixieTube_Display(4, DS1302_Time[Time][1] / 10);
NixieTube_Display(5, DS1302_Time[Time][1] % 10);
if(Flicker_Mark == 0)
{
NixieTube_Display(7, DS1302_Time[Time][2] / 10);
NixieTube_Display(8, DS1302_Time[Time][2] % 10);
}
NixieTube_Display(3, 10);
NixieTube_Display(6, 10);
}
}
void NixieTube_DisplayAlarm()
//显示CLOC作为闹钟响起的标志
{
if(Flicker_Mark == 0)
{
NixieTube_Display(1, 12);
NixieTube_Display(2, 13);
NixieTube_Display(3, 14);
NixieTube_Display(4, 12);
}
}
串口发送时间函数
void Uart_SentTime()
//串口发送当前时间
{
unsigned char i = 0;
for(i = 0; i < 3; i++)
{
Uart_SendByte(DS1302_Time[0][i] / 10 + 48 );
Uart_SendByte(DS1302_Time[0][i] % 10 + 48 );
if(i < 2)
{
Uart_SendByte(58);
}
}
}