1、基本功能:
实现时间自动显示和音响报时
① 按实时时间显示时分秒
② 每隔60秒自动短响一声
③ 按日期显示年-月日
④ 每隔60分连续响多声,几点响几声
⑤ 可设置每天4次闹钟功能
⑥ 设有调整日期、时间和止闹按钮
⑦ 有按秒闪动的双LED
⑧ 闹铃需急促响多声
⑨ 日期与时间轮流显示。
51单片机_外部中断 与 定时/计数器中断
外部中断其实隶属于按键处理,外部中断最著名的当属IT0了,若IT0赋值为0,这个按钮其实是低电平触发;若IT0赋值为1,这个按钮其实是下边沿触发。
(1条消息) 51单片机外部中断 与 定时/计数器中断踏过山河,踏过海的博客-CSDN博客_51单片机定时器中断计数
定时器其实就是倒计时,而定时器T0和T1分别对应的引脚是P3.4和P3.5。
(插播一条广告:EA代表总闸,不管是外部中断,还是定时器中断要开启,首先都要EA开闸,外部中断或定时器中断也开闸才能运行.)
设计4次闹钟思路:(这里运用比较简单方式 数组)
设置一次 然后按下k2 表示设置成功 刷新 success1
设置第二次 继续按下k2 表示设置成功 刷新 success2
设置成功把数据存放到数组中。 判断数组成员的hour min 是否相等 相等的话就响应
代码实现
下面是代码演示:此项目主要也是基于定时器实现时间的计时,利用按钮调节模式切换 设置闹钟 调节时间等功能
main函数:
#include <REGX52.H>
#include "sys.h"
#include "mode.h"
#include "beep.h"
#include "LCD1602.h"
#include "key.h"
#include <stdio.h>uchar mod;uchar naomin=0;uchar naohour=2;uchar naosec=0;//闹钟的时分秒uchar sec=59,min=59,hour=5,day,month,countmin,counthour,n;
uchar day=2; uchar month=9; uint year=2022;
uint i,j;
int cont_res_flag = 0;
extern u8 stop_clock_flag;void main()
{Timer1Init();Int0Init();Int1Init();LCD_Init();LCD_ShowString(1,1," / / ");LCD_ShowString(2,1," : : ");while(1){Mode();key_control();}
}void Int0() interrupt 0 //外部中断 按键操作 关闭开启闹钟响
{delay(10);//消抖if(K3==0){Buzzer_Time(100);stop_clock_flag = ~stop_clock_flag;}}void Int1() interrupt 2
{delay(15);if(K4==0){if(mod==1){LCD_ShowString(1,1," / / ");LCD_ShowString(2,1," : : ");delay(10);mod=0;delay(10);}else{LCD_ShowString(2,1," : : ");delay(10);mod=1;}}
}int k = 0;
void Timer1() interrupt 3
{int j= 0;TH1=0xd8;TL1=0xf0;i++;k++;if(i==100)//1秒{sec++; i=0; j++; Led2_on();Led1_on();}if(j==10){j=0;Led1_on();}if(sec==60){min++; sec=0; n=0; countmin++;Buzzer_Time(50);}if(min==60){hour++; min=0; counthour++;cont_res_flag = 1;for( ; j <hour+1; j++){Buzzer_Time(100);delay(60);}}if(hour==24) {day++; hour=0;}if(day==30){month++; day=1;}if(month==12){year++; month=1;}if(k == 70){k = 0;if(hour == bufhour[0] && min == bufmin[0] && stop_clock_flag == 1){Buzzer_Time(100);}if(hour == bufhour[1] && min == bufmin[1] && stop_clock_flag == 1){Buzzer_Time(100);}if(hour == bufhour[2] && min == bufmin[2] && stop_clock_flag == 1){Buzzer_Time(100);}if(hour == bufhour[3] && min == bufmin[3] && stop_clock_flag == 1){Buzzer_Time(100);} }
}
sys.c
#include <REG52.H>sbit LED1=P2^0;
sbit LED2=P2^1;
//sbit LED3=P2^2;
//sbit LED4=P2^3;void delay(unsigned int xms) //@12.000MHz
{while(xms){unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i);xms--;}
}void time0_init(void)
{TMOD|=0X01;//选择为定时器0模式,工作方式1TH0=0XFC; //给定时器赋初值,定时1msTL0=0X18; ET0=1;//打开定时器0中断允许EA=1;//打开总中断TR0=1;//打开定时器
}void Timer1Init()
{TMOD=0x10;TH1=0xd8;TL1=0xf0; //10msET1=1;EA=1;TR1=1;
}void Int0Init()
{EX0=1;IT0=1;EA=1;
}void Int1Init()
{EX1=1;IT1=1;EA=1;
}void Led1_on()
{LED1=~LED1;delay(20);LED1=~LED1;
}void Led2_on()
{LED2=~LED2;delay(20);LED2=~LED2;
}//void Led3_on()
//{
// LED3=~LED3;
// delay(20);
// LED3=~LED3;
//}//void Led4_on()
//{
// LED4=~LED4;
// delay(20);
// LED4=~LED4;
//}
sys.h
#ifndef __SYS_H__
#define __SYS_H__#include <REGX52.H>typedef unsigned char uchar;
typedef unsigned int uint;sbit K1=P3^0;
sbit K2=P3^1;
sbit K3=P3^2;
sbit K4=P3^3;//蜂鸣器端口:
sbit Buzzer=P1^5;extern uchar sec,min,hour,day,month,countmin,counthour,mod,naomin,naohour,n,naosec;
extern uint year;void Led1_on();
void Led2_on();
void Led3_on();
void Led4_on();
void Timer1Init();
void Int0Init();
void Int1Init();
void delay(unsigned int xms);
void time0_init(void);#endif
beep.c
#include <REGX52.H>
#include <INTRINS.H>
#include "sys.h"/*** @brief 蜂鸣器私有延时函数,延时500us* @param 无* @retval 无*/
void Buzzer_Delay500us() //@12.000MHz
{unsigned char i;_nop_();i = 247;while (--i);
}/*** @brief 蜂鸣器发声* @param ms 发声的时长,范围:0~32767* @retval 无*/
void Buzzer_Time(unsigned int ms)
{unsigned int i;for(i=0;i<ms*2;i++){Buzzer=!Buzzer;Buzzer_Delay500us();}
}
beep.h
#ifndef __BEEP_H_
#define __BEEP_H_void Buzzer_Time(unsigned int ms);#endif
lcd1602.c
#include <REGX52.H>//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0//函数定义:
/*** @brief LCD1602延时函数,12MHz调用可延时1ms* @param 无* @retval 无*/
void LCD_Delay()
{unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i);
}/*** @brief LCD1602写命令* @param Command 要写入的命令* @retval 无*/
void LCD_WriteCommand(unsigned char Command)
{LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief LCD1602写数据* @param Data 要写入的数据* @retval 无*/
void LCD_WriteData(unsigned char Data)
{LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief LCD1602设置光标位置* @param Line 行位置,范围:1~2* @param Column 列位置,范围:1~16* @retval 无*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}
}/*** @brief LCD1602初始化函数* @param 无* @retval 无*/
void LCD_Init()
{LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动LCD_WriteCommand(0x01);//光标复位,清屏
}/*** @brief 在LCD1602指定位置上显示一个字符* @param Line 行位置,范围:1~2* @param Column 列位置,范围:1~16* @param Char 要显示的字符* @retval 无*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{LCD_SetCursor(Line,Column);LCD_WriteData(Char);
}/*** @brief 在LCD1602指定位置开始显示所给字符串* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param String 要显示的字符串* @retval 无*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);}
}/*** @brief 返回值=X的Y次方*/
int LCD_Pow(int X,int Y)
{unsigned char i;int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result;
}/*** @brief 在LCD1602指定位置开始显示所给数字* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~65535* @param Length 要显示数字的长度,范围:1~5* @retval 无*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief 在LCD1602指定位置开始以有符号十进制显示所给数字* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:-32768~32767* @param Length 要显示数字的长度,范围:1~5* @retval 无*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief 在LCD1602指定位置开始以十六进制显示所给数字* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~0xFFFF* @param Length 要显示数字的长度,范围:1~4* @retval 无*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber<10){LCD_WriteData(SingleNumber+'0');}else{LCD_WriteData(SingleNumber-10+'A');}}
}/*** @brief 在LCD1602指定位置开始以二进制显示所给数字* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~1111 1111 1111 1111* @param Length 要显示数字的长度,范围:1~16* @retval 无*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}
}
#ifndef __LCD1602_H__
#define __LCD1602_H__//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);#endif
mode.c
#include "sys.h"
#include "LCD1602.h"
#include "key.h"
#include "beep.h"
#include "stdio.h"u8 reduce_plus_flag = 1;//默认加u8 stop_clock_flag = 1;//默认1 开闹钟 0停闹钟uchar set_time = 0;
uchar bufhour[4] ;
uchar bufmin[4] ;void naozhongjishu()
{} void key_control()
{u8 key_code;if(K1==0){delay(50);if(K1==0){if(set_time > 3){set_time = 0;}Buzzer_Time(100);LCD_ShowString(1,1," set success!");LCD_ShowString(2,1," ");delay(80);bufhour[set_time] = naohour;bufmin[set_time] = naomin;set_time++;if(mod==1){LCD_ShowString(1,1," / / ");LCD_ShowString(2,1," : : ");delay(10);mod=0;delay(10);}else{LCD_ShowString(2,1," : : ");delay(10);mod=1;} }}key_code = key_matrix_ranks_scan();if(key_code == 1){if( reduce_plus_flag == 0)reduce_plus_flag =1;else{reduce_plus_flag =0;}}
//1.修改日期 if(key_code == 2 && reduce_plus_flag == 1) //年份加{year++;;}else if(key_code == 3 && reduce_plus_flag == 1) //月份加{month++;}else if(key_code == 4 && reduce_plus_flag == 1) //日 加{day++;}if(key_code == 2 && reduce_plus_flag == 0) //年份减{year--;}else if(key_code == 3 && reduce_plus_flag == 0) //月份减{month--;}else if(key_code == 4 && reduce_plus_flag == 0) //日 减{day--;}
//2.修改时间if(key_code == 6 && reduce_plus_flag == 1) //时加{if(mod==0){hour++;}else{naohour++;}}else if(key_code == 7 && reduce_plus_flag == 1) //分加{if(mod==0){min++;}else{naomin++;}}else if(key_code == 8 && reduce_plus_flag == 1) //秒 加{if(mod==0){sec++;}else{naosec++;}}if(key_code == 6 && reduce_plus_flag == 0) //时减{if(mod==0){hour--;}else{naohour--;}}else if(key_code == 7 && reduce_plus_flag == 0) //分减{if(mod==0){min--;}else{naomin--;}}else if(key_code == 8 && reduce_plus_flag == 0) //秒 减{if(mod==0){sec--;}else{naosec--;}}//3.判断时间格式if(sec==60){min++; sec=0; n=0; countmin++;}if(min==60){hour++; min=0; counthour++;}if(hour==24) {day++; hour=0;}if(day==30){month++; day=1;}if(month==12){year++; month=1;}if(naosec==60){naomin++;naosec=0;} if(naomin==60){naohour++;naomin=0;}if(naohour==24){naohour=0;}
}void Mode()
{if(mod==0){LCD_ShowNum(2,7,sec,2);LCD_ShowNum(2,4,min,2);LCD_ShowNum(2,1,hour,2);LCD_ShowNum(1,9,day,2);LCD_ShowNum(1,6,month,2); LCD_ShowNum(1,1,year,4);}else{LCD_ShowString(1,1,"clock: ");LCD_ShowNum(2,7,naosec,2);LCD_ShowNum(2,4,naomin,2);LCD_ShowNum(2,1,naohour,2);}
}void zhinao()
{n=100;
}
mode.h
#ifndef __MODE_H_
#define __MODE_H_void naozhongjishu();
void key_control();
void Mode();
void zhinao();extern int set_time ;
extern uchar bufhour[4] ;
extern uchar bufmin[4] ;//uchar bufhour[4] ;
//uchar bufmin[4] ;#endif
key.c (矩阵按键扫描)
#include "reg52.h"
#include "key.h"/*******************************************************************************
* 函 数 名 : delay_10us
* 函数功能 : 延时函数,ten_us=1时,大约延时10us
* 输 入 : ten_us
* 输 出 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{while(ten_us--);
}/*******************************************************************************
* 函 数 名 : key_matrix_ranks_scan
* 函数功能 : 使用行列式扫描方法,检测矩阵按键是否按下,按下则返回对应键值
* 输 入 : 无
* 输 出 : key_value:1-16,对应S1-S16键,0:按键未按下
*******************************************************************************/
u8 key_matrix_ranks_scan(void)
{u8 key_value=0;KEY_MATRIX_PORT=0xf7;//给第一列赋值0,其余全为1if(KEY_MATRIX_PORT!=0xf7)//判断第一列按键是否按下{delay_10us(1000);//消抖switch(KEY_MATRIX_PORT)//保存第一列按键按下后的键值 {case 0x77: key_value=1;break;case 0xb7: key_value=5;break;case 0xd7: key_value=9;break;case 0xe7: key_value=13;break;}}while(KEY_MATRIX_PORT!=0xf7);//等待按键松开 KEY_MATRIX_PORT=0xfb;//给第二列赋值0,其余全为1if(KEY_MATRIX_PORT!=0xfb)//判断第二列按键是否按下{delay_10us(1000);//消抖switch(KEY_MATRIX_PORT)//保存第二列按键按下后的键值 {case 0x7b: key_value=2;break;case 0xbb: key_value=6;break;case 0xdb: key_value=10;break;case 0xeb: key_value=14;break;}}while(KEY_MATRIX_PORT!=0xfb);//等待按键松开 KEY_MATRIX_PORT=0xfd;//给第三列赋值0,其余全为1if(KEY_MATRIX_PORT!=0xfd)//判断第三列按键是否按下{delay_10us(1000);//消抖switch(KEY_MATRIX_PORT)//保存第三列按键按下后的键值 {case 0x7d: key_value=3;break;case 0xbd: key_value=7;break;case 0xdd: key_value=11;break;case 0xed: key_value=15;break;}}while(KEY_MATRIX_PORT!=0xfd);//等待按键松开 KEY_MATRIX_PORT=0xfe;//给第四列赋值0,其余全为1if(KEY_MATRIX_PORT!=0xfe)//判断第四列按键是否按下{delay_10us(1000);//消抖switch(KEY_MATRIX_PORT)//保存第四列按键按下后的键值 {case 0x7e: key_value=4;break;case 0xbe: key_value=8;break;case 0xde: key_value=12;break;case 0xee: key_value=16;break;}}while(KEY_MATRIX_PORT!=0xfe);//等待按键松开return key_value;
}
key.h
#ifndef __KEY_H__
#define __KEY_H__
typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
#define KEY_MATRIX_PORT P1 //使用宏定义矩阵按键控制口
u8 key_matrix_ranks_scan(void);
#endif