STM32之RTC實時時鐘

2021-01-11 電子工程世界

RTC實時時鐘簡介: 
STM32的RTC外設,實質是一個掉電後還繼續運行的定時器,從定時器的角度來看,相對於通用定時器TIM外設,它的功能十分簡單,只有計時功能(也可以觸發中斷).但是從掉電還能繼續運行來看,它是STM32中唯一一個具有這個功能功能的外設.(RTC外設的複雜之處不在於它的定時,而在於它掉電還可以繼續運行的特性) 
所謂掉電,是指電源Vpp斷開的情況下,為了RTC外設掉電可以繼續運行,必須給STM32晶片通過VBAT引腳街上鋰電池.當主電源VDD有效時,由VDD給RTC外設供電.當VDD掉電後,由VBAT給RTC外設供電.無論由什麼電源供電,RTC中的數據始終都保存在屬於RTC的備份域中,如果主電源和VBA都掉電,那麼備份域中保存的所有數據都將丟失.(備份域除了RTC模塊的寄存器,還有42個16位的寄存器可以在VDD掉電的情況下保存用戶程序的數序,系統復位或電源復位時,這些數據也不會被復位). 
從RTC的定時器特性來說,它是一個32位的計數器,只能向上計數.他使用的時鐘源有三種,分別為: 
1,高速外部時鐘的128分頻:HSE/128; 
2,低速內部時鐘LSI; 
3,低速外部時鐘LSE; 
使用HSE分頻時鐘或者LSI的時候,在主電源VDD掉電的情況下,這兩個時鐘來源都會受到影響,因此沒法保證RTC正常工作.所以RTC一般都時鐘低速外部時鐘LSE,頻率為實時時鐘模塊中常用的32.768KHz,因為32768 = 2^15,分頻容易實現,所以被廣泛應用到RTC模塊.(在主電源VDD有效的情況下(待機),RTC還可以配置鬧鐘事件使STM32退出待機模式).

RTC工作過程: 

RTC架構: 
圖中淺灰色的部分都是屬於備份域的,在VDD掉電時可在VBAT的驅動下繼續運行.這部分僅包括RTC的分頻器,計數器,和鬧鐘控制器.若VDD電源有效,RTC可以觸發RTC_Second(秒中斷)、RTC_Overflow(溢出事件)和RTC_Alarm(鬧鐘中斷).從結構圖可以看到到,其中的定時器溢出事件無法被配置為中斷.如果STM32原本處於待機狀態,可由鬧鐘事件或WKUP事件(外部喚醒事件,屬於EXTI模塊,不屬於RTC)使它退出待機模式.鬧鐘事件是在計數器RTC_CNT的值等於鬧鐘寄存器RTC_ALR的值時觸發的. 
因為RTC的寄存器是屬於備份域,所以它的所有寄存器都是16位的.它的計數RTC_CNT的32位由RTC_CNTL和RTC_CNTH兩個寄存器組成,分別保存計數值的低16位和高16位.在配置RTC模塊的時鐘時,把輸入的32768Hz的RTCCLK進行32768分頻得到實際驅動計數器的時鐘TR_CLK = RTCCLK/37768 = 1Hz,計時周期為1秒,計時器在TR_CLK的驅動下計數,即每秒計數器RTC_CNT的值加1(常用)

由於備份域的存在,使得RTC核具有了完全獨立於APB1接口的特性,也因此對RTC寄存器的訪問要遵守一定的規則. 
系統復位後,禁止訪問後備寄存器和RCT,防止對後衛區域(BKP)的意外寫操作.(執行以下操作使能對後備寄存器好RTC的訪問): 
1,設置RCC_APB1ENR寄存器的PWREN和BKPEN位來使能電源和後備接口時鐘. 
2,設置PWR_CR寄存器的DBP位使能對後備寄存器和RTC的訪問. 
設置為可訪問後,在第一次通過APB1接口訪問RTC時,必須等待APB1與RTC外設同步,確保被讀取出來的RTC寄存器值是正確的,如果在同步之後,一直沒有關閉APB1的RTC外設接口,就不需要再次同步了. 
如果內核要對RTC寄存器進行任何的寫操作,在內核發出寫指令後,RTC模塊在3個RTCCLK時鐘之後,才開始正式的寫RTC寄存器操作.我們知道RTCCLK的頻率比內核主頻低得多,所以必須要檢查RTC關閉操作標誌位RTOFF當這個標誌被置1時,寫操作才正式完成. 
(以上操作在STM32庫裡面都有庫函數,不需要具體的查閱寄存器~~~~)

UNIX時間戳: 
假如從現在起,把計數器RTC_CNT的計數值置0,然後每秒加1,RTC_CNT什麼時候會溢出? RTC_CNT是一個32位寄存器,可存儲的最大值為(2^32-1),這樣的話就是在2^32秒之後溢出,大概換算為: 
Time = 2^32/365/24/60/60大約等於136年 
假如某個時刻讀取到計數器的數值為X = 60*60*24*2(2天),又知道計數器是在2016年1月1日的0時0分0秒置0的,那麼根據計數器的這個相對時間數值,可以計算得到這個時刻是2016年1月3日的0時0分0秒了,而計數器會在(2016+136)年左右溢出.(如果我們穿越回到2016年1月1日,如果還在使用這個計數器提供事件的話就會出問題啦.). 
定時器被置0的這個事件被稱為計時元年,相對計時元年經過的秒數稱為時間戳.

PS: 
大多數作業系統都是利用時間戳和計時元年來計算當前時間的,而這個時間戳和計時元年大家都取了同一個標準——UNIX時間戳和UNIX計時元年.UNIX 計時元年被設置為格林威治時間1970年1月1日0時0分0秒,大概是為了紀念UNIX的誕生吧.而UNIX時間戳即為當前時間相對於UNIX計時元年經過的秒數.在這個計時系統中,使用的是有符號的32位整型變量來保存UNIX時間戳的,即實際可用計數位數比我們上面例子中的少了一位,少了這一位,UNIX 計時元年也相對提前了,這個計時方法在2038年1月19日03時14分07秒將會發生溢出.這個時間離我們並不遠,UNIX時間戳被廣泛應用到各種系統中,溢出可能會導致系統發生嚴重錯誤,差不多到這個時候,記得注意這個問題呀.

實例分析: 
利用RTC提供北京時間: 
RTC外設這個連續計數的計數器,在相應軟體配置下,可提供時鐘日曆的功能,修改計數器的值則可以重新設置系統當前的時間和日期.而 由於它的時鐘配置系統(RCC_BDCR 寄存器)是在備份域,在系統復位或從待機模式喚醒後RTC的設置和時間維持不變,利用它,可以實現實時時鐘的功能.

main函數: 
struct rtc_time systmtime; 
int main(void) 

/串口配置/ 
USART1_Config(); 
/配置RTC秒中斷優先級/ 
NVIC_Configuration(); 
//RTC檢測及配置 
RTC_CheckAndConfig(&systmtime); 
//刷新時間 
Time_Show(&systmtime); 

main函數流程: 
1,用到了串口,配置好串口(代碼和之前的例程一樣); 
2,配置RTC秒中斷優先級,這裡設置主優先級為1,次優先級為0(只用到一個RTC,中斷隨便寫都可以).(代碼和之前的中斷例程相似,只不過中斷通道不一樣,這裡使用的中斷通道是RTC_IRQn); 
3,查看RTC外設是否在本次VDD上電前被配置過,如果沒有被配置過,則需要輸入當前時間,重新初始化RTC和配置時間; 
4,配置好RTC後,根據秒中斷設置的標誌位,每隔1秒向終端更新一次;

事件管理結構體 rtc_time 
struct rtc_time 

int tm_sec; 
int tm_min; 
int tm_hour; 
int tm_mday; 
int tm_mon; 
int tm_year; 
int tm_wday; 

這個類型的結構體有時,分,秒,日,月,年及星期7個成員.當需要給RTC的計時器重新配置時間時(更改時間戳),肯定不會詢問用戶現在距離UNIX計時元年過了多少秒,而是向用戶詢問現在的公元紀年,以及所在時區的事件.根據RTC計時器向用戶輸出時間. 
這就是 rtc_time 這個結構體的作用,配置RTC時,保存用戶輸入的時間,其它函數通過它求出UNIX時間戳,寫入RTC,RTC正常運行後,需要輸出時間時,其它函數通過RTC獲取UNIX時間戳,轉化成用友好的時間表示方式保存在這個結構體上.

PS: 
起始在C語言標準庫ANSI C中,也有類似的結構體所以 struct tm,位於標準的time.h文件中,轉化函數是mktime()和localtime(),分別把tm結構體成員轉化成時間戳和用時間戳轉化成結構體成員.

檢查RTC RTC_CheckAndConfig()

void RTC_CheckAndConfig(struct rtc_time *tm) 

/檢查備份寄存器BKP_DR1,內容不為0xA5A5,則需要重新配置時間並且詢問用戶調整時間/ 
if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5) 

printf(「\r\n\r\n RTC not yet configured….」); 
/* RTC 配置 */ 
RTC_Configuration(); 
printf(「\r\n\r\n RTC configured….」); 
/* 用戶輸入時間*/ 
Time_Adjust(tm); 
/再往備份寄存器BKP_DR1寫入0xA5A5/ 
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); 

/啟動無需設置新時鐘/ 
else 

/檢查是否掉電重啟/ 
if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET) 

printf(「\r\n\r\n Power On Reset occurred….」); 

/檢查是否Reset復位/ 
else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET) 

printf(「\r\n\r\n External Reset occurred….」); 

printf(「\r\n No need to configure RTC….」); 
/等待寄存器同步/ 
RTC_WaitForSynchro(); 
/允許RTC秒中斷/ 
RTC_ITConfig(RTC_IT_SEC, ENABLE); 
/等待上次RTC寄存器寫操作完成/ 
RTC_WaitForLastTask(); 

/定義了時鐘輸出宏,則配置校正時鐘輸出到 PC13,用於RTC時鐘頻率的校準或調整時間補償/ 
#ifdef RTCClockOutput_Enable 
/使能PWR和BKP的時鐘/ 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); 
/允許訪問BKP備份域/ 
PWR_BackupAccessCmd(ENABLE); 
/輸出64分頻時鐘/ 
BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock); 
#endif 
RCC_ClearFlag(); 

if語句調用BKP_ReadBackupRegister()讀取RTC備份域寄存器裡面的值,判斷備份寄存器裡面的是否正確,根據後面代碼,如果配置成功,會向備份域寄存器寫入數值0xA5A5. 
(這個數值在VDD掉電後仍然會保存,如果VBAT也掉電,那麼備份域,RTC所有寄存器將被復位,這時這個寄存器的值就不會等於0xA5A5了,RTC的計數器的值也是無效的. 
簡單的說,就是寫入的這個數值用作標誌RTC是否從未被配置或配置是否已經失效,然後寫入任何數值到任何一個備份域寄存器,只要檢查的時候與寫入值匹配就行了)

RTC未被配置或者配置已經失效的情況: 
1,如果RTC從未被配置或者配置已經失效(備份域寄存器寫入值等於0xA5A5)這兩種情況其中一種為真的話,則調用RTC_Configuration()來初始化RTC,配置RTC外設的控制參數,時鐘分頻等,並往電腦的超級終端列印出相應的調試信息; 
2,初始化好RTC之後,調用函數 Time_Adjust() 讓用戶鍵入(通過超級終端輸入)時間值; 
3,輸入時間值後,Time_Adjust() 函數把用戶輸入的北京時間轉化為UNIX時間戳,並把這個UNIX時間戳寫入到RTC外設的計數寄存器RTC_CNT.接著RTC外設在這個時間戳的基礎上,每秒對RTC_CNT加1,RTC時鐘就運行起來了,並且在VDD掉電還運行,以後需要知道時間就直接讀取RTC的計時值,就可以計算出時間了; 
4,設置好時間後,調用BKP_WriteBackupRegister()把0xA5A5這個值寫入備份域寄存器,作為配置成功的標誌;

確認RTC曾經被配置過的情況: 
1,調用RCC_GetFlagStatus檢測是上電復位還是按鍵復位,根據不同的復位情況在超級終端中列印出不同的調試信息(兩種復位都不需要重新設置RTC裡面的時間值); 
2,調用RTC_WaitForSynchro等待APB1接口與RTC外設同步,上電後第一次通過APB1接口訪問RTC時必須要等待同步; 
3,同步完成後調用RTC_ITConfig()使能RTC外設的秒中斷(使能RTC的秒中斷是一個對RTC外設寄存器的寫操作); 
4,進行寫操作以後,必須調用RTC_WaitForLastTask()來等待,確保寫操作完成;

在下面有一個條件編譯選項詢問是否需要output RTCCLK/64 on Tamper pin,這是RTC的時鐘輸出配置,在rtc的頭文件定義 RTCClockOutput_Enable這個宏,PC13引腳會輸出RTCCLK的64分頻時鐘,主要是用於RTC時鐘頻率的校準或調整時間補償. 
(如果需要用到這個時鐘信號的話,只需要在頭文件定義RTCClockOutput_Enable這個宏就行了,不要定義為0值就行了~~~~)

初始化RTC RTC_Configuration(): 
void RTC_Configuration(void) 

/使能PWR和BKP時鐘/ 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); 
/對備份域進行軟體復位/ 
PWR_BackupAccessCmd(ENABLE); 
/對備份域進行軟體復位/ 
BKP_DeInit(); 
/* 使能低速外部時鐘 LSE */ 
RCC_LSEConfig(RCC_LSE_ON); 
/* 等待LSE起振穩定 */ 
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) 
{} 
/* 選擇LSE作為 RTC 外設的時鐘*/ 
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); 
/* 使能RTC時鐘 */ 
RCC_RTCCLKCmd(ENABLE); 
/* 等待RTC寄存器與APB1同步*/ 
RTC_WaitForSynchro(); 
/* 等待對RTC的寫操作完成*/ 
RTC_WaitForLastTask(); 
/* 使能RTC秒中斷 */ 
RTC_ITConfig(RTC_IT_SEC, ENABLE); 
/* 等待對RTC的寫操作完成 */ 
RTC_WaitForLastTask(); 
/* 設置RT 時鐘分頻: 使RTC定時周期為1秒 */ 
RTC_SetPrescaler(32767); 
/* RTC 周期 = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */ 
/等待對RTC的寫操作完成 / 
RTC_WaitForLastTask(); 

在這個初始化函數裡,沒有見到熟悉的初始化結構體,對RTC的每一個初始化參數都是使用相應的庫函數來配置的.RTC作為備份域的一份子,在訪問前首先要使能備份域、電源管理外設的時鐘,設置備份域訪問權限,作為定時器,初始化時必須要選擇好時鐘來源,時鐘分頻.

時間調節Time_Adjust(): 
void Time_Adjust(struct rtc_time *tm) 

/* 等待前面可能的 RTC 寫操作完成 */ 
RTC_WaitForLastTask(); 
/* 利用串口,在終端向用戶詢問當前北京時間(年月日時分秒), 
寫入到 rtc_time 型結構體 */ 
Time_Regulate(tm); 
/* 計算輸入的日期是星期幾,把rtc_time型結構體填充完整 */ 
GregorianDay(tm); 
/* 根據輸入日期,計算出 UNIX 時間戳,修改當前 RTC 計數寄存器內容*/ 
RTC_SetCounter(mktimev(tm)); 
/* 等待 RTC 寫操作完成 */ 
RTC_WaitForLastTask(); 

這裡流程就是使用Time_Regulate()從終端獲取當前北京時間,然後根據用戶的輸入,調用函數mktimev()根據用戶輸入的年,月,日,時,.分,秒數據,計算出相應的UNIX時間戳,最後調用庫函數RTC_SetCounter()把這個UNIX時間戳寫入到計數器RTC_CNT,RTC就正式運行了.

獲取時間Time_Regulate(): 
void Time_Reglate(struct rtc_time *tm) 

u32 Tmp_YY = 0xFF, Tmp_MM = 0xFF, Tmp_DD = 0xFF, Tmp_HH =0xFF, Tmp_MI = 0xFF, Tmp_SS = 0xFF;

printf("\r\n==========Time Settings==================");


printf("\r\n 請輸入年份(Please Set Years): 20");

while (Tmp_YY == 0xFF)

{

Tmp_YY = USART_Scanf(99);

}

printf("\n\r 年份被設置為: 20%0.2d\n\r", Tmp_YY);

tm->tm_year = Tmp_YY+2000;


Tmp_MM = 0xFF;

printf("\r\n 請輸入月份(Please Set Months): ");

while (Tmp_MM == 0xFF)

{

Tmp_MM = USART_Scanf(12);

}

printf("\n\r 月份被設置為: %d\n\r", Tmp_MM);

tm->tm_mon= Tmp_MM;


Tmp_DD = 0xFF;

printf("\r\n 請輸入日期(Please Set Dates): ");

while (Tmp_DD == 0xFF)

{

Tmp_DD = USART_Scanf(31);

}

printf("\n\r 日期被設置為: %d\n\r", Tmp_DD);

tm->tm_mday= Tmp_DD;


Tmp_HH = 0xFF;

printf("\r\n 請輸入時鐘(Please Set Hours): ");

while (Tmp_HH == 0xFF)

{

Tmp_HH = USART_Scanf(23);

}

printf("\n\r 時鐘被設置為: %d\n\r", Tmp_HH );

tm->tm_hour= Tmp_HH;


Tmp_MI = 0xFF;

printf("\r\n 請輸入分鐘(Please Set Minutes): ");

while (Tmp_MI == 0xFF)

{

Tmp_MI = USART_Scanf(59);

}

printf("\n\r 分鐘被設置為: %d\n\r", Tmp_MI);

tm->tm_min= Tmp_MI;


Tmp_SS = 0xFF;

printf("\r\n 請輸入秒鐘(Please Set Seconds): ");

while (Tmp_SS == 0xFF)

{

Tmp_SS = USART_Scanf(59);

}

printf("\n\r 秒鐘被設置為: %d\n\r", Tmp_SS);

tm->tm_sec= Tmp_SS; 

這裡就是在裡面從終端獲取用戶輸入的時間,要留意的是,從終端輸入的ASCII碼,而不是實際數值(在USART_Scanf裡面做處理)


PS:這裡補上USART_Scanf()的代碼,之前串口篇的時候好像沒有附上 

static uint8_t USART_Scanf(uint32_t value) 

uint32_t index = 0; 

uint32_t tmp[2] = {0, 0}; 

while (index < 2) 

while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) ==RESET) 

{}


    tmp[index++] = (USART_ReceiveData(USART1));

    /*數字0到9的ASCII碼為0x30至0x39*/

    if((tmp[index - 1] < 0x30) || (tmp[index -1] > 0x39))

    {

        printf("\n\rPlease enter valid number between 0 and 9 -->: ")

        index--;

    }

}

/* 計算輸入字符的 ASCII 碼轉換為數字*/

index = (tmp[1] - 0x30) + ((tmp[0] - 0x30) * 10);


if (index > value)

{

    printf("\n\rPlease enter valid number between 0 and %d", value);

    return 0xFF;

}

return index;   


}


計算UNIX時間戳mktimev(): 

從用戶端獲取了北京時間後,就可以用它換成 UNIX 時間戳了,但不能忽略一個重要的問題——時差.UNIX時間戳的計時元年是以標準時間(GMT 時區)為準的,而北京時間為 GMT+8,即時差為+8小時.為了保證我們寫入到RTC_CNT的是標準的UNIX時間戳(主要是為了兼容),以北京時間轉化出的秒數要減去8*60*60才是標準的UNIX時間戳. 

u32 mktimev(struct rtc_time *tm) 

if (0 >= (int) (tm->tm_mon -= 2)) 

tm->tm_mon += 12; 

tm->tm_year -= 1; 

/計算出輸入的北京時間的一共的秒數/ 

return((( (u32)(tm->tm_year/4 - tm->tm_year/100 + tm->tm_year/400 + 367*tm->tm_mon/12 + tm->tm_mday) 

+ tm->tm_year*365 - 719499)*24 + tm->tm_hour)*60 + tm->tm_min)*60 + tm->tm_sec-8*60*60; 

/8*60*60把輸入的北京時間轉換為標準時間在寫入計時器中,確保計時器的數據為標準UNIX時間戳/ 

8*60*60把輸入的北京事件轉換為標準事件在寫入計時器中,確保計時器的數據為標準UNIX時間戳,如果向使用其他時區,則根據不同喲的時區修改這個值. 

返回值最終被寫入到RTC_CNT計數器中RTC_SetCounter(mktimev(tm));


輸出時間到終端Time_Show(): 

void Time_Show(struct rtc_time *tm) 

while (1) 

/每個1s/ 

if(TimeDisplay == 1) 

/顯示時間/ 

Time_Display(RTC_GetCounter(),tm); 

TimeDisplay = 0; 

TimeDisplay是RTC秒中斷標誌,RTC的秒中斷被觸發後,進入中斷服務函數,把這個變量 TimeDisplay置1.這個函數是死循環檢查這個標誌,變為1時,調用Time_Display()顯示最新時間,實現每隔1秒向終端更新一次時間,更新完後再把 TimeDisplay置0,等待下次秒中斷.


RTC秒中斷服務函數: 

void RTC_IRQHandler(void) 

if (RTC_GetITStatus(RTC_IT_SEC) != RESET) 

/* 清除秒中斷標誌 */ 

RTC_ClearITPendingBit(RTC_IT_SEC); 

/* 把標誌位置 1 */ 

TimeDisplay = 1; 

/* 等待寫操作完成 */ 

RTC_WaitForLastTask(); 

在這個函數中並沒有任何對RTC_CNT的操作,如果VDD掉電,RTC是無法觸發秒中斷的,所以想利用秒中斷的方案實現實時時鐘是不現實的,秒中斷最適合用在類似本例程的觸發顯示的時間更新場合,而不是用於計數.


顯示時間Time_Display(): 

void Time_Display(uint32_t TimeVar,struct rct_time *tm) 

static uint32_t FirstDisplay = 1; 

uint32_t BJ_TimeVar; 

uint8_t str[15]; // 字符串暫存


/* 把標準時間轉換為北京時間*/

BJ_TimeVar =TimeVar + 8*60*60;

/*利用時間戳轉換為北京時間*/

to_tm(BJ_TimeVar, tm);


if((!tm->tm_hour && !tm->tm_min && !tm->tm_sec) || (FirstDisplay))

{

    GetChinaCalendar((u16)tm->tm_year, (u8)tm->tm_mon, (u8)tm->tm_mday, str);


    printf("\r\n\r\n 今天農曆:%0.2d%0.2d,%0.2d,%0.2d", str[0], str[1], str[2], str[3]);


    GetChinaCalendarStr((u16)tm->tm_year,(u8)tm->tm_mon,(u8)tm->tm_mday,str);

    printf(" %s", str);


    if(GetJieQiStr((u16)tm->tm_year, (u8)tm->tm_mon, (u8)tm->tm_mday, str))

    {

        printf(" %s\n\r", str);

    }

    FirstDisplay = 0;

}

printf("\r UNIX 時間戳 = %d ,當前時間為: %d 年(%s 年) %d 月 %d日 (星期%s) %0.2d:%0.2d:%0.2d",TimeVar,tm->tm_year, zodiac_sign[(tm->tm_year-3)%12], tm->tm_mon, tm->tm_mday,WEEK_STR[tm->tm_wday], tm->tm_hour,tm->tm_min, tm->tm_sec);


這裡的第一個輸入參數為UNIX時間戳,在Time_Show()調用的時候,利用庫函數RTC_GetCounter()讀取了RTC_CNT的當前數值,並把這個計數值作為Time_Dispaly()的輸入參數. 

根據配置,RTC_CNT的計數值是標準時間GMT的UNIX時間戳,為了計算北京時間,在使用RTC_CNT計數值轉換北京時間時,要加上時差(BJ_TimeVar =TimeVar + 8*60*60;).之後,把這個變量 BJ_TimeVar作為函數 to_tm()的輸入參數,把時間戳轉換成年,月,日,時,分,秒的格式,並保存到時間結構體中. 

(to_tm()(純算法)和GetChinaCalendar()這裡就不展開了,需要的話可以留言我會發送給你)


PS: 

如果要使用普通的51晶片實現實時時鐘,需要藉助時鐘晶片,DS1302或DS12C887,在STM32裡面只要用到一個定時器就搞掂了!!!


關鍵字:STM32  RTC  實時時鐘 編輯:什麼魚 引用地址:http://news.eeworld.com.cn/mcu/2018/ic-news071740383.html

相關焦點

  • avr單片機和stm32區別與優缺點分析
    摘要:avr單片機和stm32單片機是目前使用較廣泛的單片機,那麼avr單片機和stm32單片機有什麼區別呢?有什麼優劣勢呢?可以廣泛應用於計算機外部設備、工業實時控制、儀器儀表、通訊設備、家用電器等各個領域。   avr單片機特點:   1. avr系列沒有類似累加器A的結構,它主要是通過R16~R31寄存器來實現A的功能。
  • 用於車載T-BOX等高精度計時的汽車級時鐘模塊RTC:RA8900CE
    天來給大家講解下用於車載T-BOX等高精度計時的汽車級時鐘模塊RTC:RA8900CE。車載實時時鐘晶片RA8900CE內置32.768Khz的晶體,實現年、月、日、星期、小時、分鐘和秒精準計時。RA8900CE滿足AEC-Q200認證,內置溫補功能,保證實時時鐘的穩定可靠,功耗低至0.7uA,可滿足低功耗需求。車載遠程信息終端T-BOX,是汽車與網絡的交互樞紐,通過CAN通訊網絡連接車身控制系統,通過藍牙、4G連接網際網路,實現車聯網功能。對於車載T-BOX來說,也要同時滿足作為黑盒子(事件記錄)的功能,因此需要對時間、汽車狀態事件等重要信息進行記錄,需要用到可靠的時鐘源。
  • 超級電容(法拉電容)和實時時鐘
    打開APP 超級電容(法拉電容)和實時時鐘 發表於 2020-04-30 11:34:55 涓流充電時鐘晶片是一個可編程3線串行接口時鐘晶片,可用超級電容或可充電電池備份系統的時間和日期,還提供31位元組的非易失SRAM用於數據存儲。由DS1302和超級電容構成的電源備份電路,DS1302的VCC2接主電源,VCC1接超級電容正極。
  • stm32_timer基本定時器配置及實現燈閃爍
    下面以定時器2~7的時鐘說明這個倍頻器的作用:當APB1的預分頻係數為1時,這個倍頻器不起作用,定時器的時鐘頻率等於APB1的頻率;當APB1的預分頻係數為其它數值(即預分頻係數為2、4、8或16)時,這個倍頻器起作用,定時器的時鐘頻率等於APB1的頻率兩倍。
  • 高精度時鐘晶片有哪些?(四款高精度時鐘晶片介紹)
    四、時鐘晶片具有數據斷電保護作用。 時鐘晶片之作用能夠記錄和存儲數據,是因為其內部有一個RAM單元,此ram單元一部分用於對時鐘顯示的控制,絕大一部用於單元數據的存儲,而且此RAM單位具有著斷電保護功能。 五、時鐘晶片具有很好的檢測功能。
  • EEE1588精密網絡同步時鐘協議(時鐘伺服器)-v2.0協議淺析
    但是,只有「同步的」的IP網絡才是一個真正的電信級網絡,才能夠為IP網絡傳送各種實時業務與數據業務的多重播放業務提供保障。從通信關係上又可把時鐘分為主時鐘和從時鐘,理論上任何時鐘都能實現主時鐘和從時鐘的功能,但一個PTP通信子網內只能有一個主時鐘。整個系統中的最優時鐘為最高級時鐘GMC(Grandmaster Clock),有著最好的穩定性、精確性、確定性等。
  • STM32入門之USART
    在同步通信的方式下,要求收發雙方以相同的時鐘頻率進行,而且準確協調,通過共享時鐘信號,保證收發雙方的準確同步,效率較高。因此同步通信方式傳輸速率較高,但實現比較複雜。常見的通信如:I2C和SPI.異步通信的話,無須共享同一個時鐘信號即可發送和接受,但雙方需要約定遵循相同的異步通信協議即規定傳輸的數據包的內容,一般由起始位、主體數據、校驗位以及停止位組成,通訊雙方的數據包格式要約定一致才能正常收發數據。除此之外,波特率也應相同。按照數據在兩個通信站點的數據流向串行通信可分為全雙工,半雙工和單工。
  • STM32四種庫對比:寄存器、標準外設庫、HAL、LL
    https://www.st.com/en/embedded-software/stm32snippets.html (公號不支持外連結,請複製連結到瀏覽器打開) https://www.st.com/en/embedded-software/stm32-standard-peripheral-libraries.html
  • STM32單片機APB1和APB2的區分
    打開APP STM32單片機APB1和APB2的區分 發表於 2018-12-26 15:17:08 1.首先注意的的是圖中畫綠色圈圈的兩個,HSE和HSI分別表示外部時鐘和內部時鐘
  • 時鐘
    時鐘 生活工具 大小: 8.52M
  • STM32之TIMER定時器之間不同點
    使用定時器預分頻器和RCC時鐘控制預分頻器,可以實現脈衝寬度和波形周期從幾個微秒到幾個毫秒的調節。 高級控制定時器(TIM1和TIM8)和通用定時器(TIMx)是完全獨立的,它們不共享任何資源。它們可以同步操作。TIM6和TIME7是基本定時器,他們通常作為通用定時器提供時間基準,特別地可以為數模轉換器(DAC)提供時鐘。
  • 低功耗時鐘晶片RX8111CE和RX4111CE的特徵和應用
    大家好,今天給大家介紹愛普生RTC中的兩款低功耗時鐘晶片RX4111CE和RX8111CE。近年來,在電子設備集約化、小型化不斷加速的背景之下,驅動著市場要求RTC時鐘晶片具備高精度、小型化及低功耗的特性。愛普生實時時鐘模塊( RTC )相對於晶片內置RTC的產品由於具有獨立供電,所以設備總體斷電時也能保持計時模塊持續工作,具有更好的穩定性,節能效果更加突出。可以替換美國Dallas達拉斯的相關時鐘晶片。
  • 時鐘 - 時鐘下載_時鐘手機版下載【官方安卓版】-太平洋下載中心
    時鐘 系統安全 大小: 1.25M
  • 單片機系統中日曆時鐘自動校準及調整
    1、概述本文引用地址:http://www.eepw.com.cn/article/171826.htm在各類檢測控制系統中,需要通過日曆時鐘進行時間上的控制或對事件所發生的時間進行記錄。如電網檢測系統,路燈控制系統等。
  • 雲賞櫻、雲蹦迪……萬物皆可「雲」背後的RTC實時音視頻技術
    ,這是一項應用廣泛但鮮為人知的技術,雲蹦迪、雲看房、雲上課、雲招聘等背後都要給實時音視頻技術記一功。不同場景, RTC實時音視頻的訴求不同實時音視頻技術,英文全稱Real-time Conmunicaiton,簡稱RTC,主要負責實時音視頻通訊的穩定性和流暢性,關鍵詞在「實時」和「流暢」,所以在互動直播、視頻會議、遊戲連麥等應用場景發揮著至關重要的作用,廣泛的應用於社交、在線教育、遊戲電競、IoT、VR/AR、醫療等行業
  • 劍與遠徵:守護時間秩序-時鐘之神來也!
    今天介紹半神的時鐘,希望你們喜歡。(1) 故事背景時間是一股不可違抗力量,它能夠令容顏衰老,令刀劍生鏽,令樹木枯萎,一切有形之物都無法逃避被時間磨滅的命運。唯有時間本身是不朽的,參透了時間的奧秘,也就意味著掌握了永恆,這正是眾神不朽不滅的原因。
  • 用DS12887製作無電源仍可走時的時鐘
    也就是說不使用時這個時鐘不需要供電,在你加上電源之後就可以顯示時間,並且仍然是正確的。就好像有些手機關閉之後又取下電板,等你下次開機的時候它又能顯示正確的時間。其實這些手機裡面都是有後備電池的,就是做時鐘那一小塊電路有後備電池供著電的。要是後備電池沒有了電,取下電板後問題就來了,這也是為什麼有些用久了的手機取下電板再裝上,時間就不對了的緣故。
  • 同步時鐘系統在弱電智能化的應用
    時鐘也就是常見的顯示時間屏,其直觀顯示時間信息的方式,而網絡時鐘就是指通過網絡方式走NTP的協議來進行時間同步的時鐘。 5、軌道交通同步系統 時鐘系統是軌道交通重要的組成部分之一,除了為上班族、來往的遊客工作人員提供準確的時間信息,同步時鐘系統要為其他監控系統、控制系統等弱電子系統提供統一的時鐘信號,使各系統的定時集中同步,在整個地鐵系統中使用相同的定時標準。