DS1302可调时钟+单片机+普中+江科大自化协

1 功能:

2 参考程序

2.1 主程序

#include <REGX52.H>
#include "LCD1602.H"
#include "DS1302.H"
#include "Key.H"
#include "Timer0.H"

unsigned char     KeyNum;            //记录按键值
unsigned char     MODE;            //功能模式选择,0为时间显示模式,1为时间调整模式
unsigned char     TimeSetSelect;    //时间设置选择位,0-5,年、月、日、时、分、秒
unsigned char     TimeSetFlashFlag;//闪烁标志位,0为熄灭,1为显示
    
void TimeShow(void)        //显示时间
{
    DS1302_ReadTime( );                //读取时间
    LCD1602_ShowNum(1,1,DS1302_Time[0],2);    //显示年
    LCD1602_ShowNum(1,4,DS1302_Time[1],2);    //显示月
    LCD1602_ShowNum(1,7,DS1302_Time[2],2);    //显示日
    LCD1602_ShowNum(2,1,DS1302_Time[3],2);    //显示时
    LCD1602_ShowNum(2,4,DS1302_Time[4],2);    //显示分
    LCD1602_ShowNum(2,7,DS1302_Time[5],2);    //显示秒
}

void TimeSet(void)        //调整时间
{    
    if(KeyNum == 2)        //按键2按下,调整时间设置选择位
    {
        TimeSetSelect++;    //时间设置选择位加1
        TimeSetSelect%=6;    //0-6循环,越界清零
    }
        
    if(KeyNum == 3)        //调整数值,加1操作
    {
        DS1302_Time[TimeSetSelect]++;
        if(DS1302_Time[0]>99)    {DS1302_Time[0] = 0;}    //年越界判断
        if(DS1302_Time[1]>12)    {DS1302_Time[1] = 1;}    //月越界判断
        
        //日越界判断
        if((DS1302_Time[1]==1)||(DS1302_Time[1]==3)||(DS1302_Time[1]==5)||(DS1302_Time[1]==7)||
            (DS1302_Time[1]==8)||(DS1302_Time[1]==10)||(DS1302_Time[1]==12))    //大月31天
            {
                if(DS1302_Time[2]>31)    {DS1302_Time[2] = 1;}    
            }
        else if((DS1302_Time[1]==4)||(DS1302_Time[1]==6)||(DS1302_Time[1]==9)||(DS1302_Time[1]==11))
        {
            if(DS1302_Time[2]>30)    {DS1302_Time[2] = 1;}    
        }
        else if(DS1302_Time[1]==2)
        {
            if(DS1302_Time[0]%4==0)
            {
                if(DS1302_Time[2]>29)    {DS1302_Time[2] = 1;}
            }
            else
            {
                if(DS1302_Time[2]>28)    {DS1302_Time[2] = 1;}
            }
        }
        if(DS1302_Time[3]>23)    {DS1302_Time[3] = 0;}    //时越界判断
        if(DS1302_Time[4]>59)    {DS1302_Time[4] = 0;}    //分越界判断
        if(DS1302_Time[5]>59)    {DS1302_Time[5] = 0;}    //秒越界判断
    }
    if(KeyNum == 4)
    {
        DS1302_Time[TimeSetSelect]--;
        if(DS1302_Time[0]<0)    {DS1302_Time[0] = 99;}    //年越界判断
        if(DS1302_Time[1]<1)    {DS1302_Time[1] = 12;}    //月越界判断
        
        //日越界判断
        if((DS1302_Time[1]==1)||(DS1302_Time[1]==3)||(DS1302_Time[1]==5)||(DS1302_Time[1]==7)||
            (DS1302_Time[1]==8)||(DS1302_Time[1]==10)||(DS1302_Time[1]==12))    //大月31天
            {
                if(DS1302_Time[2]<1)    {DS1302_Time[2] = 31;}
                if(DS1302_Time[2]>31)    {DS1302_Time[2] = 1;}    //在减的过程中,出现越界现象,即1131现象
            }
        else if((DS1302_Time[1]==4)||(DS1302_Time[1]==6)||(DS1302_Time[1]==9)||(DS1302_Time[1]==11))
        {
            if(DS1302_Time[2]<1)    {DS1302_Time[2] = 30;}
            if(DS1302_Time[2]>30)    {DS1302_Time[2] = 1;}            
        }
        else if(DS1302_Time[1]==2)
        {
            if(DS1302_Time[0]%4==0)
            {
                if(DS1302_Time[2]<1)    {DS1302_Time[2] = 29;}
                if(DS1302_Time[2]>29)    {DS1302_Time[2] = 1;}
            }
            else
            {
                if(DS1302_Time[2]<1)    {DS1302_Time[2] = 28;}
                if(DS1302_Time[2]>28)    {DS1302_Time[2] = 1;}
            }
        }
        if(DS1302_Time[3]<0)    {DS1302_Time[3] = 23;}    //时越界判断
        if(DS1302_Time[4]<0)    {DS1302_Time[4] = 59;}    //分越界判断
        if(DS1302_Time[5]<0)    {DS1302_Time[5] = 59;}    //秒越界判断
        
    }
    
    //更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
    if((TimeSetSelect==0)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(1,1,"  ");}    //显示年
    else {LCD1602_ShowNum(1,1,DS1302_Time[0],2);}                                
    if((TimeSetSelect==1)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(1,4,"  ");}    //显示月
    else {LCD1602_ShowNum(1,4,DS1302_Time[1],2);}                                    
    if((TimeSetSelect==2)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(1,7,"  ");}    //显示日
    else {LCD1602_ShowNum(1,7,DS1302_Time[2],2);}                                    
    if((TimeSetSelect==3)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(2,1,"  ");}    //显示时
    else {LCD1602_ShowNum(2,1,DS1302_Time[3],2);}                                
    if((TimeSetSelect==4)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(2,4,"  ");}    //显示分
    else {LCD1602_ShowNum(2,4,DS1302_Time[4],2);}                                
    if((TimeSetSelect==5)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(2,7,"  ");}    //显示秒
    else {LCD1602_ShowNum(2,7,DS1302_Time[5],2);}                                
}

void main()
{    
//初始化操作
    LCD1602_Init();
    DS1302_Init();
    Timer0Init();
    
    LCD1602_ShowString(1,1,"  -  -  ");    //静态字符初始化显示
    LCD1602_ShowString(2,1,"  :  :  ");
    
    DS1302_SetTime( );                    //设置初始时间
    
    while(1)
    {
        KeyNum = Key();        //读取按键值        
        
        if(KeyNum == 1)        //按键1按下,模式切换
        {
            if(MODE == 0) {MODE = 1; TimeSetSelect = 0;}
            else if(MODE == 1) {MODE = 0;DS1302_SetTime( );}    //切换回时间显示模式,同时更新时间数据
        }
        switch(MODE)
        {
            case 0: {TimeShow();break;}        //时间显示功能
            case 1: {TimeSet();    break;}        //时间调整功能            
        }
    }
}

//定时器中断,每500ms闪烁标志位TimeSetFlashFlag状态反转一次
void Timer0_Routine() interrupt 1
{
    static unsigned int T0Count;
    TL0 = 0x18;        //设置定时初值
    TH0 = 0xFC;        //设置定时初值
    T0Count++;
    if(T0Count>=500)    //每500ms进入一次
    {
        T0Count=0;
        TimeSetFlashFlag=!TimeSetFlashFlag;        //闪烁标志位取反
    }
}

2.2 LCD1602控制源程序及头文件

#include <REGX52.H>
#include "intrins.h"

//引脚配置,硬件原理图,普中A2
sbit LCD1602_RS = P2^6;
sbit LCD1602_RW = P2^5;
sbit LCD1602_E  = P2^7;
#define LCD1602_DATAPORT P0


/**
  * @brief  LCD1602延时函数
  * @param  xms=1, 延时1ms
  * @retval 无
  */
void LCD1602_Delayms(unsigned char xms)        //@11.0592MHz
{
    unsigned char i, j,k;

    for(k=0;k<xms;k++)
    {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j);
        } while (--i);
    }

}

/**
  * @brief  LCD1602写命令,时序图
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD1602_WriteCommand(unsigned char Command)
{
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DATAPORT  = Command;
    LCD1602_E  = 1;
    LCD1602_Delayms(1);    //延时1ms
    LCD1602_E  = 0;
    LCD1602_Delayms(1);    //延时1ms
}

/**
  * @brief  LCD1602写数据,时序图
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD1602_WriteData(unsigned char Data)
{
    LCD1602_RS = 1;
    LCD1602_RW = 0;
    LCD1602_DATAPORT  = Data;
    LCD1602_E  = 1;
    LCD1602_Delayms(1);    //延时1ms
    LCD1602_E  = 0;
    LCD1602_Delayms(1);    //延时1ms
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD1602_Init(void)
{
    LCD1602_WriteCommand(0x38);    //0011_1000 功能设置,设置16x2显示,5x7点阵,8位数据接口
    LCD1602_WriteCommand(0x0C);    //0000_1100 显示开关控制,显示开,光标关,闪烁关    
    LCD1602_WriteCommand(0x06);    //0000_0110 输入方式设置,数据写后,AC自动加一,画面不动
    LCD1602_WriteCommand(0x01);    //0000_0001 清屏,数据指针清零、所有显示清零
}
/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD1602_SetCursor(unsigned char Line,unsigned char Column)
{
    if(Line == 1)
    {
        LCD1602_WriteCommand(0x80|(Column-1)); //设置要存入数据的DDROM地址,第一行地址
    }
    else
    {
        LCD1602_WriteCommand(0x80|(Column-1)+0x40); //设置要存入数据的DDROM地,第二行地址
    }
}
    

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
{
    LCD1602_SetCursor(Line,Column);
    LCD1602_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置上显示所给字符串
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符串
  * @retval 无
  */
void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String)        //指针
{
    unsigned char i;
    LCD1602_SetCursor(Line,Column);
    for(i=0;String[i]!='\0';i++)    //字符串结束标志'\0'
    {
        LCD1602_WriteData(String[i]);
    }
    
}

/**
  * @brief  返回值=X的Y次方,指数运算
  */
int LCD_Power(int X,int Y)
{
    unsigned char i;
    int result = 1;
    for(i=0;i<Y;i++)
    {
        result = result * X;
    }
    return result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    LCD1602_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        LCD1602_WriteData('0'+Number/LCD_Power(10,i-1)%10); // '0'=0x30,转换为字符显示;
        
        /** 
          * 789, 789/100%10, 取出百位
          * 789, 789/10%10, 取出十位
          * 789, 789/1%10, 取出个位
         */
    }
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column, int Number,unsigned char Length)
{
    unsigned char i;
    unsigned int Number1; 
    LCD1602_SetCursor(Line,Column);
    if(Number >= 0)
    {
        LCD1602_WriteData('+');
        Number1 = Number;
    }
    else
    {
        LCD1602_WriteData('-');
        Number1 = -Number;     //-32768
    }
    for(i=Length;i>0;i--)
    {
        LCD1602_WriteData('0'+Number1/LCD_Power(10,i-1)%10); 
    }
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xffff
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    unsigned char SingleNumber;
    LCD1602_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        SingleNumber = Number/LCD_Power(16,i-1)%16; //从高位到低位,取出每一个十六进制数字
        if(SingleNumber < 10)
            LCD1602_WriteData('0'+SingleNumber); 
        else
            LCD1602_WriteData('A'+SingleNumber-10); //SingleNumber已含有10+x
    }
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111_1111_1111_1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    LCD1602_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        LCD1602_WriteData('0'+Number/LCD_Power(2,i-1)%2); //从高位到低位,取出每一个二进制数字
    }
}
#ifndef __LCD1602_H__
#define __LCD1602_H__
    
    void LCD1602_WriteCommand(unsigned char Command);
    void LCD1602_WriteData(unsigned char Data);

    void LCD1602_Init(void);
    void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char);
    void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String);
    void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    
#endif

2.3 DS1302控制源程序及头文件

#include <REGX52.H>

//引脚定义,与硬件原理图有关
sbit DS1302_SCLK    = P3^6;
sbit DS1302_IO        = P3^4;
sbit DS1302_CE        = P3^5;

#define DS1302_SECOND_ADDR    0x80    //DS1302秒寄存器写地址
#define DS1302_MINUTE_ADDR    0x82    //DS1302分寄存器写地址
#define DS1302_HOUR_ADDR    0x84    //DS1302时寄存器写地址
#define DS1302_DATE_ADDR    0x86    //DS1302日寄存器写地址
#define DS1302_MONTH_ADDR    0x88    //DS1302月寄存器写地址
#define DS1302_DAY_ADDR        0x8A    //DS1302星期寄存器写地址
#define DS1302_YEAR_ADDR    0x8C    //DS1302年寄存器写地址
#define DS1302_WP_ADDR        0x8E    //DS1302控制寄存器写地址

 char DS1302_Time[]={22,8,7,20,8,55,7};
//时间数字,索引0-6分别为年、月、日、时、分、秒、星期

/**
  * @brief  DS1302初始化
  * @param  无
  * @retval 无
  */
void DS1302_Init(void)
{
    DS1302_CE = 0;
    DS1302_SCLK = 0;
}

/**
  * @brief  DS1302写一个字节
  * @param  Command 命令字/地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void DS1302_WriteByte(unsigned char Command,Data)
{
    unsigned char i;
    DS1302_CE = 1;
    
    for(i=0;i<8;i++)        //写地址/命令
    {
        DS1302_IO = Command & (0x01<<i); //从最低位开始,一次取出相应位的数据
        DS1302_SCLK = 1;    //SCLK上升沿,MCU向DS1302写入数据
        DS1302_SCLK = 0;    //一个完整的脉冲
    }
    
    for(i=0;i<8;i++)        //写数据
    {
        DS1302_IO = Data & (0x01<<i); //从最低位开始,一次取出相应位的数据
        DS1302_SCLK = 1;    //SCLK上升沿,MCU向DS1302写入数据
        DS1302_SCLK = 0;    //一个完整的脉冲
    }
    
    DS1302_CE = 0;
}

/**
  * @brief  DS1302读一个字节
  * @param  Command 命令字/地址
  * @retval 读出的数据
  */
unsigned char DS1302_ReadByte(unsigned char Command)
{
    unsigned char i;
    unsigned char Data = 0x00;
    
    Command = Command | 0x01;    //将读地址指令转换为相应的读地址指令
    
    DS1302_CE = 1;
    
    for(i=0;i<8;i++)        //写地址/命令
    {
        DS1302_IO = Command & (0x01<<i); //从最低位开始,一次取出相应位的数据
        DS1302_SCLK = 0;    //一个完整的脉冲,与写地址写数据相反
        DS1302_SCLK = 1;    //SCLK上升沿,MCU向DS1302写入数据        
    }
    
    for(i=0;i<8;i++)
    {
        DS1302_SCLK = 1;    //写地址读数据
        DS1302_SCLK = 0;    //SCLK下降沿,MUC读取DS1302的数据
        if(DS1302_IO) {Data = Data | (0x01<<i);}
    }
    
    DS1302_CE = 0;
    DS1302_IO = 0;            //读取后将IO设置为0,否则读出的数据会出错
    return Data;
}

/**
  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
  * @param  无
  * @retval 无
  */
void DS1302_SetTime(void)
{
    DS1302_WriteByte(DS1302_WP_ADDR,0x00);    //解除芯片写保护
    DS1302_WriteByte(DS1302_YEAR_ADDR,        DS1302_Time[0]/10*16+DS1302_Time[0]%10);    //写入年数据,十进制转为十六进制
    DS1302_WriteByte(DS1302_MONTH_ADDR,        DS1302_Time[1]/10*16+DS1302_Time[1]%10);    //写入月数据
    DS1302_WriteByte(DS1302_DATE_ADDR,        DS1302_Time[2]/10*16+DS1302_Time[2]%10);    //写入日数据
    DS1302_WriteByte(DS1302_HOUR_ADDR,        DS1302_Time[3]/10*16+DS1302_Time[3]%10);    //写入小时数据
    DS1302_WriteByte(DS1302_MINUTE_ADDR,    DS1302_Time[4]/10*16+DS1302_Time[4]%10);    //写入分钟数据
    DS1302_WriteByte(DS1302_SECOND_ADDR,    DS1302_Time[5]/10*16+DS1302_Time[5]%10);    //写入秒钟数据
    DS1302_WriteByte(DS1302_DAY_ADDR,        DS1302_Time[6]/10*16+DS1302_Time[6]%10);    //写入星期数据
    DS1302_WriteByte(DS1302_WP_ADDR,0x80);    //开启芯片写保护
}

/**
  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  * @param  无
  * @retval 无
  */
void DS1302_ReadTime(void)
{
    unsigned char Temp;
    Temp = DS1302_ReadByte(DS1302_YEAR_ADDR);     DS1302_Time[0]=Temp/16*10+Temp%16;
    Temp = DS1302_ReadByte(DS1302_MONTH_ADDR);     DS1302_Time[1]=Temp/16*10+Temp%16;
    Temp = DS1302_ReadByte(DS1302_DATE_ADDR);     DS1302_Time[2]=Temp/16*10+Temp%16;
    Temp = DS1302_ReadByte(DS1302_HOUR_ADDR);     DS1302_Time[3]=Temp/16*10+Temp%16;
    Temp = DS1302_ReadByte(DS1302_MINUTE_ADDR); DS1302_Time[4]=Temp/16*10+Temp%16;
    Temp = DS1302_ReadByte(DS1302_SECOND_ADDR);    DS1302_Time[5]=Temp/16*10+Temp%16;
    Temp = DS1302_ReadByte(DS1302_DAY_ADDR);     DS1302_Time[6]=Temp/16*10+Temp%16;
}
#ifndef __DS1302_H__
#define __DS1302_H__

//外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期    
    extern char DS1302_Time[];
    
    
    void DS1302_Init(void);
    void DS1302_WriteByte(unsigned char Command,Data);
    unsigned char DS1302_ReadByte(unsigned char Command);
    void DS1302_SetTime(void);
    void DS1302_ReadTime(void);
    
#endif

2.4 独立按键扫描函数及头文件

#include <REGX52.H>
#include "Delay.h"

/**
  * @brief  获取独立按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  */
unsigned char Key()
{
    unsigned char KeyNumber=0;
    
    if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
    if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
    if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
    if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
    
    return KeyNumber;
}
#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key();

#endif

2.5 定时器中断函数及头文件

#include <REGX52.H>

/**
  * @brief  定时器0初始化,1毫秒@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer0Init(void)
{
    TMOD &= 0xF0;        //设置定时器模式
    TMOD |= 0x01;        //设置定时器模式
    TL0 = 0x18;        //设置定时初值
    TH0 = 0xFC;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0=1;
    EA=1;
    PT0=0;
}

/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
    static unsigned int T0Count;
    TL0 = 0x18;        //设置定时初值
    TH0 = 0xFC;        //设置定时初值
    T0Count++;
    if(T0Count>=1000)
    {
        T0Count=0;
        
    }
}
*/
#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init(void);

#endif

2.6 延时函数及头文件

void Delay(unsigned int xms)
{
    unsigned char i, j;
    while(xms--)
    {
        i = 2;
        j = 239;
        do
        {
            while (--j);
        } while (--i);
    }
}
#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

 

posted @ 2022-08-08 16:14  豌豆茶  阅读(349)  评论(0编辑  收藏  举报