單片機學(xué)習(xí)記錄——可調(diào)節(jié)時鐘

2021-3-25

第一次在簡書上記錄學(xué)習(xí),請多關(guān)照?。?!
我學(xué)習(xí)的教程是:(https://www.bilibili.com/video/BV1Mb411e7re?p=1
老師的講課方法非常適合我這樣的小白:手把手教如何打代碼。可我就算是跟著老師敲程序,還是做了將近兩天時間。(修BUG是個痛苦的過程)
話不多說,上程序:

頭文件

#ifndef _DELAY_H__
#define _DELAY_H__

void Delay(unsigned int xms);

#endif
#ifndef __DS1302_H__
#define __DS1302_H__

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
#ifndef _KEY_H__
#define _KEY_H__

unsigned char Key();

#endif
#ifndef __LCD1602_H__
#define __LCD1602_H__

//用戶調(diào)用函數(shù):
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

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init(void);

#endif

源文件

#include <REGX52.H>

void Delay(unsigned int xms)        //@12.000MHz
{
    unsigned char i, j;
    while(xms--)
    {
        i = 12;
        j = 169;
        do
        {
            while (--j);
        } while (--i);
    }
}
#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_DAY    0x8A
#define DS1302_YEAR   0x8C
#define DS1302_WP     0x8E

char DS1302_Time[]={21,3,24,12,3,0,1};

void DS1302_Init(void)//初始化
{
    DS1302_SCLK=0;
    DS1302_CE=0;
}

void DS1302_WriteByte(unsigned char Command,Data)
{
    unsigned char i;
    DS1302_CE=1;//高電平
    for(i=0;i<8;i++)
    {
        DS1302_IO=Command&(0x01<<i);//取Command最后一位
        DS1302_SCLK=1;//上升脈沖
        //必要時延時
        DS1302_SCLK=0;//下降脈沖
    }
        for(i=0;i<8;i++)
    {
        DS1302_IO=Data&(0x01<<i);//取Command最后一位
        DS1302_SCLK=1;//上升脈沖
        //必要時延時
        DS1302_SCLK=0;//下降脈沖
    }
    DS1302_CE=0;//停止
}
unsigned char DS1302_ReadByte(unsigned char Command)
{
    unsigned char i,Data=0x00;
    Command|=0x01;//將末尾置1
    DS1302_CE=1;

    for(i=0;i<8;i++)
    {
        DS1302_IO=Command&(0x01<<i);//取Command最后一位
        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;
    
}

void DS1302_SetTime(void)
{
        DS1302_WriteByte(DS1302_WP,0x00);//解除保護
        //十進制轉(zhuǎn)BCD
        DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//年
        DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);//月
        DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);//日
        DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);//時
        DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);//分
        DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);//秒
        DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);//星期
        DS1302_WriteByte(DS1302_WP,0x80);//開啟保護
}

void DS1302_ReadTime(void)
{
        unsigned char Temp;
    
        //BCD轉(zhuǎn)十進制
      Temp=DS1302_ReadByte(DS1302_YEAR);
        DS1302_Time[0]=Temp/16*10+Temp%16;
    
        Temp=DS1302_ReadByte(DS1302_MONTH);
        DS1302_Time[1]=Temp/16*10+Temp%16;
    
        Temp=DS1302_ReadByte(DS1302_DATE);
        DS1302_Time[2]=Temp/16*10+Temp%16;
    
        Temp=DS1302_ReadByte(DS1302_HOUR);
        DS1302_Time[3]=Temp/16*10+Temp%16;
    
        Temp=DS1302_ReadByte(DS1302_MINUTE);
        DS1302_Time[4]=Temp/16*10+Temp%16;
    
        Temp=DS1302_ReadByte(DS1302_SECOND);
        DS1302_Time[5]=Temp/16*10+Temp%16;
    
        Temp=DS1302_ReadByte(DS1302_DAY);
        DS1302_Time[6]=Temp/16*10+Temp%16;

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

unsigned char Key()
{
    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;   
}
#include <REGX52.H>

//引腳配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函數(shù)定義:
/**
  * @brief  LCD1602延時函數(shù),12MHz調(diào)用可延時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寫數(shù)據(jù)
  * @param  Data 要寫入的數(shù)據(jù)
  * @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設(shè)置光標(biāo)位置
  * @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初始化函數(shù)
  * @param  無
  * @retval 無
  */
void LCD_Init()
{
    LCD_WriteCommand(0x38);//八位數(shù)據(jù)接口,兩行顯示,5*7點陣
    LCD_WriteCommand(0x0c);//顯示開,光標(biāo)關(guān),閃爍關(guān)
    LCD_WriteCommand(0x06);//數(shù)據(jù)讀寫操作后,光標(biāo)自動加一,畫面不動
    LCD_WriteCommand(0x01);//光標(biāo)復(fù)位,清屏
}

/**
  * @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指定位置開始顯示所給數(shù)字
  * @param  Line 起始行位置,范圍:1~2
  * @param  Column 起始列位置,范圍:1~16
  * @param  Number 要顯示的數(shù)字,范圍:0~65535
  * @param  Length 要顯示數(shù)字的長度,范圍: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指定位置開始以有符號十進制顯示所給數(shù)字
  * @param  Line 起始行位置,范圍:1~2
  * @param  Column 起始列位置,范圍:1~16
  * @param  Number 要顯示的數(shù)字,范圍:-32768~32767
  * @param  Length 要顯示數(shù)字的長度,范圍: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指定位置開始以十六進制顯示所給數(shù)字
  * @param  Line 起始行位置,范圍:1~2
  * @param  Column 起始列位置,范圍:1~16
  * @param  Number 要顯示的數(shù)字,范圍:0~0xFFFF
  * @param  Length 要顯示數(shù)字的長度,范圍: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指定位置開始以二進制顯示所給數(shù)字
  * @param  Line 起始行位置,范圍:1~2
  * @param  Column 起始列位置,范圍:1~16
  * @param  Number 要顯示的數(shù)字,范圍:0~1111 1111 1111 1111
  * @param  Length 要顯示數(shù)字的長度,范圍: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');
    }
}

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Delay.h"
#include "Timer0.h"
#include "key.h"

unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;

void TimeShow(void)
{
        DS1302_ReadTime();//讀取時間
        LCD_ShowNum(1,1,DS1302_Time[0],2);
        LCD_ShowNum(1,4,DS1302_Time[1],2);
        LCD_ShowNum(1,7,DS1302_Time[2],2);
        LCD_ShowNum(2,1,DS1302_Time[3],2);
        LCD_ShowNum(2,4,DS1302_Time[4],2);
        LCD_ShowNum(2,7,DS1302_Time[5],2);
}

void TimeSet(void)
{
            if(KeyNum==2)
            {
                TimeSetSelect++;//設(shè)置界面提示 年、月、日、時、分、秒,舍棄星期
                TimeSetSelect%=6;//越界清零
            }
            if(KeyNum==3)
            {
                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) 
                {   
                        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) 
                {   
                        if(DS1302_Time[2]<1){DS1302_Time[2]=31;}
                } 
                
                //小月越界判斷
                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;}
                }
                
                //特殊月份二月日期判斷
                else if(DS1302_Time[1]==2)
                {
                        if(DS1302_Time[0]%4==0)
                        {
                                if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
                        }//閏年
                        
                      else 
                        {  
                                if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
                        }//平年
                }
                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;}//秒越界判斷
                
            }
            if(TimeSetSelect==0&&TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}
            else{LCD_ShowNum(1,1,DS1302_Time[0],2);}
            
            if(TimeSetSelect==1&&TimeSetFlashFlag==1){LCD_ShowString(1,4,"  ");}
            else{LCD_ShowNum(1,4,DS1302_Time[1],2);}
                        
            if(TimeSetSelect==2&&TimeSetFlashFlag==1){LCD_ShowString(1,7,"  ");}
            else{LCD_ShowNum(1,7,DS1302_Time[2],2);}
            
            if(TimeSetSelect==3&&TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}
            else{LCD_ShowNum(2,1,DS1302_Time[3],2);}
            
            if(TimeSetSelect==4&&TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}
            else{LCD_ShowNum(2,4,DS1302_Time[4],2);}
            
            if(TimeSetSelect==5&&TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}
            else{LCD_ShowNum(2,7,DS1302_Time[5],2);}
}       

void main()
{
    LCD_Init();
    DS1302_Init();
    Timer0Init();
    LCD_ShowString(1,1,"  -  -  ");
    LCD_ShowString(2,1,"  :  :  ");
    
    DS1302_SetTime();//設(shè)置時間!??!

    
    while(1)
    {
        KeyNum=Key();//讀取按鍵
        if(KeyNum==1)//按下按鍵1切換設(shè)置狀態(tài)
        {
            if(MODE==0){MODE=1;}
            else if(MODE==1){MODE=0;DS1302_SetTime();}
        }
        switch(MODE)
        {
            case 0:TimeShow();break;
            case 1:TimeSet();break;         
        }
    }
}


void Timer0_Routine() interrupt 1
{
    static unsigned int T0Count;
    TL0 = 0x18;     //設(shè)置定時初值
    TH0 = 0xFC;     //設(shè)置定時初值
    T0Count++;
    if(T0Count>=1000)//1000ms為周期
    {
        T0Count=0;
        TimeSetFlashFlag=!TimeSetFlashFlag;//!邏輯取反;~按位取反
    }
}
#include <REGX52.H>

Timer0Init(void)
{
    TMOD &= 0xF0;       //設(shè)置定時器模式
    TMOD |= 0x01;       //設(shè)置定時器模式
    TL0 = 0x18;     //設(shè)置定時初值
    TH0 = 0xFC;     //設(shè)置定時初值
    TF0 = 0;        //清除TF0標(biāo)志
    TR0 = 1;        //定時器0開始計時
    ET0=1;
    EA=1;
    PT0=0;
}
可調(diào)節(jié)時鐘.jpg

總結(jié)

程序分六大模塊:延時、中斷、按鍵讀取、LCD1602、DS1302、主函數(shù)
學(xué)習(xí)內(nèi)容/難點:根據(jù)時序定義配置DS1302時鐘芯片
不足之處:延遲模塊有死循環(huán)等待
改進之處:原程序使用unsigned char定義DS1302_Time,沒有考慮到調(diào)節(jié)時間減小超出范圍的情況。改為char類型后即可解決。(存在未知BUG)

2021-3-27更新

還未解決進入調(diào)節(jié)模式后顯示錯誤BUG

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容