9.1實(shí)驗(yàn)內(nèi)容
通過(guò)本實(shí)驗(yàn)主要學(xué)習(xí)以下內(nèi)容:
- RTC簡(jiǎn)介
- RTC復(fù)位
- RTC實(shí)現(xiàn)萬(wàn)年歷
- RTC使用注意事項(xiàng)
9.2實(shí)驗(yàn)原理
9.2.1RTC簡(jiǎn)介
RTC(Real Time Clock)——實(shí)時(shí)時(shí)鐘定時(shí)器,可以用作日歷。RTC電路分兩個(gè)電源域部分,其一位于備份域中,該部分包括一個(gè)32位的累加計(jì)數(shù)器、一個(gè)鬧鐘、一個(gè)預(yù)分頻器、一個(gè)分頻器以及RTC時(shí)鐘配置寄存器。備份域這部分電路不會(huì)因?yàn)橄到y(tǒng)復(fù)位或者MCU進(jìn)入低功耗而丟失數(shù)據(jù),所以在系統(tǒng)復(fù)位或MCU從低功耗下喚醒,RTC的設(shè)置和時(shí)間都可以保持不變。另一部分位于VDD電源域中,該部分只包括APB接口以及一組控制寄存器。
關(guān)于RTC的兩個(gè)電源域及分布在兩個(gè)電源域中的內(nèi)容,需要讀者牢記,否則會(huì)由于細(xì)節(jié)沒(méi)處理好導(dǎo)致RTC工作異常。 |
以下為GD32F303的RTC框圖:
圖中RTC_CNT為計(jì)數(shù)值,每個(gè)SC_CLK時(shí)鐘到來(lái)時(shí),這個(gè)計(jì)數(shù)值增加+1。SC_CLK時(shí)鐘源有三個(gè):LXTAL(外部低速晶振)、IRC40K(內(nèi)部40K晶振)和HXTAL/128,三個(gè)時(shí)鐘源經(jīng)過(guò)RTC_DIV產(chǎn)生SC_CLK。RTC_DIV的配置是通過(guò)RTC_PSC加載所得,RTC_PSC由寄存器RTC預(yù)分頻寄存器高位(RTC_PSCH)和RTC預(yù)分頻寄存器低位(RTC_PSCL)配置,有效位20bit,即RTC的時(shí)鐘分頻器最大值為2的20次方,完全足夠應(yīng)用了。
了解了上面的內(nèi)容,RTC的工作原理就很好理解了。舉個(gè)例子,RTC的時(shí)鐘源選擇LXTAL,頻率為32.768KHz,然后設(shè)置RTC_PSC為32768,那么SC_CLK即為1Hz,也就是說(shuō),每秒鐘RTC_CNT值+1,所以我們只要獲得一段時(shí)間內(nèi)RTC_CNT值增加了多少數(shù),也就知道經(jīng)過(guò)了多少秒。
我們從手冊(cè)中可以看到RTC_CNT由RTC計(jì)數(shù)寄存器高位(RTC_CNTH)和RTC計(jì)數(shù)寄存器低位(RTC_CNTL)設(shè)置,這兩個(gè)寄存器組合起來(lái)的有效位為32bit,即RTC_CNT可以記錄2的32次方,即4,294,967,296個(gè)數(shù),按照每秒增加一次的話,可以記錄136多年,
除了基礎(chǔ)的記時(shí)間的功能,RTC還有一個(gè)鬧鐘功能,RTC運(yùn)行時(shí),當(dāng)RTC_CNT的值增加到和RTC_ALRM(由RTC鬧鐘寄存器高位(RTC_ALRMH)和RTC鬧鐘寄存器低位(RTC_ALRML)設(shè)置)相等時(shí),則會(huì)產(chǎn)生ALRM中斷,當(dāng)然,程序中需要實(shí)現(xiàn)使能ALARM中斷(ALRMIE)。
RTC還有另外兩個(gè)中斷,一個(gè)是秒中斷,另一個(gè)是溢出中斷。秒中斷好理解,即每秒鐘進(jìn)入一個(gè)中斷;溢出中斷則是當(dāng)RTC_CNT溢出時(shí)產(chǎn)生中斷。
9.2.2RTC復(fù)位
這里把RTC的復(fù)位單獨(dú)作為一個(gè)章節(jié)來(lái)說(shuō),是因?yàn)檫@里很容易出錯(cuò)導(dǎo)致想要實(shí)現(xiàn)的功能無(wú)法實(shí)現(xiàn)。
前面說(shuō)過(guò),RTC分為兩個(gè)電源域——備份域和VDD電源域,而一般的復(fù)位比如NRST腳復(fù)位、軟件復(fù)位等只能復(fù)位VDD和VDDA電源域 ,而無(wú)法復(fù)位備份域;備份域復(fù)位需要VBAT掉電或者通過(guò)備份域控制寄存器(RCU_BDCTL)的BKPDRST來(lái)進(jìn)行備份域復(fù)位。
上節(jié)中的RTC框圖中被深色框住的即屬于備份域,這里提到一個(gè)很容易出錯(cuò)的地方,即RTC的時(shí)鐘源選擇。從框圖看到時(shí)鐘源由RTCSRC[1:0]來(lái)設(shè)置,這個(gè)位域?qū)儆趥浞萦蚩刂萍拇嫫鳎≧CU_BDCTL)。
備份域控制寄存器( RCU_BDCTL):
寄存器描述中指出,一旦RTC的時(shí)鐘源選擇后,除了將備份域復(fù)位,否則時(shí)鐘不能被改變。舉個(gè)例子:一個(gè)產(chǎn)品選擇LXTAL作為RTC時(shí)鐘,但可能因?yàn)槟承┰騆XTAL停振了,需要將時(shí)鐘源切換到IRC40K,程序如何實(shí)現(xiàn)呢?沒(méi)錯(cuò),需要復(fù)位備份域(控制BKPDRST位)才能重新選擇時(shí)鐘源,但一旦備份域進(jìn)行了復(fù)位,包括RTC_CNT等數(shù)據(jù)都會(huì)丟失,所以在備份域復(fù)位前需要對(duì)RTC內(nèi)的各個(gè)數(shù)據(jù)進(jìn)行保存處理,待備份域復(fù)位后再重新寫入。
9.2.3RTC實(shí)現(xiàn)萬(wàn)年歷
本實(shí)驗(yàn)用RTC做一個(gè)萬(wàn)年歷,其中還需要考慮到閏年閏月的情況。實(shí)驗(yàn)設(shè)置的基準(zhǔn)時(shí)間是1970年,即當(dāng)RTC_CNT為0時(shí),為1970年,實(shí)驗(yàn)最高可記錄到2106年(1970+136)。
9.3硬件設(shè)計(jì)
本實(shí)驗(yàn)實(shí)現(xiàn)每秒鐘通過(guò)串口打印實(shí)時(shí)時(shí)間,即需要使用到開(kāi)發(fā)板USB串口模塊。另外RTC時(shí)鐘源使用的是外部低速晶振,外部晶振原理圖如下:
9.4代碼解析
9.4.1RTC配置
在driver_rtc.c中定義了RTC配置函數(shù)rtc_configuration
C void rtc_configuration(void) { /*使能備份域和PMU時(shí)鐘*/ rcu_periph_clock_enable(RCU_BKPI); rcu_periph_clock_enable(RCU_PMU); /*使能備份域?qū)懝δ?/ pmu_backup_write_enable(); /*備份域復(fù)位*/ bkp_deinit(); /*依據(jù)選擇的時(shí)鐘源進(jìn)行時(shí)鐘配置*/ /*使用LXTAL*/ #ifdef RTC_CLOCK_SOURCE_LXTAL /* 使能 LXTAL */ rcu_osci_on(RCU_LXTAL); /* 等待LXTAL Ready */ rcu_osci_stab_wait(RCU_LXTAL); /*選擇LXTAL作為RTC時(shí)鐘*/ rcu_rtc_clock_config(RCU_RTCSRC_LXTAL); #endif /*使用IRC40K*/ #ifdef RTC_CLOCK_SOURCE_IRC40K /* 使能 IRC40K*/ rcu_osci_on(RCU_IRC40K); /* 等待IRC40K Ready */ rcu_osci_stab_wait(RCU_IRC40K); /*選擇IRC40K作為RTC時(shí)鐘*/ rcu_rtc_clock_config(RCU_RTCSRC_IRC40K); #endif #ifdef RTC_CLOCK_SOURCE_HXTAL_DIV_128 /* 使能LXTAL */ rcu_osci_on(RCU_HXTAL); /* 等待HXTAL Ready */ rcu_osci_stab_wait(RCU_HXTAL); /*選擇HXTAL/128作為RTC時(shí)鐘*/ rcu_rtc_clock_config(RCU_RTCSRC_HXTAL_DIV_128); #endif /*使能RTC時(shí)鐘*/ rcu_periph_clock_enable(RCU_RTC); /*等待RTC寄存器同步*/ rtc_register_sync_wait(); /*等待上一次操作完成*/ rtc_lwoff_wait(); /*使能RTC秒中斷*/ rtc_interrupt_enable(RTC_INT_SECOND); /*等待上一次操作完成*/ rtc_lwoff_wait(); /*依據(jù)選擇的時(shí)鐘源來(lái)設(shè)置分頻,使RTC周期為1s*/ #ifdef RTC_CLOCK_SOURCE_LXTAL rtc_prescaler_set(32767); #endif #ifdef RTC_CLOCK_SOURCE_IRC40K rtc_prescaler_set(40000); #endif #ifdef RTC_CLOCK_SOURCE_HXTAL_DIV_128 rtc_prescaler_set(HXTAL_VALUE/128); #endif /*等待上一次操作完成*/ rtc_lwoff_wait(); } |
時(shí)鐘源通過(guò)driver_rtc.h中的宏定義來(lái)選擇:
C #define RTC_CLOCK_SOURCE_LXTAL //#define RTC_CLOCK_SOURCE_IRC40K //#define RTC_CLOCK_SOURCE_HXTAL_DIV_128 |
9.4.2萬(wàn)年歷實(shí)現(xiàn)
在bsp_rtc.c中定義了實(shí)現(xiàn)萬(wàn)年歷的幾個(gè)函數(shù):rtc_time_set、rtc_time_display、is_leap_year等。
rtc_time_set函數(shù)——第一次需要手動(dòng)設(shè)置當(dāng)前時(shí)間:
C uint32_t rtc_time_set(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { uint16_t t; uint32_t seccount = 0; if (bkp_read_data(BKP_DATA_0) != 0xA5A5) { rtc_configuration(); if(year < 1970 || year > 2099) return 1; for(t = 1970; t < year; t++){ if(is_leap_year(t)){ seccount += 31622400; }else{ seccount += 31536000; } } month -= 1; for(t=0; t < month; t++){ seccount += (uint32_t)month_table[t] * 86400; if(is_leap_year(year) && t==1){ seccount+=86400; } } seccount += (uint32_t)(day-1) * 86400; seccount += (uint32_t)hour * 3600; seccount += (uint32_t)minute * 60; seccount += second; rtc_counter_set(seccount); bkp_write_data(BKP_DATA_0, 0xA5A5); return 0; } else { if (rcu_flag_get(RCU_FLAG_PORRST) != RESET){ printf("Power On Reset occurred....\r\n"); }else if (rcu_flag_get(RCU_FLAG_EPRST) != RESET){ printf("External Reset occurred....\r\n"); } rcu_all_reset_flag_clear(); rcu_periph_clock_enable(RCU_PMU); pmu_backup_write_enable(); rtc_register_sync_wait(); rtc_interrupt_enable(RTC_INT_SECOND); rtc_lwoff_wait(); return 0; } } |
該函數(shù)中在第一次上電運(yùn)行時(shí),會(huì)進(jìn)行初始時(shí)間設(shè)置,然后寫入特定數(shù)據(jù)到備份域數(shù)據(jù)寄存器中。當(dāng)發(fā)生系統(tǒng)復(fù)位但Vbat未掉電的情況下,則不會(huì)重新進(jìn)行時(shí)間設(shè)置,但需要重新開(kāi)啟秒時(shí)鐘中斷,因?yàn)镾CIE處于VDD電源域而不在備份域。
rtc_time_display函數(shù)——打印實(shí)時(shí)時(shí)間:
C void rtc_time_display(uint32_t timevar) { static uint16_t daycnt = 0; uint32_t temp = 0; uint16_t temp1 = 0; temp = timevar / 86400; if(daycnt != temp) { daycnt = temp; temp1 = 1970; while(temp >= 365){ if(is_leap_year(temp1)){ if(temp >= 366) temp-=366; else break; }else temp -= 365; temp1++; } calendar.years = temp1; temp1=0; while(temp >= 28) { if(is_leap_year(calendar.years) && temp1 == 1){ if(temp >= 29) temp -= 29; else break; }else{ if(temp >= month_table[temp1]) temp -= month_table[temp1]; else break; } temp1++; } calendar.months = temp1 + 1; calendar.days = temp + 1; } temp = timevar % 86400; calendar.hours = temp / 3600; calendar.minutes = (temp % 3600) / 60; calendar.seconds = (temp % 3600) % 60; printf("Time: %0.4d-%0.2d-%0.2d,%0.2d:%0.2d:%0.2d\r\n", calendar.years, calendar.months, calendar.days, calendar.hours, calendar.minutes, calendar.seconds); } |
is_leap_year函數(shù)——判斷當(dāng)前年是否為閏年:
C uint8_t is_leap_year(uint16_t year) { if((year%4 == 0 && year % 100 != 0) || (year % 400 == 0)){ return 1; }else{ return 0; } } |
9.4.3main函數(shù)和中斷函數(shù)實(shí)現(xiàn)
以下為main函數(shù)代碼:
C int main(void) { delay_init();//delay函數(shù)初始化 bsp_uart_init(&BOARD_UART);//BOARD_UART串口初始化 nvic_irq_enable(RTC_IRQn,1,0);//打開(kāi)RTC的NVIC rtc_time_set(year_set,month_set,day_set,hour_set,minute_set,second_set); //設(shè)置當(dāng)前時(shí)間 while (1){ /* 判斷是否到1s了*/ if (timedisplay == 1){ /* 顯示實(shí)時(shí)時(shí)間*/ rtc_time_display(rtc_counter_get()); timedisplay = 0; } } } |
本例程main函數(shù)首先進(jìn)行了延時(shí)函數(shù)初始化,再初始化開(kāi)發(fā)板USB串口,開(kāi)啟RTC的NVIC后設(shè)置了當(dāng)前時(shí)間,在while(1)循環(huán)中等待RTC數(shù)據(jù)更新,然后將實(shí)時(shí)時(shí)間打印出來(lái)。
中斷函數(shù)代碼:
C void RTC_IRQHandler(void) { if (rtc_flag_get(RTC_FLAG_SECOND) != RESET){ /* 清除RTC秒中斷標(biāo)志位*/ rtc_flag_clear(RTC_FLAG_SECOND); timedisplay = 1; /* 等待上一次操作完成 */ rtc_lwoff_wait(); } } |
9.5實(shí)驗(yàn)結(jié)果
為了驗(yàn)證萬(wàn)年歷是否工作正常,設(shè)定初始時(shí)間為2022年12月31日23時(shí)59分50s,當(dāng)程序開(kāi)始運(yùn)行時(shí),每秒鐘打印時(shí)間,經(jīng)過(guò)10s后,可以看到時(shí)間變?yōu)?023年1月1日0時(shí)0分0秒,說(shuō)明萬(wàn)年歷有效。
本教程由GD32 MCU方案商聚沃科技原創(chuàng)發(fā)布,了解更多GD32 MCU教程,關(guān)注聚沃科技官網(wǎng)
-
單片機(jī)
+關(guān)注
關(guān)注
6037文章
44558瀏覽量
635354 -
開(kāi)發(fā)板
+關(guān)注
關(guān)注
25文章
5050瀏覽量
97483 -
萬(wàn)年歷
+關(guān)注
關(guān)注
3文章
186瀏覽量
23926 -
RTC
+關(guān)注
關(guān)注
2文章
538瀏覽量
66542
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論