10. 實時鐘系統設計

_laiwenjie發表於2019-02-28

10. 實時鐘系統設計

  10.1 系統結構

  10.2 程式設計

    10.2.1 實時鐘程式設計

    10.2.2 按鍵和顯示程式設計

    10.2.3 系統程式設計

  10.3 程式實現

 

  10.1 系統結構

  • 實時鐘系統包括STM32MCU、實時鐘電路DS1302、2個按鍵、4個LED、LED顯示器和UART-USB轉換器CP2102

  • 實時鐘電路提供實時鐘計時,包括年、月、日、星期、時、分和秒等
  • 2個按鍵用於切換LCD顯示和設定實時鐘
  • 4個LED指示LCD顯示的內容
  • LCD分別顯示年、月、星期、日、時、分和秒等
  • UART-USB轉換器CP2102通過USB實現與計算機的序列通訊
  • DS1302是美國DALLAS公司推出的一種高效能、低功耗、帶RAM的實時時鐘電路,可以對年、月、星期、日、時、分和秒進行計時,具有閏年補償功能
  • DS1302採用三線介面與CPU進行同步通訊,可採用突發方式一次傳送多個位元組的時鐘或RAM資料
  • DS1302的工作電壓為2.5-5.5V,8引腳封裝

  • DS1302控制位元組

  • DS1302讀寫時序

  • DS1302時鐘暫存器

 

  10.2 程式設計

    10.2.1 實時鐘程式設計

  • 實時鐘程式設計根據DS1302讀寫時序進行編寫。DS1302讀寫時序包括2個基本操作:寫時序包括2個寫操作(寫地址和寫資料),讀時序包括1個寫操作(寫地址)和1個讀操作(讀資料)因此,首先編寫2個基本操作的子程式
  • DS1302寫子程式
//DS1302寫子程式
//入口引數:data-寫資料

void Ds1302_Write(char data)
{
    char m;

    for(m=0; m<8; m++)
    {
        if(data & 1<<m)
            GPIOB->BSRR = 1<<7;     //PB.07(I/O)=1
        else
            GPIOB->BRR = 1<<7;      //PB.07(I/O)=0

        GPIOB->BSRR = 1<<6;         //PB.06(SCLK)=1
        GPIOB->BRR = 1<<6;          //PB.06(SCLK)=0
    }
}
  • DS1302讀子程式
//DS1302讀子程式
//出口引數:讀資料

char DS1302_Read(void)
{
    char m,data=0;

    for(m=0;m<8;m++)
    {
        data |= (GPIOB->IDR>>7&1)<<m; //PB.07(I/O)

        GPIOB->BSRR = 1<<6;           //PB.06(SCLK)=1

        GPIOB->BRR = 1<<6;            //PB.06(SCLK)=0
    }

    return data;
}
  • DS1302寫資料子程式
//DS1302寫資料子程式
//入口引數:addr-地址,data-資料

void Ds1302_Write_Data(char addr,char data)
{

    RCC->APB2ENR |= 1<<3;             //開啟GPIOB時鐘

    GPIOB->CRL &= 0x000fffff;

    GPIOB->CRL |= 0x33300000;         //PB.07-PB.05通用推輓輸出

    GPIOB->BSRR = 1<<5;               //PB.05(RST)=1

    addr = 0x80+(addr<<1);            //寫資料

    Ds1302_Write(addr);

    Ds1302_Write(data);

    GPIOB->BRR = 1<<5;                //PB.05(RST)=0
}
  • DS1302讀資料子程式
//DS1302讀資料子程式
//入口引數:addr-地址
//出口引數:資料

char Ds1302_Read_data(char addr)
{
    char data=0;

    RCC->APB2ENR |= 1<<3;             //開啟GPIOB時鐘

    GPIOB->CRL &= 0x000fffff;

    GPIOB->CRL |= 0x33300000;         //PB.07-PB.05通用推輓輸出

    GPIOB->BSRR = 1<<5;               //PB.05(RST)=1

    addr = 0x80+(addr<<1)+1;          //讀資料

    Ds1302_Write(addr);

    GPIOB->CRL &= 0x0fffffff;

    GPIOB->CRL |= 0x40000000;         //PB.07(I/O)浮空輸入

    data = Ds1302_Read();

    GPIOB->BRR 1<<5;                  //PB.05(RST)=0

    return data;
}

    10.2.2 按鍵和顯示程式設計

  • 按鍵操作和LCD顯示狀態圖

  • 系統共有8個狀態:4個顯示狀態(分別顯示年、月日、時分和分秒)和4個設定狀態(分別設定月、日、時和分)
  • 狀態間用切換鍵(KEY1)和設定鍵(KEY2)轉換:切換鍵用於切換顯示和設定狀態以及退出設定狀態,設定鍵用於進入設定狀態和設定月、日、時和分等
  • 4個顯示狀態和4個設定狀態同時分別用4個LED進行指示

    1、按鍵程式設計(按鍵採用中斷方式工作)

  • 按鍵介面初始化子程式
//按鍵介面初始化子程式

void Key_Init(void)
{
    RCC->APB2ENR |= 1<<3;             //開啟按鍵介面(GPIOB)時鐘

    RCC->APB2ENR |= 1;                //開啟AFIO時鐘

    AFIO->EXTICR[2] |= 0x0011;        //EXTI9=PB.9,EXTI8=PB.8

    EXTI->FTSR |= 0x0300;             //EXTI9和EXTI8下降沿觸發

    EXTI->IMR |= 0x0300;              //允許EXTI9和EXTI8中斷

    NVIC->IPR[5] |= 0x80000000;       //EXTI9-8中斷搶佔優先順序1

    NVIC->ISER[0] |= 1<<23;           //允許EXTI9-8中斷
}
  • 按鍵介面處理子程式
//按鍵中斷處理子程式
void EXTI9_5_IRQHandler(void)
{
    Delay(10);                             //延時10ms消抖動

    if((EXTI->PR&1<<8)&&(~GPIOB->IDR&1<<8))//KEY1按下(PR.8=1,IDR.8=0)
    {
        if(flag<4)                         //顯示狀態
        {
            if(flag<4)                     //顯示狀態切換
                flag=0;
        }
        else //設定狀態
        {
            if(++flag == 8)                //設定狀態切換
            {
                flag = 2;                  //退出設定狀態

                GPIOB->BRR = 0xf000;       //亮所有LED

                Delay(200);                //延時200ms

                Ds1302_Write_Data(4,month);//寫資料到DS1302

                Ds1302_Write_Data(3,date);

                Ds1302_Write_Data(2,hour);

                Ds1302_Write_Data(1,min);

                Ds1302_Write_Data(0,0);
            }
        }
    }

    if((EXTI->PR & 1<<9) && (~GPIOB->IDR & 1<<9)) //KEY2按下(PR.9=1,IDR.9=0)
    {
        if(flag<4)

        flag = 4;                         //進入設定狀態

        if(flag == 4)                     //設定月
        {
            month = Bin_Bcd(++month);     //2-10進位制調整

            if(month > 0x12)
                month = 1;
        }

        if(flag == 5)                     //設定日
        {
            date = Bin_Bcd(++date);       //2-10進位制調整

            if(date>0x31)
                date = 1;
        }

        if(flag == 6)                     //設定時
        {
            hour = Bin_Bcd(++hour);       //2-10 進位制調整

            if(hour>0x24)
                hour = 0;
        }

        if(flag == 7)                     //設定分
        {
            min = Bin_Bcd(++min);         //2-10進位制調整

            if(min>0x60)
                min = 0;
        }
    }
    EXTI->PR |= 0x0300;                   //清除中斷觸發請求
}
  • 延時子程式Delay()和2-10進位制調整子程式Bin_Bcd()
//延時子程式
void Delay(char ms)
{
    msec = 0;

    while(msec < ms);
}

//2-10進位制調整子程式
char Bin_Bcd(char data)
{
    if((data & 0xf) >9)
        data += 6;

    return data;
}
  • 延時子程式中的msec計時用SysTick中斷實現,相應的SysTick初始化子程式和SysTick中斷處理子程式
//SysTick初始化子程式
void SysTick_Init(void)
{
    SysTick->LOAD = 1E3;     //設定1ms重灌值(時脈頻率為8MHz/8)

    SysTick->CTRL = 2;       //計數到0時中斷

    SysTick->CTRL = 1;       //啟動定時器
}

//SysTick中斷處理子程式
void SysTick_Handler(void)
{
    msec += 1;               //毫秒計時
}
  • 由於SysTick中斷巢狀在按鍵中斷中,而STM32的中斷巢狀是通過搶佔優先順序實現。因此,為了保證程式正常工作,必須用下列語句設定搶佔優先順序
SCB->AIRCR = 0x05FA0600;         //1位搶佔優先順序,3位響應優先順序

NVIC->IPR[5] |= 0x80000000;      //EXTI9-8中斷搶佔優先順序1
  • SysTick的搶佔優先順序預設為0,比EXTI9-8搶佔優先順序高可以在按鍵中斷中巢狀SysTick中斷,實現延時功能

    2、顯示程式設計(包括LED狀態指示和LCD資料顯示)

  • 顯示處理子程式
//顯示處理子程式
void Disp_Proc(void)
{
    switch(flag)
    {
        case 0:                         //顯示年
        {
            GPIOB->BSRR = 0xf000;       //滅所有LED

            GPIOB->BRR = 0x8000;        //亮LED4

            Lcd_Proc(0x20,year,0);      //顯示年

            break;
        }

        case 1:                         //顯示月日
        {
            GPIOB->BSRR = 0xf000;       //滅所有LED

            GPIOB->BRR = 0x4000;        //亮LED3

            Lcd_Proc(month,date,2);     //顯示月,日

            break;
        }

        case 2:                         //顯示時分
        {
            GPIOB->BSRR = 0xf000;       //滅所有LED

            GPIOB->BRR = 0x2000;        //亮LED2

            Lcd_Proc(hour,min,8);       //顯示時:分

            Delay(200);

            Lcd_Proc(hour,min,0);       //冒號(:)閃爍

            break;
        }

        case 3:                         //顯示分秒
        {
            GPIOB->BSRR = 0xf000;       //滅所有LED

            GPIOB->BRR = 0x1000;        //亮LED1

            Lcd_Proc(min,sec,0);        //顯示分秒

            break;
        }

        case 4:                         //設定月
        {
            GPIOB->BSRR = 0xf000;       //滅所有LED

            GPIOB->BRR = 0x8000;        //亮LED4

            Lcd_Proc(0xff,date,2);      //月閃爍

            Delay(200);

            Lcd_Proc(month,date,2);

            break;
        }

        case 5:                         //設定日
        {
            GPIOB->BSRR = 0xf000;       //滅所有LED

            GPIOB->BRR = 0x4000;        //亮LED3

            Lcd_Proc(month,0xff,2);     //日閃爍

            Delay(200);

            Lcd_Proc(month,date,2);

            break;
        }

        case 6:                         //設定時
        {
            GPIOB->BSRR = 0xf000;       //滅所有LED

            GPIOB->BRR = 0x2000;        //亮LED2

            Lcd_Proc(hour,min,8);       //時閃爍

            Delay(200);

            Lcd_Proc(hour,min,8);

            break;

        }

        case 7:                         //設定分
        {
            GPIOB->BSRR = 0xf000;       //滅所有LED

            GPIOB->BRR = 0x1000;        //亮LED1

            Lcd_Proc(hour,0xff,0);      //分閃爍

            Delay(200);

            Lcd_Proc(hour,min,8);

            break;

        }
    }
}

  • 為了使設定的資料閃爍,需要對Lcd_Proc()中的lcd_code[]資料定義做如下修改
char lcd_code[16]={0xeb,0x60,0xad,0xe5,0x66,0xc70xcf,0x61,

                    0xef,0xe7,0x6f,0xce,0x8b,0xec,0x8f,0x00};

 

  10.2.3 系統程式設計

  • 實時鐘系統主程式和部分初始化子程式流程圖

  • 主程式首先對系統進行初始化,包括SCB(系統控制模組)初始化、SysTick初始化、按鍵介面初始化、LED介面初始化、LCD介面初始化和USART1介面初始化等
  • 其中SCB初始化設定中斷優先順序組,SysTick初始化允許計數到0時中斷,按鍵介面初始化允許按鍵介面中斷,並設定按鍵中斷的搶佔優先順序低於SysTick中斷的搶佔優先順序,以允許在按鍵中斷巢狀SysTick中斷,實現延時功能
  • 系統初始化後寫實時鐘資料到DS1302作為實時鐘的初始資料,然後進入主迴圈
  • 主迴圈的主要操作(包括顯示處理和串列埠傳送資料)每1s執行1次。顯示狀態下從DS1302讀取實時鐘資料顯示並通過串列埠進行傳送,設定狀態下不從DS1302讀取實時鐘資料,只顯示當前資料並等待通過按鍵進行設定
  • 按鍵程式和延時程式通過中斷機制進行呼叫,可以實時響應按鍵和延時操作,顯示程式和串列埠傳送程式則通過主程式進行呼叫

 

 

相關文章