STM32_RTC君

        五一假期已過,你們是否還像五一五二五三那樣快樂呢??答案就交給大家本身尋找了哈、、說到五1、、就從五一開始的那一刻起、、就開始計時着、、到五一假期結束、、呵呵、、在這裏,智商和情商比我高的人估計又猜到我要說什麼了、、關於日期,關於時間、STM32也提供了強大的RTC模塊、、至於RTC模塊究竟是哪三個英文單詞的縮寫,我就不說了、好了,言歸正傳、這個語氣、請你們想一想周星馳唐伯虎點秋香當中華安與對穿王的對白:我倆、、、言歸正傳:算法

       在這裏、先別急,我們來看看通常的RTC模塊(芯片):編程

   一、 描述:RTC芯片是一種能提供日曆(年月日)/時鐘(時分秒)、數據存儲等功能的專用集成電路;數組

   二、應用環境:一般使用後備電池,以保證其在系統掉電的狀況下運行;函數

                      其時鐘源由外部32.768KHz晶振提供,能夠實現鬧鐘功能。字體

   三、做用:應用於某些系統的時鐘信號源和參數設置的存儲電路。spa

                RTC具備計時準確、耗電低和體積小等特色,特別是在各類嵌入式系統中用於記錄事件發生的時間和相關信息, 如通訊工程、電力自動化、工業控制等領域。code

   再來看看STM32中RTC有哪些區別呢(概述,具體分析請別急哈、):blog

   一、不具備提供日曆/時鐘功能;接口

   二、可以提供一個精確的秒週期信號:配置RTCCLK以及RTC_DIV,使得預分頻器產生頻率爲1秒的秒脈衝(TR_CLK),做爲RTC的時鐘基準事件

   三、具備鬧鐘功能。

   4:特色:(1)保護寄存器---防止誤操做(請注意這一點哈,很是關鍵

                (2)3個事件/中斷源:秒、溢出、鬧鐘(連外部中斷線17上,用於將MCU從中止模式喚醒)

                (3)RTC內核和時鐘的設置位於備份區域(也請注意這一點哈,由於要使RTC能記憶,就靠它了):

                        有獨立的VBAT供電;

                        只能由備份區域復位才能將其復位;

                        從待機模式喚醒後,RTC的設置仍被保留。

     啊哈、、以上摘自STM3210X+其他模塊培訓資料、、在這裏擺出來就是爲了給你們有一個比較全面的認識先、、莫告我盜版哈、、好了、、接下來,來看看時鐘吧、、

   翻開「葵花寶典」第STM32篇之RTC參考手冊能夠看出:能夠選擇如下三種RTC的時鐘源:(直接看圖哈這下就不說是美麗的塗鴉了、、、

  

在這裏,咱們選擇獨立的32.768KHz晶振(LSE)來做爲外部時鐘源、提早說下:只要在RTC的預分頻轉載寄存器中寫入0x7fff(也就是32767),就能夠產生以秒爲單位的信號了、、

接下來,咱們來看看參考手冊裏的一段話:(RTC核心)由一組可編程計數器組成,分紅兩個主要模塊。

第一個模塊是RTC的預分頻模塊,它可編程產生最長爲1秒的RTC時間基準TR_CLK。RTC的預分頻模塊包含了一個20位的可編程分頻器(RTC預分頻器)。若是在RTC_CR寄存器中設置了相應的容許位,則在每一個TR_CLK週期中RTC產生一箇中斷(秒中斷)。

第二個模塊是一個32位的可編程計數器,可被初始化爲當前的系統時間。系統時間按TR_CLK週期累加並與存儲在RTC_ALR寄存器中的可編程時間相比較,若是RTC_CR控制寄存器中設置了相應容許位,比較匹配時將產生一個鬧鐘中斷。

    接下來請允許我介紹下幾位「大神」:

    

     從紅色區域咱們能夠看出咱們這裏要實現以秒爲單位的計時,就要設置RTC_CRH的最低位爲1;

接下來請看(請注意紅色區域,再對照前面紅色字體的標註):

 還有幾個寄存器在此就不作具體介紹了,你們能夠參考中文手冊,在此僅列出:

     RTC預分頻裝載寄存器(RTC_PRLH/RTC_PRLL)

     RTC計數器寄存器 (RTC_CNTH / RTC_CNTL)

     注:由於RTC是由獨立的時鐘源提供,不掛載在ABP橋上,可是,奇怪的是;軟件又是經過ABP1橋來訪問RTC的寄存器,因爲可讀寄存器只在RTC ABP1橋時鐘進行同步的RTC時鐘上升沿被更新,可讀標誌位也是這樣,這就涉及到一個概念:同步,假如ABP1被剛剛開啓,在第一次的寄存器更新以前,從ABP1橋上讀取的RTC的寄存器就有可能被破壞了,爲了不此種狀況的發生,在讀取寄存器的時候必定要等待他們同步、、至於如何操做、、請看上圖寄存器的紅色標記

    好了、、人須要記憶、、不然失憶了,對彼此都不是一件好事、、固然、RTC也同樣、、你不但願RTC在系統復位或者一些意外後而是RTC失效、、因此、、RTC也要有記憶的功能、至於如何記憶呢?STM32也爲咱們提供了BKP模塊:翻開「葵花寶典」第STM32篇之BKP備份寄存器有這麼一段描述:

備份寄存器是42個16位的寄存器,可用來存儲84個字節的用戶應用程序數據。他們處在備份域裏,當VDD電源被切斷,他們仍然由VBAT維持供電。當系統在待機模式下被喚醒,或系統復位或電源復位時,他們也不會被複位。

  因此,咱們在這就要開啓BKP的時鐘、、

  然而、、事情並非那麼的簡單、、在這裏、、僅僅打開BKP的時鐘還不夠、、由於某種特殊緣由、、請看

 

由於系統復位後,RTC和後備寄存器處於被保護狀態以防意外寫入、、因此在此也要打開PWR的時鐘,在這裏,咱們須要取消保護,向後備寄存器寫入一個字節0x5050或者0x0505都行,以標註時鐘已經配置過了、、到時檢查這個字節來決定是否要從新配置、、

而BKP PWR時鐘都是掛載在ABP1橋上的:

那咱們要怎麼來操做呢:請看代碼註釋:

 1 u8 RTC_Init(void)
 2 {
 3    //檢查是否是第一次配置時鐘
 4     u8 temp=0;
 5    
 6      if(BKP_ReadBackupRegister(BKP_DR1)!= 0x5050)//從指定的後備寄存器中讀出數據,讀出了與寫入的指定數據不相同,這時候須要初始化
 7      {
 8       //1/經過使能PER和BKP外設時鐘來打開電源盒後備接口的時鐘使其能對後備寄存器和RTC的訪問
11    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能時鐘
12    PWR_BackupAccessCmd(ENABLE);    //取消備份域寫保護13          //2/復位備份域,開啓外部低速振盪器
14          /* Reset the BKP registers */ BKP_DeInit();
15          /* Enable the HSE */ RCC_LSEConfig(RCC_LSE_ON);
16          
17   while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)//等待低速時鐘準備就緒,在選擇時鐘前必定要等待時鐘就緒、、不然、後果嚴重
18          {
19         temp++;
20              delay_ms(10);
21      }
22          if(temp >= 250)
23          {
24         return 1;
25      }         
26          //3/選擇時鐘和使能時鐘
27          /* Select the LSE as RTC clock source */RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
28          /* Enable the RTC clock */RCC_RTCCLKCmd(ENABLE);
29          RTC_WaitForLastTask();    //等待最近一次對RTC寄存器的寫操做完成。注:必定要等寫操做完成
30          RTC_WaitForSynchro();        //等待RTC寄存器同步
31          
32          RTC_ITConfig(RTC_IT_SEC, ENABLE);        //使能秒中斷
33          
34          RTC_WaitForLastTask();    //等待最近一次對RTC的寫操做完成
35          RTC_EnterConfigMode();/// 容許配置    
36          
37          RTC_SetPrescaler(32767); //設置RTC的預分頻值
38          
39          RTC_WaitForLastTask();    //等待最近一次對RTC寫操做的完成
40          
41          RTC_Set(2014,5,4,12,0,5);  //設置時間
42          
43        RTC_ExitConfigMode(); //退出配置
44         
45      BKP_WriteBackupRegister(BKP_DR1, 0X5050);    // 向指定的後備域寫入一字節        
46 
47     }
48         else //不然,系統繼續計時
49         {
50          RTC_WaitForSynchro();    
51           RTC_ITConfig(RTC_IT_SEC, ENABLE);    //開啓秒中斷
52           RTC_WaitForLastTask();    
53     }
54         RTC_NVIC_Config();
55         RTC_Get();//更新時間
56         return 0;
57 }

   能夠看出、、若是咱們要配置下一次,必定要等待上次對RTC的操做是否完成,若是沒有,就必須等待上次結束才能開始下一次的操做、、同時,若是要對相關的寄存器進行寫操做。、必定要進入配置,配置完以後記得退出配置//..

     接下來,給中秒中斷的中斷服務函數,相信你們也比較清楚了:

 3 void RTC_IRQHandler(void)
 4 {         
 5     if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
 6     {                            
 7         RTC_Get();
 8      }
 9     if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)
10     {
11         RTC_ClearITPendingBit(RTC_IT_ALR);      
12       }                                                    
13     RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);        
14     RTC_WaitForLastTask();                                                   
15 }

注:對於相關的庫函數,因爲篇幅的緣由,在此就不仔細列出來了、請你們諒解、、你們能夠參照固件庫、、

在這裏給出戰艦原子的時間算法程序:就不細講了、、你們好好研究、、說實話、、我也有些不太懂、因此請你們多多交流:

//判斷是不是閏年函數//月份   1  2  3  4  5  6  7  8  9  10 11 12
//閏年   31 29 31 30 31 30 31 31 30 31 30 31
//平年   31 28 31 30 31 30 31 31 30 31 30 31
u8 Is_Leap_Year(u16 year)
{              
    if(year%4==0) //
    { 
        if(year%100==0) 
        { 
            if(year%400==0)return 1;
            else return 0;   
        }else return 1;   
    }else return 0;    
}    
//設置時鐘//時鐘=》秒//月份數據表
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正數據(我不清楚是怎麼來的)  
//這裏 const 表示這個數組裏的值不容許改變
//平年的月份日期表 const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31}; u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec) { u16 t; u32 seccount=0; if(syear<1970||syear>2099)return 1; for(t=1970;t<syear;t++) //把全部年份的秒鐘相加 { if(Is_Leap_Year(t))seccount+=31622400;//閏年的秒數 else seccount+=31536000; //不然爲平年 } smon-=1; for(t=0;t<smon;t++) //把前面的月份的秒數相加 { seccount+=(u32)mon_table[t]*86400;//月份秒數相加 if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//閏年2月份增長一條的秒數 } seccount+=(u32)(sday-1)*86400;//把前面日期的秒數相加 seccount+=(u32)hour*3600;//小時 seccount+=(u32)min*60; //分鐘 seccount+=sec;//總秒數 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE); RTC_SetCounter(seccount); RTC_WaitForLastTask(); //注意、、此時也要等待、、 return 0; } //返回當前的時間 u8 RTC_Get(void) { static u16 daycnt=0; u32 timecount=0; u32 temp=0; u16 temp1=0; timecount=RTC_GetCounter(); temp=timecount/86400; if(daycnt!=temp) { daycnt=temp; temp1=1970; while(temp>=365) { if(Is_Leap_Year(temp1)) { if(temp>=366)temp-=366; else {temp1++;break;} } else temp-=365; temp1++; } calendar.w_year=temp1; temp1=0; while(temp>=28) { if(Is_Leap_Year(calendar.w_year)&&temp1==1) { if(temp>=29)temp-=29; else break; } else { if(temp>=mon_table[temp1])temp-=mon_table[temp1];//ƽÄê else break; } temp1++; } calendar.w_month=temp1+1; calendar.w_date=temp+1; } temp=timecount%86400; calendar.hour=temp/3600; calendar.min=(temp%3600)/60; calendar.sec=(temp%3600)%60; calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date); return 0; } //得到是星期幾 爲了幫助理解,在原子論壇裏找出一份,(別告我侵權哈,我在這裏引用哈) u8 RTC_Get_Week(u16 year,u8 month,u8 day) { u16 temp2; u8 yearH,yearL; yearH=year/100; yearL=year%100; // 若是爲21世紀,年份數加100 if (yearH>19)yearL+=100; temp2=yearL+yearL/4; temp2=temp2%7; temp2=temp2+day+table_week[month-1]; if (yearL%4==0&&month<3)temp2--; return(temp2%7); }

有時候,想知道公元某年某月某日是星期幾,能夠用下面的公式算出來:                

      這裏的方括號表示只取商的整數部分。式中:

  x:這一年是公元多少年。

  y:這一天是這一年的第幾天。

  s:星期幾。不過要先除以7,再取餘數。沒有餘數是星期日,餘數是123456

    分別是星期1、星期2、星期3、星期4、星期5、星期六。

好比,今年國慶節(2010101)是星期幾?

  x2010

  y312831303130313130131×530×3281274

  s2010150220527427702770÷75

因此,今年國慶節是星期五。

若是,你只想知道這個公式怎樣用,到這兒就能夠了。而要想知道這個公式的道理是什麼,那可就說來話長了。

「星期制」是公元32137日,古羅馬皇帝君士坦丁宣佈開始實行的,而且規定這一天爲星期一。實際上,就是把公元元年元旦(公元111)規定爲星期一。(至關於公式中的x1y1,因此s1)

一般1年有365天,365÷752……1,就是說比52個星期多1天。因此,同一個日期,下一年是星期幾,就要比上一年向後推1天。好比,上一年元旦是星期三,下一年元旦就是星期四。

一般,每過1年,同一日期是星期幾就要向後推1」,是理解這個公式的關鍵。

要想知道某年某月某日是星期幾,首先,要知道這一年元旦以公元元年元旦是星期一爲起點,已經把星期幾向後推了多少天,還要知道這一天是這一年的第幾天。而要知道這一年元旦已經把星期幾向後推了多少天,能夠從公元元年到這一年已通過了多少年算起,先按1年向後推1天計算,再根據閏年的規定進行調整。

閏年的規定是:年份是4的倍數的通常都是閏年,其中,年份是整百數的通常不是閏年,只有年份是400的倍數的纔是閏年。

如今,能夠解釋公式中各部分的含義了。

 

  這樣一來,s就是在公元元年元旦是星期一的基礎上,須要把這一天是星期幾向後推的總天數。因此,s除以7取餘數,就能說明這一天是星期幾。(摘抄,敬請諒解哈)

 

   到這裏、、也接近尾聲了、、看了一下時間、、呵呵、、又忘了睡覺了、、待會還要上課、、好吧、、篇幅有點多、、但願你們耐心的看完、、相信看到前面一句話,也算是差很少看完了、、但願能對大家和之後的我有一個很好的理解上的幫助、、有寫得不對的地方但願能告訴我、、旁觀者清嘛、、謝謝你們、、五一事後、、你們仍需努力、、爲生活而奮鬥、、、

相關文章
相關標籤/搜索