close

第4章

時鐘的驅動-RTC

4.1 RTC簡介

RTC是一種提供日期/時間的周邊硬體元件,常用於嵌入式系統中顯示時間資訊和紀錄事件發生的時間,RTC擁有一組連續計數的計數器,在相應軟體配置下,可以修改計數器的值來重新配置當前的時間和日期。

 RTC模塊和時鐘的配置系統(RCC_BDCR暫存器)位於後備區(BKP),一旦系統主電源斷電,RTC仍可藉由輔助電力(例如鈕扣電池)維持運作,因而可實現不中斷的即時時鐘功能。但是在系統使用輔助電力時,會禁止訪問後備區(BKP)的暫存器和RTC,以防止對後備區(BKP)的意外寫入操作,所以在設置時間之前,先要取消後備區(BKP)寫入保護,可用以下方式:

l   藉由設置RCC_APB1ENR暫存器中的PWREN與 BKPEN 位,將電源與後備區的時鐘起始。

l    設置Power Control Register (PWR_CR)的DBP位,將後備區暫存器與RTC的存取機制啟動。

4.2 功能概述

RTC 主要分成兩個部分(如下圖),第一個部分(APB1介面)是用來和APB1的匯流排連接,此單元包含一個16位的暫存器,可以透過APB1匯流排對此暫存器進行讀寫的操作,APB1介面由APB1匯流排的時鐘所驅動,藉此與APB1匯流排連接。

另一部分(RTC核心)由一組可編程計數器組成,分成兩個主要模塊。第一塊模塊是RTC的除頻器模塊,可編程產生最長為1秒的TR_CLK作為RTC基準時間。 RTC的除頻器模塊包含了一個20位的可編程除頻器(RTC除頻器)。如果在RTC_CR暫存器起始相應的Enable bit,則在每個TR_CLK週期中RTC產生一個中斷(秒中斷)。第二個模塊是一個32位的可編程計數器,可被初始化為當前的系統時間,一個32位的時鐘計數器,按秒鐘計算,可以記錄4294967296秒,約為136年左右,作為一般應用這已經非常足夠了,此外,除頻的時間計算為 RTCCLK/RTC_PR = (32.768 KHz)/(32767+1)。
RTC還有一個鬧鐘暫存器RTC_ALR,用於產生鬧鐘,比較系統時間TR_CLK與存儲在RTC_ALR暫存器中的可編程時間,比較匹配時將產生一個鬧鐘中斷(如果RTC_CR控制暫存器中設置了相應Enable bit)。

image001  

4.3 RTC 暫存器描述

       以下介紹RTC中所使用的暫存器。

4.3.1 RTC Control register high (RTC_CRH)

image002  

 

這些位用來屏蔽中斷請求。注意:系統重新設置後所有的中斷都會取消,因此可通過寫RTC暫存器來確保在初始化後沒有掛起中斷的請求。當周邊正在完成前一次寫入操作時(標誌位RTOFF=0),不能對RTC_CRH暫存器進行寫操作。
RTC功能由這個控制暫存器控制。一些位的寫操作必須經過一個特殊的配置過程來完成。

 

4.3.2 RTC Control register low (RTC_CRL)

image003  

image004  

 

RTC的功能由這個控制暫存器控制,當前一個寫操作還沒完成時(RTOFF=0),不能寫入RTC_CR暫存器。

 

4.3.3 RTC prescaler load register(RTC_PRLH / RTC_PRLL)

 

除頻器暫存器用來保存RTC除頻器的周期計數值。它們受RTC_CR暫存器的RTOFF位保護,當RTOFF值為'1'時允許進行寫操作。

image005  

image006  

 

註:如果輸入的時鐘頻率為32.768kHz(fRTCCLK),這個暫存器寫入7FFFh可獲得週期為1秒鐘的信號。

 

4.3.4 RTC prescaler divider register (RTC_DIVH / RTC_DIVL)

 

在TR_CLK的每個週期裡,RTC除頻器中計數器的值都會被重新設置為RTC_PRL暫存器的值。使用者可通過讀取RTC_DIV暫存器,取得除頻器計數器的當前值,而不停止除頻器的工作,從而獲得精確的時間測量。此暫存器是只讀暫存器,其值在RTC_PRL或RTC_CNT暫存器中的值發生改變後,由硬體重新裝載。

image007  

4.3.5 RTC counter register (RTC_CNTH / RTC_CNTL)

RTC核心有一個32位可編程的計數器,可通過兩個16位的暫存器訪問。計數器以除頻器產生的TR_CLK時間基準為參考進行計數。 RTC_CNT暫存器用來存放計數器的計數值。他們受RTC_CR上的位RTOFF寫保護,僅當RTOFF值為'1'時,允許寫操作。在RTC_CNTH或RTC_CNTL)上的進行寫操作,能夠直接裝載到相應的可編程計數器,並且重新裝載RTC除頻器。當進行讀操作時,直接返回計數器內的計數值(系統時間)。

image008  

 

4.3.6 RTC alarm register high (RTC_ALRH / RTC_ALRL)

 

當可編程計數器的值與RTC_ALR中的32位值相等時,即觸發一個鬧鐘事件,並且產生RTC鬧鐘中斷。此暫存器受RTC_CR暫存器裡的RTOFF位寫保護,僅當RTOFF值為'1'時,允許寫操作。

image009  

image010  

 

4.4  RTC Standard Driver Library

 

以下介紹RTCFirmware Library functions

 

3.4.1 RTC register structure

 

RTC 暫存器結構, RTC_TypeDef, 定義在 stm32f10x_map.h 檔案,如以下敘述:

 

 

 

 image011  

 

RTC的暫存器介紹:

image012  

 

RTC周邊宣告在stm32f10x_map.h:

image013  

 

4.4.2 RTC firmware library function

 

以下提供了各種樣式的RTC function:

image014  

以下列舉幾項實驗會用到的Function,詳細請參考ARM-based 32-bit MCU STM32F101xx and STM32F103xx firmware library.

l   RTC Interrupt Configure Function

image015  

image016  

l   RTC EnterConfigure Mode Function

image017  

l   RTC ExitConfigure Mode Function

image018  

l   RTCGet Counter Function

image019  

l   RTCSet Counter Function

image020  

l   RTCSetPrescaler  Function

image021  

l   RTCSetAlarm  Function

image022  

l   RTCGetFlag Status Function

image023  

image024  

l   RTCClearFlag Status Function

image025  

4.5  立即演練

嵌入式軟體架構如圖說明:

image026  

嵌入式軟體Grafcet:

image027  

image028  

實驗1  使用超級終端機顯示時間

實驗說明:使用RTC second interrupt來產生時間,並用Virtual COM Port 在超級終端機上顯示時間。

程式碼片段介紹:

 

int main(void)

{

#ifdef DEBUG

       debug();

#endif

       NVIC_Configuration(); /* NVIC configuration */

       USART_Configuration(); /* Configure the USART1 */

       if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)

{      

 

 

 

 

 

      /*接續上頁*/

printf("\r\n\n RTC not yet configured....");     /* RTC Configuration */

              RTC_Configuration();

              printf("\r\n RTC configured....");

              Time_Adjust();                   /* Adjust time by values entred by the user on the hyperterminal */

              BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);

       } 

else                                                  /* Check if the Power On Reset flag is set */

       {

               if(RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)

                    printf("\r\n\n Power On Reset occurred....");

               else if(RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)

          printf("\r\n\n External Reset occurred....");       

     printf("\r\n No need to configure RTC....");

     RTC_WaitForSynchro(); /* Wait for RTC registers synchronization */

     RTC_ITConfig(RTC_IT_SEC, ENABLE); /* Enable the RTC Second */

    /* Wait until last write operation on RTC registers has finished */

     RTC_WaitForLastTask();

  }

  RCC_ClearFlag(); /* Clear reset flags */

  while(1)

       Time_Show();

}

 

 

 

l  NVIC Configure

 

 

 

void NVIC_Configuration(void)

{

    NVIC_InitTypeDef NVIC_InitStructure;

#ifdef  VECT_TAB_RAM 

  /* Set the Vector Table base location at 0x20000000 */

  NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);

#else  /* VECT_TAB_FLASH  */

  /* Set the Vector Table base location at 0x08003000 */

  NVIC_SetVectorTable(0x08003000, 0x0);  

#endif

 

 

 

 

 

 

        /*接續上頁*/

 

    /* Configure one bit for preemption priority */

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    /* Enable the RTC Interrupt */

    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQChannel;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

}

 

 

 

 

 

l   RTC Configure

 

void RTC_Configuration(void)

{

  /* Enable PWR and BKP clocks */

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

  PWR_BackupAccessCmd(ENABLE); /* Allow access to BKP Domain */

  BKP_DeInit();                /* Reset Backup Domain */

  RCC_LSEConfig(RCC_LSE_ON);   /* Enable LSE */

 while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); /* Wait till LSE is ready */

  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);        /* Select LSE as RTC Clock Source */

  RCC_RTCCLKCmd(ENABLE);                               /* Enable RTC Clock */

  RTC_WaitForLastTask();

  RTC_WaitForSynchro();   /* Wait for RTC registers synchronization */

  RTC_WaitForLastTask(); /* Wait until last write operation on RTC registers has finished */

  RTC_ITConfig(RTC_IT_SEC, ENABLE);  /* Enable the RTC Second */

  RTC_WaitForLastTask(); /* Wait until last write operation on RTC registers is terminated */

/*接續上頁*/

  /* Set RTC prescaler: set RTC period to 1sec */

  RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */

  RTC_WaitForLastTask(); /* Wait until last write operation on RTC registers has finished */

}

 

 

 

 


  Time_Regulate( ) & Time_Adjust( ) function

u32 Time_Regulate(void)

{

  u32 Tmp_HH = 0xFF, Tmp_MM = 0xFF, Tmp_SS = 0xFF;

    Tmp_HH =0;

    Tmp_MM = 0;

  Tmp_SS = 0;                *

  /* Return the value to store in RTC counter register */

  return((Tmp_HH*3600 + Tmp_MM*60 + Tmp_SS));

}

void Time_Adjust(void)

{   RTC_WaitForLastTask();

   RTC_SetCounter(Time_Regulate()); /* Change the current time */

   RTC_WaitForLastTask();

}

 

 


Time_Display( ) & Time_Show( ) function

 

 

 

void Time_Display(u32 TimeVar)

{

  u32 THH = 0, TMM = 0, TSS = 0;

  THH = TimeVar / 3600; /* Compute  hours */

  TMM = (TimeVar % 3600) / 60; /* Compute minutes */

  TSS = (TimeVar % 3600) % 60; /* Compute seconds */

  printf("Time: %0.2d:%0.2d:%0.2d\r", THH, TMM, TSS);

}

void Time_Show(void)

{  printf("\n\r");

  while (1) /* Infinite loop */ {

  if (TimeDisplay == 1) /* If 1s has paased */ {

      Time_Display(RTC_GetCounter()); /* Display current time */

      TimeDisplay = 0;

    }

}

 

 

 

l   RTC Interrupt Handler

image030  

實驗結果:

第一次啟動 (BKP_DR1) != 0xA5A5

image031  

 

將SIOC Reset(不斷電) (BKP_DR1) == 0xA5A5

image032  

 

實驗2  調整RTC時間

 

實驗說明:利用 RTC second interrupt來產生時間,並用Virtual COM Port 在超級終端機上顯示時間,請修改u32 Time_Regulate(void) ,讓使用者自行輸入現在時間, 使用printf()scanf()

程式碼片段介紹:

 

u32 Time_Regulate(void)

{

  u32 Tmp_HH = 0xFF, Tmp_MM = 0xFF, Tmp_SS = 0xFF;

/* modify your code

* return((Tmp_HH*3600 + Tmp_MM*60 + Tmp_SS));  /* Return the value to store in RTC counter register */

}

void Time_Adjust(void)

{

   RTC_WaitForLastTask(); /* Wait until last write operation has finished */

   RTC_SetCounter(Time_Regulate()); /* Change the current time */

   RTC_WaitForLastTask(); /* Wait until last write operationhas finished */

}

 

 

 

實驗結果:

image033  

 

實驗3  使用RTC鬧鐘

 

實驗說明:利用RTC alarm interrupt 製作鬧鐘,請使用者先輸入現在時間,再輸入鬧鐘時間,請參考RTC second interrupt 的使用方式,修改void TC_Configuration(void) 、void Time_Adjust(void)、void RTC_IRQHandler(void)

 

程式碼片段介紹:

 

l  RTC Configure

 

/* RTC Configure */

/* Enable the RTC Second */

  RTC_ITConfig(RTC_IT_ALR, ENABLE );

/* Wait until last write operation on RTC registers is finished */

  RTC_WaitForLastTask();

 

 

 

 

l  Time Adjust

 

void Time_Adjust(void)

{

   RTC_WaitForLastTask(); /* Wait until last write operation on RTC registers has finished */

   RTC_SetCounter(Time_Regulate()); /* Change the current time */

   RTC_WaitForLastTask(); /* Wait until last write operation on RTC registers has finished */

/* modify your code

  RTC_SetAlarm(0x10);

  RTC_WaitForLastTask();

*/

}

 

 

 

 

l  RTC Interrupt Handler

 

/* Interrupt Handler */

void RTC_IRQHandler(void)

{

   if (RTC_GetITStatus(RTC_IT_SEC) != RESET)  {

     RTC_ClearITPendingBit(RTC_IT_SEC); /* Clear RTC Second interrupt */

     TimeDisplay = 1; /* Enable time update */

     /* Wait until last write operation on RTC registers has finished */

     RTC_WaitForLastTask();

     /* Reset RTC Counter when Time is 23:59:59 */

     if (RTC_GetCounter() == 0x00015180) {

       RTC_SetCounter(0x0);

       /* Wait until last write operation on RTC registers has finished */

      RTC_WaitForLastTask();

     }

   }

/* modify your code

        if(RTC_GetITStatus(RTC_IT_ALR) != RESET){

           …………

        } */

}

 

 

 

 

實驗結果:

 

 

image034  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 youboook 的頭像
    youboook

    youboook的部落格

    youboook 發表在 痞客邦 留言(0) 人氣()