一.任务介绍
1.运用知识
本次任务运用到的知识有矩阵键盘、定时器、DS1302时钟,附加任务运用到的知识有,点阵屏及串口通信
2.概况
本次任务要求制作一个时钟显示确定时间,并可以通过矩阵键盘来调节时钟显示
附加要求时钟信息通过串口通讯用电脑接收,设计一个闹钟,当闹钟时间到了则让点阵屏闪烁
显示部分:
数字信息的显示用到数码管显示的知识,短横线的闪烁则需要用到定时器模块,以1s为一个周期,按下S8进入显示模式
调节部分:
任务要求按下S8进入调节模式,在这里可以用Mode来判断进入哪个模式
任务要求按下S1 5 2 6 3 7进行对时间的加减,并要求按下时对应位进行闪烁,然后通过对应按键对时间进行加减
串口传输:
将每一次变化的信息都传入串口助手中,则可以定义一个变量,将当前时间赋值给它,当不等的时候输出信息,然后在将当前时间赋值给它,任务中我设置了按下按键K1对串口传输的开关。
有问题的是,在文本模式中,输出的信息是以ASCLL码转换,因此需要稍稍处理才能达到理想结果
闹钟:
设置个条件判断即可,但是因为引脚冲突,会产生原时钟显示不正常的现象,但是起到闹钟作用即可
二.程序源码
1.主文件
main.c
#include <REGX52.H>
#include "Delay.h"
#include "Timer.h"
#include "Keyscan.h"
#include "DS1302.h"
#include "Nixie.h"
#include "Uart.h"
#include "MatrixLED.h"
unsigned char KeyNum,Select,Mode,flag1,flag2,flag3,flag4;
void ShowTime(){
DS1302_ReadTime();
Nixie(1,DS1302_Time[3]/10);
Nixie(2,DS1302_Time[3]%10);
if(flag1){ //短横线闪烁
Nixie(3,11);
Nixie(6,11);
}else{
Nixie(3,12);
Nixie(6,12);
}
Nixie(4,DS1302_Time[4]/10);
Nixie(5,DS1302_Time[4]%10);
Nixie(7,DS1302_Time[5]/10);
Nixie(8,DS1302_Time[5]%10);
}
void SetTime(){
if(Select==0){ //进入该模式全部不动
Nixie(1,DS1302_Time[3]/10);
Nixie(2,DS1302_Time[3]%10);
Nixie(3,11);
Nixie(6,11);
Nixie(4,DS1302_Time[4]/10);
Nixie(5,DS1302_Time[4]%10);
Nixie(7,DS1302_Time[5]/10);
Nixie(8,DS1302_Time[5]%10);
}
if(KeyNum==1||KeyNum==5) Select=3; //调节时
if(Select==3){
if(Select==3&&flag2){Nixie(1,DS1302_Time[3]/10);Nixie(2,DS1302_Time[3]%10);} //时闪烁
Nixie(4,DS1302_Time[4]/10);
Nixie(5,DS1302_Time[4]%10);
Nixie(7,DS1302_Time[5]/10);
Nixie(8,DS1302_Time[5]%10);
Nixie(3,11);
Nixie(6,11);
//调节时加减及越界判断
if(KeyNum==1){
DS1302_Time[Select]++;
if (DS1302_Time[3] > 23) DS1302_Time[3] = 0;
}
else if(KeyNum==5){
DS1302_Time[Select]--;
if (DS1302_Time[3] < 0) DS1302_Time[3] = 23;
}
}
if(KeyNum==2||KeyNum==6) Select=4; //调节分
if(Select==4){
if(Select==4&&flag2){Nixie(4,DS1302_Time[4]/10);Nixie(5,DS1302_Time[4]%10);} //分闪烁
Nixie(1,DS1302_Time[3]/10);
Nixie(2,DS1302_Time[3]%10);
Nixie(7,DS1302_Time[5]/10);
Nixie(8,DS1302_Time[5]%10);
Nixie(3,11);
Nixie(6,11);
//调节分加减及越界判断
if(KeyNum==2){
DS1302_Time[Select]++;
if (DS1302_Time[4] > 59) DS1302_Time[4] = 0;
}
else if(KeyNum==6){
DS1302_Time[Select]--;
if (DS1302_Time[4] < 0) DS1302_Time[4] = 59;
}
}
if(KeyNum==3||KeyNum==7) Select=5; //调节秒
if(Select==5){
if(Select==5&&flag2){Nixie(7,DS1302_Time[5]/10);Nixie(8,DS1302_Time[5]%10);} //秒闪烁
Nixie(4,DS1302_Time[4]/10);
Nixie(5,DS1302_Time[4]%10);
Nixie(1,DS1302_Time[3]/10);
Nixie(2,DS1302_Time[3]%10);
Nixie(3,11);
Nixie(6,11);
//调节秒加减及越界判断
if(KeyNum==3){
DS1302_Time[Select]++;
if (DS1302_Time[5] > 59) DS1302_Time[5] = 0;
}
else if(KeyNum==7){
DS1302_Time[Select]--;
if (DS1302_Time[5] < 0) DS1302_Time[5] = 59;
}
}
}
void main(){
//时钟 定时器 串口初始化
DS1302_Init();
Timer0_Init();
Uart1_Init();
//设置时间
DS1302_SetTime();
while(1){
KeyNum=Key(); //读取按键
if(KeyNum==8) {Mode=0;Select=0;DS1302_SetTime();}
else if(KeyNum==4) {Mode=1;}
switch(Mode){ //模式选择
case 0:ShowTime();break;
case 1:SetTime();break;
}
if(KeyNum==17) flag4=~flag4;
if(flag4){ //通过串口将信息传给电脑,可通过K1进行开启和关闭
if(flag3!=DS1302_Time[5]){
unsigned char i;
for(i=3;i<6;i++){
if(DS1302_Time[i]>=0&&DS1302_Time[i]<10){
Uart1_SendByte(48);
Uart1_SendByte(DS1302_Time[i]+48);
if(i==3||i==4) Uart1_SendByte(58);
}
if(DS1302_Time[i]>9){
Uart1_SendByte(DS1302_Time[i]/10+48);
Uart1_SendByte(DS1302_Time[i]%10+48);
if(i==3||i==4) Uart1_SendByte(58);
}
}
flag3=DS1302_Time[5];
}
}
if(DS1302_Time[3]==11&&DS1302_Time[4]==45&&DS1302_Time[5]==14){ //设置闹钟,时间是11:45:14
unsigned char j;
for(j=0;j<8;j++){
MatrixLED_Show(j,0xFF);
}
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0count;
TL0 = 0x66;
TH0 = 0xFC;
T0count++;
if(T0count>=500){
T0count=0;
if(Mode==0) flag1=!flag1; //控制显示模式短横线闪烁
if(Mode==1) flag2=!flag2; //控制调节模式数字闪烁
}
}
2.相关模块
1.Delay.c
#include <INTRINS.H>
void Delay(unsigned int ms) //@11.0592MHz
{
unsigned char data i, j;
while(ms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
2.Timer.c
#include <REGX52.H>
//定时器0配置
void Timer0_Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
/*void Timer0_Routine() interrupt 1
{
static unsigned int T0count;
TL0 = 0x66;
TH0 = 0xFC;
T0count++;
if(T0count>=){
T0count=0;
}
}
*/
//定时器1配置
void Timer1_Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TL1 = 0x66; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1=1;
EA=1;
PT0=0;
}
/*
void Timer1_Routine() interrupt 3
{
static unsigned int T1count;
TL1 = 0x66;
TH1 = 0xFC;
T1count++;
if(T1count>=){
}
}
*/
3.Keyscan.c
#include <REGX52.H>
#include "Delay.h"
/**
* @brief 矩阵键盘、独立按键读取按键键码,矩阵键盘范围为1~16,独立按键范围为17~20
* @param 无
* @retval KeyNumber 按下按键的键码值
如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回0
*/
unsigned char Key()
{
unsigned char KeyNumber=0;
P1=0xFF;
P1_3=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}
P1=0xFF;
P1_2=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}
P1=0xFF;
P1_1=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}
P1=0xFF;
P1_0=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=8;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}
if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=17;}
if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=18;}
if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=19;}
if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=20;}
return KeyNumber;
}
4.DS1302.c
#include <REGX52.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_YEAR 0x8C
#define DS1302_DAY 0x8A
#define DS1302_WP 0x8E
char DS1302_Time[]={23,12,12,12,0,0,2};
//年月日时分秒,星期,写保护
unsigned char DS1302_Parameter[]={DS1302_YEAR,DS1302_MONTH,DS1302_DATE,DS1302_HOUR,DS1302_MINUTE,DS1302_SECOND,DS1302_DAY,DS1302_WP};
/**
* @brief DS1302初始化
* @param 无
* @retval 无
*/
void DS1302_Init(){DS1302_CE=0;DS1302_SCLK=0;}
/**
* @brief 写入一个字节
* @param Command 命令标识,用于判断写入的地址
Data 写入的字节
* @retval 无
*/
void DS1302_WriteByte(unsigned char Command,unsigned char Data){
unsigned char i;
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;
}
/**
* @brief 读取一个字节
* @param Command命令标识
* @retval 读取出的字节
*/
unsigned char DS1302_ReadByte(unsigned char Command){
unsigned char i,Data=0x00;
Command|=0x01;
DS1302_CE=1;
for(i=0;i<8;i++){
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0;
DS1302_SCLK=1;
}
for(i=0;i<8;i++){
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO){Data|=(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0;
return Data;
}
/**
* @brief BCD码转换
* @param Mod 转为()进制 10为转换为十进制,16为转换为16进制
Data 需要转换的数据
* @retval 转换后的结果
*/
unsigned char BCD_Transform(unsigned char Mod,unsigned char Data){
if(Mod==10){Data=Data/16*10+Data%16;}
if(Mod==16){Data=Data/10*16+Data%10;}
return Data;
}
/**
* @brief 设置时间
* @param 无
* @retval 无
*/
void DS1302_SetTime(){
unsigned char i;
DS1302_WriteByte(DS1302_Parameter[7],0x00);
for(i=0;i<7;i++){
DS1302_WriteByte(DS1302_Parameter[i],BCD_Transform(16,DS1302_Time[i]));
}
DS1302_WriteByte(DS1302_Parameter[7],0x80);
}
/**
* @brief 写入时间
* @param 无
* @retval 无
*/
void DS1302_ReadTime(){
unsigned char i;
for(i=0;i<7;i++){
DS1302_Time[i]=BCD_Transform(10,DS1302_ReadByte(DS1302_Parameter[i]));
}
}
5.Nixie.c
#include <REGX52.H>
#include "Delay.h"
//10是U,11是-,12是不显示
unsigned char Nixie_arr[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x3E,0x40,0x00};
//数码管显示
void Nixie(unsigned char location, num){
switch(location){
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
P0=Nixie_arr[num];
//消影
Delay(1);
P0=0x00;
}
6.Uart.c
#include <REGX52.H>
#include "DS1302.h"
/**
* @brief 串口初始化 //4800bps@11.0592MHz
* @param 无
* @retval 无
*/
void Uart1_Init(void)
{
PCON |= 0x80; //使能波特率倍速位SMOD
SCON = 0x40; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xF4; //设置定时初始值
TH1 = 0xF4; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
}
/**
* @brief 串口发送一个字节数据
* @param Byte 要发送的字节数据
* @retval 无
*/
void Uart1_SendByte(unsigned char Byte){
SBUF=Byte;
while(TI==0);
TI=0;
}
7.MatrixLED.c
#include <REGX52.H>
#include "Delay.h"
sbit RCLK_=P3^5;
sbit SRCLK_=P3^6;
sbit SER_=P3^4;
#define MATRIX_LED_PORT P0
/**
* @brief 点阵屏初始化
* @param 无
* @retval 无
*/
void Matrix_Init(){RCLK_=0;SRCLK_=0;}
/**
* @brief 74HC595写入一个字节
* @param Byte 写入的字节
* @retval 无
*/
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++){
SER_=Byte&(0x80>>i); //读位
SRCLK_=1; //移位
SRCLK_=0;
}
RCLK_=1; //锁存
RCLK_=0;
}
/**
* @brief LED点阵屏显示一列数据
* @param column 要选择的列,范围:0~7
Data 选择列显示的数据,高位在上,1为亮0为灭
* @retval
*/
void MatrixLED_Show(unsigned char column,Data){
_74HC595_WriteByte(Data);
MATRIX_LED_PORT=~(0x80>>column);
Delay(1);
MATRIX_LED_PORT=0xFF;
}