基于 51 单片机串口通信的电子钟设计与实现

课程大作业-基于 51 单片机的串口电子钟

基于 51 单片机的串口电子钟

  • 一、电子钟系统设计方案
  • 1. 项目概述
  • 2. 工程文件组成
  • 3.程序运行流程
  • 3.1. 系统初始化
  • 3.2. 主循环
  • 3.3. 中断服务
  • 3.4. 定时器控制
  • 3.5. 用户交互
  • 3.6. 显示更新
  • 3. 7. EEPROM存储
  • 3.8. 报警功能
  • 3.9. 辅助功能
  • 4、注意事项
  • 5、缺陷
  • 6、51单片机型号
  • 二、代码展示
  • 1、主模块mian.c
  • 2、头文件及函数模块
  • 2.1、全局变量定义头文件"globals.h"
  • 2.2、延时函数头文件"Delay.h"及"Delay.c"
  • 2.3、"EEPROM.h"头文件及"EEPROM.c"
  • 2.4、数码管显示头文件"segmentFlash.h"及"segmentFlash.c"
  • 2.5、星期显示头文件"WebDisplay.h"及"WebDisplay.c"
  • 2.6、外部中断服务函数头文件"InitInterrupt.h"及"InitInterrupt.c"
  • 2.7、时器中断服务函数头文件"InitTimer.h"及"InitTimer.c"
  • 2.8、串口中断服务函数头文件"InitTimer.h"及"InitTimer.c"
  • 2.9、流水灯实现函数头文件"Lightflash.h"及"Lightflash.c"
  • 三、致谢
  • 一、电子钟系统设计方案

    1. 项目概述

    本项目旨在设计一个基于51单片机的电子钟系统,最终实现以下功能:

  • 1.在串口助手上每间隔 1 秒打印一次日期与时间
  • 2.流水灯
  • 3.能在数码管上显示当前的小时、分钟值,并能够使用按键切换显示为月、日
  • 4.能使用串口助手发送命令快速调整电子钟的日期与时间
  • 5.能设置闹钟,到达设定时间蜂时鸣器鸣叫,并且按下按键后能够让蜂鸣器停止鸣叫
  • 6.能将断电时的日期与时间保存至 EEPROM,并且能够在重新上电后恢复断电前的日期与时间,继续计时
  • 7.自行设计一个串口助手,能与电子钟通信,并且能快速对日期、时间以及闹钟功能进行设置
  • 2. 工程文件组成

    工程文件主要由以下模块组成:

  • 主函数(main.c):负责协调各个子模块和硬件组件,确保电子钟的正常运行和用户交互
  • 全局变量(globals.h):存储系统状态和时间信息。
  • EEPROM 操作模块(EEPROM.c/.h):负责与EEPROM通信,存储和读取时间设置。
  • 定时器模块(InitTimer.c/.h):管理时间更新的定时器中断。
  • 中断模块(InitInterrupt.c/.h):处理外部中断,如按键输入。
  • 数码管控制模块(SegmentFlash.c/.h):控制数码管显示时间或日期。
  • 串口通信模块(InitUART.c/.h):处理串口通信,用于时间同步和设置。
  • 流水灯控制模块(Lightflash.c/.h):控制LED流水灯效果。
  • 3.程序运行流程

    电子钟程序的运行主要依赖于几个关键组件的协同工作:主循环、中断服务、定时器控制、用户交互和显示更新。以下是对电子钟程序运行流程的简要分析:

    3.1. 系统初始化

    在程序的开始,会进行一系列的初始化操作:

  • 初始化单片机的I/O端口,设置LED、按键、数码管等的端口状态。
  • 初始化定时器,配置定时器的模式和初始值,用于后续的时间计数。
  • 初始化串口通信,设置波特率和相关控制寄存器,以便进行数据的发送和接收。
  • 初始化外部中断,配置外部中断的触发方式和优先级。
  • 初始化EEPROM,准备用于存储时间设置等数据。
  • 3.2. 主循环

    程序进入一个无限循环,这是电子钟的核心运行逻辑:

  • 显示控制:根据用户设置或默认行为,更新数码管显示,可能是时间或日期。
  • 按键检测:检测按键输入,允许用户设置时间或激活闹钟等。
  • 报警检测:检查当前时间是否与设定的闹钟时间匹配,如果是,则触发蜂鸣器。
  • 3.3. 中断服务

    中断服务是电子钟响应外部事件的关键:

  • 定时器中断:定时器溢出时触发中断,用于递增秒、分、时等时间变量。
  • 外部中断:按键按下时触发中断,用于切换显示模式或输入设置。
  • 串口中断:接收到串口数据时触发中断,用于接收外部发送的时间设置命令。
  • 3.4. 定时器控制

    定时器是电子钟时间控制的核心:

  • 秒递增:定时器每次溢出递增秒数,当秒数达到60时,清零秒数并递增分钟。
  • 分钟和小时递增:类似地,分钟和小时的递增逻辑也是基于定时器中断。
  • 日、月、年更新:根据月份和闰年情况,更新日、月、年。
  • 3.5. 用户交互

    用户通过按键和串口与电子钟进行交互:

  • 设置时间:用户可以通过串口设置当前的时间。
  • 设置闹钟:用户可以串口设置设置闹钟时间。
  • 切换显示:用户可以通过按键在时间显示和日期显示之间切换。
  • 3.6. 显示更新

    电子钟的显示更新通常由以下触发:

  • 数码管刷新:定时器中断中可能包含刷新数码管的逻辑,以更新显示的时间或日期。
  • 按键操作:按键操作后,程序会更新数码管显示,以反映新的时间设置或报警状态。
  • 3. 7. EEPROM存储

    电子钟使用EEPROM存储时间设置,以保持在断电后时间不丢失:

  • 写入EEPROM:在时间更新后,将新的时间写入EEPROM。
  • 从EEPROM读取:在系统初始化时,从EEPROM读取存储的时间,并设置为当前时间。
  • 3.8. 报警功能

    当设定的闹钟时间到达时,电子钟会通过蜂鸣器发出声音提醒用户:

  • 闹钟匹配:程序会不断检查当前时间是否与闹钟时间匹配。
  • 蜂鸣器控制:一旦匹配,程序将控制蜂鸣器发声。
  • 3.9. 辅助功能

  • 流水灯效果:程序可能包含流水灯控制逻辑,用于指示状态或美化显示。
  • 串口显示:单片机会将时间日期和所设置的闹钟等信息通过串口发送到电脑。
  • 4、注意事项

  • 通过按动KEY1可实现数码管上月日、小时分钟的切换
  • 当闹钟响铃时,通过按动KEY3关闭响铃
  • 通过串口修改电子钟时间和设置闹钟时,需要按照以下图所示的格式,并且在发送数据之前需要先按下单片机的KEY2按键,触发外部中断1,重置串口通信信息写入位置,实现命令更新(否者可能会导致电子钟出现紊乱)
    通过串口调节时间
  • 通过串口设置闹钟

    5、缺陷

    本次项目最终仅实现了前6个功能,最后一个功能(自行设计一个串口助手)尚未实现。

    6、51单片机型号

    设计过程当中所用到的单片机为乐育STC89C52学习板
    单片机实物图

    二、代码展示

    1、主模块mian.c

    /*********************************************************************************************************
    设计任务: 基于51单片机设计的电子钟
    模块名称: main.c
    作者:liujiajing
    日期:2024年5月24日
    *********************************************************************************************************/
    
    /*********************************************************************************************************
                                  头文件和引脚定义
    **********************************************************************************************************/
    #include <reg52.h>            // 包含51单片机的寄存器定义
    #include <stdio.h>            // 标准输入输出库
    #include "globals.h"          // 全局变量声明
    #include "InitUART.h"         //串口中断服务函数
    #include "EEPROM.h"           // EEPROM操作函数
    #include "InitTimer.h"        // 定时器初始化相关函数
    #include "InitInterrupt.h"    // 中断初始化相关函数
    #include "Delay.h"            // 延时函数
    #include "SegmentFlash.h"     // 数码管闪烁控制函数
    #include "WebDisplay.h"       // 网络显示相关函数
    #include "Lightflash.h"       // 流水灯控制函数
    
    sbit LED1 = P2^4;             //定义LED1
    sbit LED2 = P2^5;             //定义LED2
    sbit LED3 = P2^6;             //定义LED3
    sbit LED4 = P2^7;             //定义LED4
    sbit BEEP = P1^0;             //定义蜂鸣器BEEP
    sbit KEY3 = P3^4;             //定义按键KEY3
    
    /*********************************************************************************************************
                                  全局变量的定义
    *********************************************************************************************************/
    unsigned char seg_flash = 0;                                         // 用于控制数码管用于控制显示模式,当seg_flash=0时显示小时分钟,当seg_flash=1时显示月日
    unsigned char s_num = 0;                                             // 串口数据接收计数器
    unsigned int year, month, day, hour, min, sec;                       // 存储时间的变量
    unsigned int what_d;                                                 // 控制星期显示的变量
    unsigned int led = 0;                                                // 控制流水灯的变量
    unsigned int set_month, set_day, set_hour, set_min;                  // 存储闹钟时间的变量
    unsigned char alarm;                                                 // 闹钟控制变量
    unsigned char set;                                                   // 设置控制变量
    unsigned char arrBUFF[22];                                           // 用于存储串口接收的中间数据
    unsigned char arr_Days[12]={31,29,31,30,31,30,31,31,30,31,30,31};    // 定义了一个数组,存储每个月的天数
    
    /*********************************************************************************************************
                                   主函数
    *********************************************************************************************************/
    void main() 
    {
    	seg_flash = 0;      //初始数码管控制变量为0
    	P2 = 0xEF;          //定义P2寄存器,点亮LED灯
    	led = 0;            //定义LED灯控制变量初始化为0
    
    	// 配置中断和定时器
    	InitInterrupt1(); // 初始化外部中断1
    	InitInterrupt0(); // 初始化外部中断0
    	InitTimer0();     // 初始化定时器0
    	InitUART();       // 初始化串口通信
    
    	if(0x0000 == IAPByteRead(0x2C00)) {   //如果数据已被初始化
    		year = IAPByteRead(0x2000);
    		month = IAPByteRead(0x2200);
    		day = IAPByteRead(0x2400);
    		hour = IAPByteRead(0x2600);
    		min = IAPByteRead(0x2800);
    		sec = IAPByteRead(0x2A00);
    	 } else {
    		IAPByteWrite(0x2C00,0x0000);      //若无,初始化参数
    		year = 2024;
    		month = 5;
    		day = 20;
    		hour = 13;
    		min = 14;
    		sec = 0;
      	}
    
    
    	//主循环
    	while(1) {
    
    		if(1==set && arrBUFF[0]=='s' && arrBUFF[1]=='e' && arrBUFF[2]=='t' && '_'==arrBUFF[20]) {
    			set = 0;
    
    			year = ((arrBUFF[4] - 48) * 1000 )+ ((arrBUFF[5] - 48) * 100) + ((arrBUFF[6] - 48) * 10) + (arrBUFF[7] - 48);//计算设定时间年、月、日、时、分
    			month = ((arrBUFF[9] - 48) * 10) + (arrBUFF[10] - 48);
    			day = ((arrBUFF[12] - 48) * 10) + (arrBUFF[13] - 48);
    			hour = ((arrBUFF[15] - 48) * 10) + (arrBUFF[16] - 48);
    			min = ((arrBUFF[18] - 48) * 10) + (arrBUFF[19] - 48);
    
    			IAPSectorErase(0x2000);       //将擦除起始地址为0x2000的扇区
    			IAPByteWrite(0x2000,year);    //将year的值写入起始地址为0x2000的扇区
    
    			IAPSectorErase(0x2200);       //擦除其实地址为0x2200的扇区
    			IAPByteWrite(0x2200,month);   //将month写入起始地址为0x2200的扇区
    
    			IAPSectorErase(0x2400);       //擦除起始地址为0x2400的扇区
    			IAPByteWrite(0x2400,day);     //将day写入起始地址为0x2400的扇区
    
    			IAPSectorErase(0x2600);       //擦除其实地址为0x2600的扇区
    			IAPByteWrite(0x2600,hour);    //将hour值写入起始地址为0x2600的扇区
    
    			IAPSectorErase(0x2800);       //擦除起始地址为0x2800的扇区
    			IAPByteWrite(0x2800,min);     //将min值写入起始地址为0x2800的扇区
    		}
    
    		else if(1==set && 'a'==arrBUFF[0] && 'l'==arrBUFF[1] && 'a'==arrBUFF[2] && 'r'==arrBUFF[3] && 'm'==arrBUFF[4] && '_'==arrBUFF[17]) { //判断串口发送命令是否满足时间设定条件
    			alarm = 1;//设定闹钟控制参数为1
    
    			set_month = ((arrBUFF[6] - 48) * 10) + (arrBUFF[7] - 48);//计算设定闹钟时间月、日、时、分
    			set_day = ((arrBUFF[9] - 48) * 10) + (arrBUFF[10] - 48);
    
    			set_hour = ((arrBUFF[12] - 48) * 10) + (arrBUFF[13] - 48);
    			set_min = ((arrBUFF[15] - 48) * 10) + (arrBUFF[16] - 48);
    
    			set = 0;//设置时间设置控制参数为1
    
    		}
    
    		if(0 == seg_flash) {
    			SegmentFlash(hour, min);//按键未被按下时显示时间
    		} else if(1 == seg_flash) { //按键被按下显示日期
    			SegmentFlash(month, day);
    		}
    
        
       //蜂鸣器鸣叫实现闹钟功能 
    		if(month == set_month && day == set_day && hour == set_hour && min == set_min && 1 == alarm) { 
    			BEEP = 0;
    		}
      
        //按下按键3停止闹钟
    		if(0 == KEY3) { 
    			DelayNms(50);
    
    			if(0 == KEY3) {
    				alarm = 0;//
    				BEEP = 1;
    			}
    		}
        
        
        
    	}
    }
    
    

    2、头文件及函数模块

    2.1、全局变量定义头文件"globals.h"

    /********************globals.h***********************/
    #ifndef _GLOBALS_H_
    #define _GLOBALS_H_
    
    // 'seg_flash' 是一个全局变量,用于数码管控制显示模式。
    extern unsigned char seg_flash; 
    
    // 's_num' 是一个全局计数器,用于跟踪串口数据接收的进度或其他计数功能。
    extern unsigned char s_num;
    
    // 'arr_Days' 是一个数组,用于存储每个月的天数。这有助于处理日期计算,特别是月份变换时。
    extern unsigned char arr_Days[];
    
    // 下面的变量用于存储电子钟的当前时间。
    // 'year' 存储当前年份。
    // 'month' 存储当前月份。
    // 'day' 存储当前日期。
    // 'hour' 存储当前小时(24小时制)。
    // 'min' 存储当前分钟。
    // 'sec' 存储当前秒。
    extern unsigned int year, month, day, hour, min, sec;
    
    // 'what_d' 是用于存储星期几的变量。
    // 'led' 是用于控制LED或流水灯的变量。
    extern unsigned int what_d, led;
    
    // 下列变量用于设置闹钟的时间。
    // 'set_month' 存储闹钟的月份。
    // 'set_day' 存储闹钟的日期。
    // 'set_hour' 存储闹钟的小时。
    // 'set_min' 存储闹钟的分钟。
    extern unsigned int set_month, set_day, set_hour, set_min;
    
    // 'alarm' 是一个标志变量,用于指示闹钟是否激活。
    extern unsigned char alarm;
    
    // 'set' 是一个控制设置模式的变量,用于指示电子钟是否处于设置状态。
    extern unsigned char set;
    
    // 'arrBUFF' 是一个用于存储串口接收到的数据的缓冲区。
    extern unsigned char arrBUFF[];
    
    #endif // _GLOBALS_H_
    

    2.2、延时函数头文件"Delay.h"及"Delay.c"

    /************Delay.h************/
    #ifndef __DELAY_H__
    #define __DELAY_H__
    
    void DelayNms(int nms);
    
    #endif
    
    
    /******************************Delay.c***********************************/
    #include <reg52.h>
    #include "Delay.h"
    
    /*********************************************************************************************************
    函数名称:DelyNms
    函数功能:延时函数
    输入参数:void
    输出参数:void
    返回值:void
    *********************************************************************************************************/
    void DelayNms(int nms)//延时函数实现
    {
    	unsigned int i;
    	
    	for(i=0; i<nms; i++)
    	{
    	
    	}
    }
    
    

    2.3、“EEPROM.h"头文件及"EEPROM.c”

    /******************************** *****EEPROM.h**********************************************************/
    
    /*********************************************************************************************************
    特殊功能寄存器定义
    *********************************************************************************************************/
    
    sfr ISP_DATA  = 0XE2;    //
    sfr ISP_ADDRH = 0XE3;    //
    sfr ISP_ADDRL = 0XE4;    //
    sfr ISP_CMD   = 0XE5;    //
    sfr ISP_TRIG  = 0XE6;    //
    sfr ISP_CONTR = 0XE7;    //
    
    /*********************************************************************************************************
    枚举结构体定义
    *********************************************************************************************************/
    
    /*********************************************************************************************************
    API函数声明
    *********************************************************************************************************/
    void IAPSectorErase(unsigned int addr);                      //擦除指定扇区函数
    void IAPByteWrite(unsigned int addr, unsigned char dat);     //字节写入函数
    unsigned char IAPByteRead(unsigned int addr);                //字节读取函数
    
    
    /***********************************EEPROM.c***************************************/
    
    /*********************************************************************************************************
    包含头文件
    *********************************************************************************************************/
    #include <reg52.h>
    #include <EEPROM.h>
    
    /*********************************************************************************************************
    内部函数声明
    /*********************************************************************************************************/
    static void IAPTdigger();//触发IAP功能
    static void IAPDisable();//禁用IAP功能
    
    /*********************************************************************************************************
    内部函数实现
    /*********************************************************************************************************/
    static void IAPTrigger()//触发寄存器
    {
    	ISP_TRIG = 0x46;
    	ISP_TRIG = 0xB9;
    	
    }
    
    static void IAPDisable()
    {
    	ISP_CONTR = 0x00;
    	ISP_CMD = 0x00;
    	ISP_TRIG = 0x00;
    	
    }
    
    /*********************************************************************************************************
    API函数声明及实现
    /*********************************************************************************************************/
    unsigned char IAPByteRead(unsigned int addr)
    {
      unsigned char dat;        //定义数据缓存变量
      ISP_CONTR = 0x81;         //打开IAP功能,允许编程改变Flash,设置Flash操作等待时间
      ISP_CMD   = 0x01;         //允许对"Data Flash/EEPROM区"进行字节读取
      
      ISP_ADDRL = addr;         //IAP操作地址寄存器低位
      ISP_ADDRH = addr >> 8;    //IAP操作地址寄存器高位
    
      IAPTrigger();             //触发IAP功能
      dat = ISP_DATA;           //将需要读出的数据放进缓存变量
      IAPDisable();             //禁用IAP功能
      return dat;               //将读取到的数据作为返回值
    }
    void IAPSectorErase(unsigned int addr)
    {
      ISP_CONTR = 0x81;           //打开IAP功能,允许编程改变Flash,设置Flash操作等待时间
      ISP_CMD   = 0x03;           //允许对"Data Flash/EEPROM区"进行扇区擦除
    
      ISP_ADDRL = addr;           //写入IAP操作地址寄存器低位
      ISP_ADDRH = addr >> 8;      //写入IAP操作地址寄存器高位
    
      IAPTrigger();               //触发IAP功能
      IAPDisable();               //禁用IAP功能
    }
    void IAPByteWrite(unsigned int addr, unsigned char dat)
    {
      ISP_CONTR = 0x81;          //打开IAP功能,允许编程改变Flash,设置Flash操作等待时间
      ISP_CMD   = 0x02;          //允许对"Data Flash/EEPROM区"进行字节写入
    
      ISP_ADDRL = addr;          //IAP操作地址寄存器低位
      ISP_ADDRH = addr >> 8;     //IAP操作地址寄存器高位
      ISP_DATA = dat;            //将需要写入的数据放进ISP_DATA
    
      IAPTrigger();              //触发IAP功能
      IAPDisable();              //禁用IAP功能
    }
    
    

    2.4、数码管显示头文件"segmentFlash.h"及"segmentFlash.c"

    /****************************segmentFlash.h****************************/
    #ifndef __SEGMENTFLASH_H__
    #define __SEGMENTFLASH_H__
    
    void SegmentFlash(int num1, int num2);//数码管显示函数
    
    #endif
    
    
    /**************************************SegmentFlash.c*****************************************************
                                            数码管显示函数
    /*********************************************************************************************************/
    
    #include <reg52.h>
    #include "SegmentFlash.h"
    #include "Delay.h"
    
    sbit SegmentG1 = P2^3;//定义数码管1
    sbit SegmentG2 = P2^2;//定义数码管2
    sbit SegmentG3 = P2^1;//定义数码管3
    sbit SegmentG4 = P2^0;//定义数码管4
    
    static unsigned char s_arrNumber[] = 
    {0x03, 0x9f, 0x25, 0x0d, 0x99, 0x49, 0x41, 0x1f, 0x01, 0x09};//定义数码管显示数字0~9
    
    void SegmentFlash(int num1, int num2)//输入变量num1为 时|月 和 分|日
    {	
    	P0 = s_arrNumber[num1 / 10];
    	SegmentG1 = 0;
    	DelayNms(5);
    	SegmentG1 = 1;
    	
    	P0 = s_arrNumber[num1 % 10];
    	SegmentG2 = 0;
    	DelayNms(5);
    	SegmentG2 = 1;
    	
    	P0 = s_arrNumber[num2 / 10];
    	SegmentG3 = 0;
    	DelayNms(5);
    	SegmentG3 = 1;
    	
    	P0 = s_arrNumber[num2 % 10];
    	SegmentG4 = 0;
    	DelayNms(5);
    	SegmentG4 = 1;
    	
    }
    

    2.5、星期显示头文件"WebDisplay.h"及"WebDisplay.c"

    /********************************WebDisplay.h**************************************/
    #ifndef __WEBDISPLAY_H__
    #define __WEBDISPLAY_H__
    void WebDisplay(); // 星期显示函数       
    #endif
    
    /*************************************WebDisplay.c**********************************************************************************************************/
    #include <reg52.h>
    #include "WebDisplay.h"
    #include "globals.h"
    
    static unsigned int all_days;//定义总天数
    /*************************************************************************
                                          星期显示函数
    **************************************************************************/
    void WebDisplay()
    {
    	unsigned char i;                        //定义累加循环变量i
    	all_days = 0;
    	for(i = month; i > 1; i--)
    	{
    		all_days = all_days + arr_Days[i - 2];//前几月已过天数
    		
    	}
    	all_days = all_days + day;              //加上本月已过天数即为已过总天数
    	
    	what_d = all_days % 7;                  //计算星期几
    	
    }
    

    2.6、外部中断服务函数头文件"InitInterrupt.h"及"InitInterrupt.c"

    /*****************************InitInterrupt.h***********************************************/
    #ifndef _INITINTERRUPT_H_
    #define _INITINTERRUPT_H_
    
    #include "globals.h"
    
    void InitInterrupt1();      //外部中断1配置函数
    void InitInterrupt0();      //外部中断0配置函数
    
    void External0_Handler();   //外部中断0服务函数
    void External1_Handler();   //外部中断1服务函数
    
    #endif // _INITINTERRUPT_H_
    
    /**************************************InitInterrupt.c*****************************************************
                                       外部中断服务函数
    /*********************************************************************************************************/
    #include <reg52.h>
    #include "InitInterrupt.h"
    
    /*********************************************************************************************************
    内部函数声明
    /*********************************************************************************************************/
    void InitInterrupt0();//外部中断0配置函数
    void InitInterrupt1();//外部中断1配置函数
    void External0_Handler() ;
    void External1_Handler() ;
    
    /*********************************************************************************************************
    内部函数实现
    /*********************************************************************************************************/
    void InitInterrupt0()
    {
    	IT0 = 1;//设置外部中断0的触发方式为沿下降沿触发
    	EX0 = 1;//打开外部中断0的中断允许
    	
    	EA = 1;//打开总中断允许
    	
    }
    
     void InitInterrupt1()
    {
    	IT1 = 1;//设置外部中断1的触发方式为沿下降沿触发
    	EX1 = 1;//打开外部中断1的中断允许
    	
    	EA = 1;//打开总中断允许
    	
    }
    
    /*********************************************************************************************************
    外部中断0服务函数
    /*********************************************************************************************************/
    void External0_Handler() interrupt 0 //编写外部中断0服务函数,当外部中断触发时d值取反,数码管显示日期
    {
    			if(0 == seg_flash)
    			{
    				seg_flash = 1;
    			}
    			else if(1 == seg_flash)
    			{
    				seg_flash = 0;
    			}
    }
    
    /*********************************************************************************************************
    外部中断1服务函数
    /*********************************************************************************************************/
    void External1_Handler() interrupt 2//通过外部中断1重置串口通信信息写入位置,实现命令更新
    {
    	s_num = 0;
    }
    
    

    2.7、时器中断服务函数头文件"InitTimer.h"及"InitTimer.c"

    /*************************InitTimer.h*********************************/
    #ifndef __INITUART_H_
    #define __INITUART_H_
    
    void InitUART();      //串口配置函数
    void UART_handler();  //串口中断服务函数
    
    #endif 
    
    /************************************InitTimer.c*********************************************************************/
    #include <reg52.h>
    #include <stdio.h>
    #include "EEPROM.h"
    #include "InitTimer.h"
    #include "InitInterrupt.h"
    #include "globals.h"
    #include "Lightflash.h"
    #include "WebDisplay.h"
    
    /*********************************************************************************************************
    内部函数声明
    /*********************************************************************************************************/
    void InitTimer0();//定时器0配置函数
    void Timer0_Handler();//定时器中断服务函数
    
    /*********************************************************************************************************
    InitTimer0定时器0配置函数
    /*********************************************************************************************************/
    void InitTimer0() {
    	TMOD |= 0x01;//设置定时器为工作模式1
    	TH0 = 0xFC;//设置定时器高8位
    	TL0 = 0x18;//设置定时器0计数值的低8位,1ms后溢出
    	TR0 = 1;//打开计时器
    
    	ET0 = 1;//打开定时器0
    	EA = 1;//打开总中断允许
    }
    
    /*********************************************************************************************************
    定时器中断服务函数
    /*********************************************************************************************************/
    void Timer0_Handler() interrupt 1 { //编写定时器0的中断服务函数
    	static unsigned int s_iCounter;//定义变量控制秒值
    
    	InitInterrupt0();
    
    	TH0 = 0XFC;
    	TL0 = 0X18;
    
    	s_iCounter++;
    
    	if(s_iCounter>=900) {               //当计时1秒时
    		s_iCounter = 0;                   //计时控制变量清零
    		sec++;                            //秒值加1
    
    		Lightflash();                     //实现流水灯
    		led++;
    
    		IAPSectorErase(0x2A00);           //擦除起始地址为0x2A00的扇区
    		IAPByteWrite(0x2A00,sec);         //将sec的值写入该扇区
    
    		if(sec >= 60) {
    			sec = 0;
    			min++;
    
    			IAPSectorErase(0x2800);         //擦除起始地址为0x2800的扇区;
    			IAPByteWrite(0x2800,min);       //将min值写入起始地址为0x2800的扇区;
    		}
    
    		if(min >= 60) {
    			min = 0;
    			hour++;
    
    			IAPSectorErase(0x2600);         //擦除其实地址为0x2600的扇区
    			IAPByteWrite(0x2600,hour);      //将hour值写入起始地址为0x2600的扇区
    		}
    
    		if(hour >= 24) {
    			hour = 0;
    			day++;
    
    			IAPSectorErase(0x2400);         //擦除起始地址为0x2400的扇区;
    			IAPByteWrite(0x2400,day);       //将day写入起始地址为0x2400的扇区;
    		}
    
    		if(day > arr_Days[month-1]) {
    			day = 1;
    			month++;
    
    			IAPSectorErase(0x2200);         //擦除其实地址为0x2200的扇区
    			IAPByteWrite(0x2200,month);     //将month写入起始地址为0x2200的扇区
    		}
    
    		if(month > 12) {
    			month = 1;
    			year++;
    
    			IAPSectorErase(0x2000);         //将擦除起始地址为0x2000的扇区
    			IAPByteWrite(0x2000,year);      //将year的值写入起始地址为0x2000的扇区
    		}
    
    		printf("%d %d %d %d:%d:%d_", year, month, day, hour, min, sec);
    
    		WebDisplay();
    
    		switch(what_d) {                    //实现星期显示
    			case 1:
    				printf("monday\n");
    				break;
    
    			case 2:
    				printf("tuesday\n");
    				break;
    
    			case 3:
    				printf("wednesday\n");
    				break;
    
    			case 4:
    				printf("thursday\n");
    				break;
    
    			case 5:
    				printf("friday\n");
    				break;
    
    			case 6:
    				printf("saturday\n");
    				break;
    
    			case 0:
    				printf("sunday\n");
    				break;
    
    		}
    
    		printf("alarm:%d_%d_%d:%d\n",set_month, set_day, set_hour, set_min);//实现闹钟时间显示
    	}
    }
    
    
    

    2.8、串口中断服务函数头文件"InitTimer.h"及"InitTimer.c"

    /*************************InitTimer.h*********************************/
    #ifndef __INITUART_H_
    #define __INITUART_H_
    
    void InitUART();      //串口配置函数
    void UART_handler();  //串口中断服务函数
    
    #endif 
    
    /***************************************InitUART.c***********************************************************/
    #include <reg52.h>
    #include "InitUART.h"
    #include "globals.h"
    
    /*********************************************************************************************************
    内部函数声明
    /*********************************************************************************************************/
    void InitUART();      //串口配置函数
    void UART_handler();  //串口中断服务函数
    /*********************************************************************************************************
    InitUART串口配置函数
    /*********************************************************************************************************/
    void InitUART()
    {
    	ES = 1;           //打开串口接收中断允许
    	EA = 1;           //打开总中断允许
    	PT0 = 1;          //设置串口中断优先级为高优先级
    	
    	SCON = 0x50;      //设置串口为工作模式1,并打开接收允许
    	TMOD |= 0x20;     //设置定时器1为工作模式2
    	PCON = 0x80;      //设置比特率加倍
    	TL1 = 0xF3;       //设置定时器1计数初值
    	TH1 = TL1;        //设置定时器1重装载值,等于计数初值
    	TR1 = 1;          //打开定时器
    	TI = 1;           //设置串口中断请求标志
    }
    /*********************************************************************************************************
    串口中断服务函数
    /*********************************************************************************************************/
    void UART_handler() interrupt 4//实现命令写入,初始化命令设置参数
    {
    	 
    	if(RI == 1)
    	{
    		arrBUFF[s_num] = SBUF;
    	  
    		RI = 0;
    		set = 1;
    		
    		if(arrBUFF[s_num]>='0' && arrBUFF[s_num]<='z')
    		{
    			s_num ++;
    		}
    	}
    }
    

    2.9、流水灯实现函数头文件"Lightflash.h"及"Lightflash.c"

    /***************************Lightflash.h*************************/
    #ifndef __LIGHTFLASH_H_
    #define __LIGHTFLASH_H_
    
    void Lightflash();//流水灯实现函数
    
    #endif 
    
    /**************************************Lightflash.c***************************************************/
    #include <reg52.h>
    #include "Lightflash.h"
    #include "globals.h"
    
    /*********************************************************************************************************
    内部函数声明
    /*********************************************************************************************************/
    void Lightflash();//流水灯实现函数
    
    /*********************************************************************************************************
    Lightflash流水灯实现函数
    /*********************************************************************************************************/
    void Lightflash()
    {
    	if(led < 4)//实现流水灯样式
    		{
    			P2 = P2 << 1;
    		}
    	else
    		{
    			P2 = 0xEF;
    			led = 0;
    						
    		}
    }
    

    三、致谢

    本学期的课程任务和竞赛比较多,51单片机课程大作业汇报延迟了好久才得以汇报,在此感谢致行班创新实验室的所有老师们指导和辛勤付出,还有思民同学的帮助。

    作者:突破ljj

    物联沃分享整理
    物联沃-IOTWORD物联网 » 基于 51 单片机串口通信的电子钟设计与实现

    发表回复