RTC实时时钟实验(低功耗、纽扣电池供电)
目錄
- I.MX6U RTC 簡介
- 硬件原理分析
- 實(shí)驗(yàn)程序編寫
- 修改文件MCIMX6Y2.h
- 編寫實(shí)驗(yàn)程序
- 編譯下載驗(yàn)證
- 編寫Makefile 和鏈接腳本
- 編譯下載
實(shí)時時鐘是很常用的一個外設(shè),通過實(shí)時時鐘我們就可以知道年、月、日和時間等信息。因此在需要記錄時間的場合就需要實(shí)時時鐘,可以使用專用的實(shí)時時鐘芯片來完成此功能,但是現(xiàn)在大多數(shù)的MCU 或者M(jìn)PU 內(nèi)部就已經(jīng)自帶了實(shí)時時鐘外設(shè)模塊。比如I.MX6U 內(nèi)部的SNVS 就提供了RTC 功能,本章我們就學(xué)習(xí)如何使用I.MX6U 內(nèi)部的RTC 來完成實(shí)時時鐘功能。
I.MX6U RTC 簡介
如果學(xué)習(xí)過STM32 的話應(yīng)該知道,STM32 內(nèi)部有一個RTC 外設(shè)模塊,這個模塊需要一個32.768KHz 的晶振,對這個RTC 模塊進(jìn)行初始化就可以得到一個實(shí)時時鐘。I.MX6U 內(nèi)部也有個RTC 模塊,但是不叫作“RTC”,而是叫做“SNVS”,這一點(diǎn)要注意!本章我們參考《I.MX6UL參考手冊》,而不是《I.MX6ULL 參考手冊》,因?yàn)椤禝.MX6ULL 參考手冊》很多SNVS 相關(guān)的
寄存器并沒有給出來,不知道是為何?但是《I.MX6UL 參考手冊》里面是完整的。所以本章我們使用《I.MX6UL 參考手冊》,如果直接在《I.MX6UL 參考手冊》的書簽里面找“RTC”相關(guān)的字眼是找不到的。I.MX6U 系列的RTC 是在SNVS 里面,也就是《I.MX6UL 參考手冊》的第46 章“Chapter 46 Secure Non-Volatile Storage(SNVS)”。
SNVS 直譯過來就是安全的非易性存儲,SNVS 里面主要是一些低功耗的外設(shè),包括一個安全的實(shí)時計(jì)數(shù)器(RTC)、一個單調(diào)計(jì)數(shù)器(monotonic counter)和一些通用的寄存器,本章我們肯定只使用實(shí)時計(jì)數(shù)器(RTC)。SNVS 里面的外設(shè)在芯片掉電以后由電池供電繼續(xù)運(yùn)行,I.MX6U-ALPHA 開發(fā)板上有一個紐扣電池,這個紐扣電池就是在主電源關(guān)閉以后為SNVS 供電的,如圖25.1.1 所示:
因?yàn)榧~扣電池在掉電以后會繼續(xù)給SNVS 供電,因此實(shí)時計(jì)數(shù)器就會一直運(yùn)行,這樣的話時間信息就不會丟失,除非紐扣電池沒電了。在有紐扣電池作為后備電源的情況下,不管系統(tǒng)主電源是否斷電,SNVS 都正常運(yùn)行。SNVS 有兩部分:SNVS_HP 和SNVS_LP,系統(tǒng)主電源斷電以后SNVS_HP 也會斷電,但是在后備電源支持下,SNVS_LP 是不會斷電的,而且SNVS_LP是和芯片復(fù)位隔離開的,因此SNVS_LP 相關(guān)的寄存器的值會一直保存著。
SNVS 分為兩個子模塊:SNVS_HP 和SNVS_LP,也就是高功耗域(SNVS_HP)和低功耗域(SNVS_LP),這兩個域的電源來源如下:
SNVS_LP:專用的always-powered-on 電源域,系統(tǒng)主電源和備用電源都可以為其供電。
SNVS_HP:系統(tǒng)(芯片)電源。
SNVS 的這兩個子模塊的電源如圖25.1.2 所示:
圖25.1.2 中各個部分功能如下:
①、VDD_HIGH_IN 是系統(tǒng)(芯片)主電源,這個電源會同時供給給SNVS_HP 和SNVS_LP。
②、VDD_SNVS_IN 是紐扣電池供電的電源,這個電源只會供給給SNVS_LP,保證在系
統(tǒng)主電源VDD_HIGH_IN 掉電以后SNVS_LP 會繼續(xù)運(yùn)行。
③、SNVS_HP 部分。
④、SNVS_LP 部分,此部分有個SRTC,這個就是我們本章要使用的RTC。
其實(shí)不管是SNVS_HP 還是SNVS_LP,其內(nèi)部都有一個SRTC,但是因?yàn)镾NVS_HP 在系統(tǒng)電源掉電以后就會關(guān)閉,所以我們本章使用的是SNVS_LP 內(nèi)部的SRTC。畢竟我們肯定都不想開發(fā)板或者設(shè)備每次關(guān)閉以后時鐘都被清零,然后開機(jī)以后先設(shè)置時鐘。
其實(shí)不管是SNVS_HP 里面的RTC,還是SNVS_LP 里面的SRTC,其本質(zhì)就是一個定時器,和我們在第八章講的EPIT 定時器一樣,只要給它提供時鐘,它就會一直運(yùn)行。SRTC 需要外界提供一個32.768KHz 的時鐘,I.MX6U-ALPHA 核心板上的32.768KHz 的晶振就是提供這個時鐘的。寄存器SNVS_LPSRTCMR 和SNVS_LPSRTCLR 保存著秒數(shù),直接讀取這兩個寄存器的值就知道過了多長時間了。一般以1970 年1 月1 日為起點(diǎn),加上經(jīng)過的秒數(shù)即可得到現(xiàn)在的時間和日期,原理還是很簡單的。SRTC 也是帶有鬧鐘功能的,可以在寄存器SNVS_LPAR 中寫入鬧鐘時間值,當(dāng)時鐘值和鬧鐘值匹配的時候就會產(chǎn)生鬧鐘中斷,要使用時鐘功能的話還需要進(jìn)行一些設(shè)置,本章我們就不使用鬧鐘了。
接下來我們看一下本章要用到的與SRTC 相關(guān)的部分寄存器,首先是SNVS_HPCOMR 寄存器,這個寄存器我們只用到了位:NPSWA_EN(bit31),這個位是非特權(quán)軟件訪問控制位,如果非特權(quán)軟件要訪問SNVS 的話此位必須為1。
接下來看一下寄存器SNVS_LPCR寄存器,此寄存器也只用到了一個位:SRTC_ENV(bit0),此位為1 的話就使能STC 計(jì)數(shù)器。
最后來看一下寄存器SNVS_SRTCMR 和SNVS_SRTCLR,這兩個寄存器保存著RTC 的秒數(shù),按照NXP 官方的《6UL 參考手冊》中的說法,SNVS_SRTCMR 保存著高15 位,SNVS_SRTCLR保存著低32 位,因此SRTC 的計(jì)數(shù)器一共是47 位。
但是!我在編寫驅(qū)動的時候發(fā)現(xiàn)按照手冊上說的去讀取計(jì)數(shù)器值是錯誤的!具體表現(xiàn)就是時間是混亂的,因此我在查找了NXP 提供的SDK 包中的fsl_snvs_hp.c 以及Linux 內(nèi)核中的rtc-snvs.c 這兩個驅(qū)動文件以后發(fā)現(xiàn)《6UL 參考手冊》上對SNVS_SRTCMR 和SNVS_SRTCLR 的
解釋是錯誤的,經(jīng)過查閱這兩個文件,得到如下結(jié)論:
①、SRTC計(jì)數(shù)器是32位的,不是47位!
②、SNVS_SRTCMR的bit14:0這15位是SRTC計(jì)數(shù)器的高15位。
③、SNVS_SRTCLR的bit31:bit15這17位是SRTC計(jì)數(shù)器的低17位。
按照上面的解釋去讀取這兩個寄存器就可以得到正確的時間,如果要調(diào)整時間的話也是向這兩個寄存器寫入要設(shè)置的時間值對應(yīng)的秒數(shù)就可以了,但是要修改這兩個寄存器的話要先關(guān)閉SRTC。
關(guān)于SNVS 中和RTC 有關(guān)的寄存器就介紹到這里,關(guān)于這些寄存器詳細(xì)的描述,請參考《I.MX6UL 參考手冊》第2931 頁的46.7 小節(jié)。本章我們使用I.MX6U 的SNVS_LP 的SRTC,配置步驟如下:
1、初始化SNVS_SRTC
初始化SNVS_LP 中的SRTC。
2、設(shè)置RTC 時間
第一次使用RTC 肯定要先設(shè)置時間。
3、使能RTC
配置好RTC 并設(shè)置好初始時間以后就可以開啟RTC 了。
硬件原理分析
本試驗(yàn)用到的資源如下:
①、指示燈LED0。
②、RGB LCD 接口。
③、SRTC。
SRTC 需要外接一個32.768KHz 的晶振,在I.MX6U-ALPHA 核心板上就有這個32.768KHz的晶振,原理圖如圖25.2.1 所示:
實(shí)驗(yàn)程序編寫
本實(shí)驗(yàn)對應(yīng)的例程路徑為:開發(fā)板光盤-> 1、裸機(jī)例程-> 16_rtc。
修改文件MCIMX6Y2.h
在第十三章移植的NXP 官方SDK 包是針對I.MX6ULL 編寫的,因此文件MCIMX6Y2.h中的結(jié)構(gòu)體SNVS_Type 里面的寄存器是不全的,我們需要在其中加入本章實(shí)驗(yàn)所需要的寄存器,修改SNVS_Type 為如下所示:
1 typedef struct { 2 __IO uint32_t HPLR; 3 __IO uint32_t HPCOMR; 4 __IO uint32_t HPCR; 5 __IO uint32_t HPSICR; 6 __IO uint32_t HPSVCR; 7 __IO uint32_t HPSR; 8 __IO uint32_t HPSVSR; 9 __IO uint32_t HPHACIVR; 10 __IO uint32_t HPHACR; 11 __IO uint32_t HPRTCMR; 12 __IO uint32_t HPRTCLR; 13 __IO uint32_t HPTAMR; 14 __IO uint32_t HPTALR; 15 __IO uint32_t LPLR; 16 __IO uint32_t LPCR; 17 __IO uint32_t LPMKCR; 18 __IO uint32_t LPSVCR; 19 __IO uint32_t LPTGFCR; 20 __IO uint32_t LPTDCR; 21 __IO uint32_t LPSR; 22 __IO uint32_t LPSRTCMR; 23 __IO uint32_t LPSRTCLR; 24 __IO uint32_t LPTAR; 25 __IO uint32_t LPSMCMR; 26 __IO uint32_t LPSMCLR; 27 }SNVS_Type;編寫實(shí)驗(yàn)程序
本章實(shí)驗(yàn)在上一章例程的基礎(chǔ)上完成,更改工程名字為“rtc”,然后在bsp 文件夾下創(chuàng)建名為“rtc”的文件夾,然后在bsp/rtc 中新建bsp_rtc.c 和bsp_rtc.h 這兩個文件。在bsp_rtc.h 中輸入如下內(nèi)容:
1 #ifndef _BSP_RTC_H 2 #define _BSP_RTC_H 3 /*************************************************************** 4 Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 5 文件名: bsp_rtc.h 6 作者: 左忠凱 7 版本: V1.0 8 描述: RTC驅(qū)動頭文件。 9 其他: 無 10 論壇: www.openedv.com 11 日志: 初版V1.0 2019/1/3 左忠凱創(chuàng)建 12 ***************************************************************/ 13 #include "imx6ul.h" 14 15 /* 相關(guān)宏定義*/ 16 #define SECONDS_IN_A_DAY (86400) /* 一天86400秒*/ 17 #define SECONDS_IN_A_HOUR (3600) /* 一個小時3600秒*/ 18 #define SECONDS_IN_A_MINUTE (60) /* 一分鐘60秒*/ 19 #define DAYS_IN_A_YEAR (365) /* 一年365天*/ 20 #define YEAR_RANGE_START (1970) /* 開始年份1970年*/ 21 #define YEAR_RANGE_END (2099) /* 結(jié)束年份2099年*/ 22 23 /* 時間日期結(jié)構(gòu)體*/ 24 struct rtc_datetime 25 { 26 unsigned short year; /* 范圍為:1970 ~ 2099 */ 27 unsigned char month; /* 范圍為:1 ~ 12 */ 28 unsigned char day; /* 范圍為:1 ~ 31 (不同的月,天數(shù)不同).*/ 29 unsigned char hour; /* 范圍為:0 ~ 23 */ 30 unsigned char minute; /* 范圍為:0 ~ 59 */ 31 unsigned char second; /* 范圍為:0 ~ 59 */ 32 }; 33 34 /* 函數(shù)聲明*/ 35 void rtc_init(void); 36 void rtc_enable(void); 37 void rtc_disable(void); 38 unsigned int rtc_coverdate_to_seconds(struct rtc_datetime *datetime); 39 unsigned int rtc_getseconds(void); 40 void rtc_setdatetime(struct rtc_datetime *datetime); 41 void rtc_getdatetime(struct rtc_datetime *datetime); 42 43 #endif第16 到21 行定義了一些宏,比如一天多少秒、一小時多少秒等等,這些宏將用于將秒轉(zhuǎn)換為時間,或者將時間轉(zhuǎn)換為秒。第24 行定義了一個結(jié)構(gòu)體rtc_datetime,此結(jié)構(gòu)體用于描述日期和時間參數(shù)。剩下的就是一些函數(shù)聲明了,很簡單。
在文件bsp_rtc.c 中輸入如下內(nèi)容:
/*************************************************************** Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 文件名: bsp_rtc.c 作者: 左忠凱 版本: V1.0 描述: RTC驅(qū)動文件。 其他: 無 論壇: www.openedv.com 日志: 初版V1.0 2019/1/3 左忠凱創(chuàng)建 ***************************************************************/ 1 #include "bsp_rtc.h" 2 #include "stdio.h" 3 4 /* 5 * @description :初始化RTC 6 */ 7 void rtc_init(void) 8 { 9 /* 10 * 設(shè)置HPCOMR寄存器 11 * bit[31] 1 : 允許訪問SNVS寄存器,一定要置1 12 */ 13 SNVS->HPCOMR |= (1 << 31); 14 15 #if 0 16 struct rtc_datetime rtcdate; 17 18 rtcdate.year = 2018U; 19 rtcdate.month = 12U; 20 rtcdate.day = 13U; 21 rtcdate.hour = 14U; 22 rtcdate.minute = 52; 23 rtcdate.second = 0; 24 rtc_setDatetime(&rtcdate); /* 初始化時間和日期*/ 25 #endif 26 rtc_enable(); /* 使能RTC */ 27 } 28 29 /* 30 * @description : 開啟RTC 31 */文件bsp_rtc.c 里面一共有9 個函數(shù),依次來看一下這些函數(shù)的意義。函數(shù)rtc_init 明顯是初始化rtc 的,主要是使能RTC,也可以在rtc_init 函數(shù)里面設(shè)置時間。函數(shù)rtc_enable 和rtc_disable分別是RTC 的使能和禁止函數(shù)。函數(shù)rtc_isleapyear 用于判斷某一年是否為閏年。函數(shù)rtc_coverdate_to_seconds 負(fù)責(zé)將給定的日期和時間信息轉(zhuǎn)換為對應(yīng)的秒數(shù)。函數(shù)rtc_setdatetime
用于設(shè)置時間,也就是設(shè)置寄存器SNVS_LPSRTCMR 和SNVS_LPSRTCLR 。函數(shù)rtc_convertseconds_to_datetime 用于將給定的秒數(shù)轉(zhuǎn)換為對應(yīng)的時間值。函數(shù)rtc_getseconds 獲取SRTC 當(dāng)前秒數(shù),其實(shí)就是讀取寄存器SNVS_LPSRTCMR 和SNVS_LPSRTCLR,然后將其結(jié)合成32 位的值。最后一個函數(shù)rtc_getdatetime 是獲取時間值。
我們在main 函數(shù)里面先初始化RTC,然后進(jìn)入3S 倒計(jì)時,如果這3S 內(nèi)按下了KEY0 按鍵,那么就設(shè)置SRTC 的日期。如果3S 倒計(jì)時結(jié)束以后沒有按下KEY0,也就是沒有設(shè)置SRTC時間的話就進(jìn)入while 循環(huán),然后讀取RTC 的時間值并且顯示在LCD 上,在文件main.c 中輸入如下所示內(nèi)容:
1 #include "bsp_clk.h" 2 #include "bsp_delay.h" 3 #include "bsp_led.h" 4 #include "bsp_beep.h" 5 #include "bsp_key.h" 6 #include "bsp_int.h" 7 #include "bsp_uart.h" 8 #include "bsp_lcd.h" 9 #include "bsp_lcdapi.h" 10 #include "bsp_rtc.h" 11 #include "stdio.h" 12 13 /* 14 * @description : main函數(shù) 15 * @param : 無 16 * @return : 無 17 */ 18 int main(void) 19 { 20 unsigned char key = 0; 21 int t = 0; 22 int i = 3; /* 倒計(jì)時3S */ 23 char buf[160]; 24 struct rtc_datetime rtcdate; 25 unsigned char state = OFF; 26 27 int_init(); /* 初始化中斷(一定要最先調(diào)用!) */ 28 imx6u_clkinit(); /* 初始化系統(tǒng)時鐘*/ 29 delay_init(); /* 初始化延時*/ 30 clk_enable(); /* 使能所有的時鐘*/ 31 led_init(); /* 初始化led */ 32 beep_init(); /* 初始化beep */ 33 uart_init(); /* 初始化串口,波特率115200 */ 34 lcd_init(); /* 初始化LCD */ 35 rtc_init(); /* 初始化RTC */ 36 37 tftlcd_dev.forecolor = LCD_RED; 38 lcd_show_string(50, 10, 400, 24, 24, /* 顯示字符串*/ (char*)"ALPHA-IMX6UL RTC TEST"); 39 tftlcd_dev.forecolor = LCD_BLUE; 40 memset(buf, 0, sizeof(buf)); 41 42 while(1) 43 { 44 if(t==100) /* 1s時間到了*/ 45 { 46 t=0; 47 printf("will be running %d s......\r", i); 48 49 lcd_fill(50, 40,370, 70, tftlcd_dev.backcolor); /* 清屏*/ 50 sprintf(buf, "will be running %ds......", i); 51 lcd_show_string(50, 40, 300, 24, 24, buf); 52 i--;第35 行調(diào)用函數(shù)rtc_init 初始化RTC。
第42 到73 行是倒計(jì)時3S,如果在這3S 內(nèi)按下了KEY0 按鍵就會調(diào)用函數(shù)rtc_setdatetime設(shè)置當(dāng)前的時間。如果3S 倒計(jì)時結(jié)束以后沒有按下KEY0 那就表示不需要設(shè)置時間,跳出循環(huán),執(zhí)行下面的代碼。
第79 到89 行就是主循環(huán),此循環(huán)每隔1S 調(diào)用函數(shù)rtc_getdatetime 獲取一次時間值,并且通過串口打印給SecureCRT 或者在LCD 上顯示。
編譯下載驗(yàn)證
編寫Makefile 和鏈接腳本
修改Makefile 中的TARGET 為rtc,然后在在INCDIRS 和SRCDIRS 中加入“bsp/rtc”,修改后的Makefile 如下:
1 CROSS_COMPILE ?= arm-linux-gnueabihf- 2 TARGET ?= rtc 3 4 /* 省略掉其它代碼...... */ 5 6 INCDIRS := imx6ul \ 7 stdio/include \ 8 bsp/clk \ 9 bsp/led \ 10 bsp/delay \ 11 bsp/beep \ 12 bsp/gpio \ 13 bsp/key \ 14 bsp/exit \ 15 bsp/int \ 16 bsp/epittimer \ 17 bsp/keyfilter \ 18 bsp/uart \ 19 bsp/lcd \ 20 bsp/rtc 21 22 SRCDIRS := project \ 23 stdio/lib \ 24 bsp/clk \ 25 bsp/led \ 26 bsp/delay \ 27 bsp/beep \ 28 bsp/gpio \ 29 bsp/key \ 30 bsp/exit \ 31 bsp/int \ 32 bsp/epittimer \ 33 bsp/keyfilter \ 34 bsp/uart \ 35 bsp/lcd \ 36 bsp/rtc 37 38 /* 省略掉其它代碼...... */ 39 40 clean: 41 rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)第2 行修改變量TARGET 為“rtc”,也就是目標(biāo)名稱為“rtc”。
第20 行在變量INCDIRS 中添加RTC 驅(qū)動頭文件(.h)路徑。
第36 行在變量SRCDIRS 中添加RTC 驅(qū)動驅(qū)動文件(.c)路徑。
鏈接腳本保持不變。
編譯下載
使用Make 命令編譯代碼,編譯成功以后使用軟件imxdownload 將編譯完成的rtc.bin 文件下載到SD 卡中,命令如下:
chmod 777 imxdownload //給予imxdownload 可執(zhí)行權(quán)限,一次即可 ./imxdownload rtc.bin /dev/sdd //燒寫到SD 卡中,不能燒寫到/dev/sda 或sda1 設(shè)備里面!燒寫成功以后將SD 卡插到開發(fā)板的SD 卡槽中,然后復(fù)位開發(fā)板。程序一開始進(jìn)入3S倒計(jì)時,如圖25.4.2.1 所示:
如果在倒計(jì)數(shù)結(jié)束之前按下KEY0,那么RTC 就會被設(shè)置為我們代碼中設(shè)置的時間和日期值,RTC 運(yùn)行如圖24.4.2.2 所示:
我們在main 函數(shù)中設(shè)置的時間是2018 年1 月15 日,16 點(diǎn)23 分0 秒,在倒計(jì)數(shù)結(jié)束之前按下KEY0 按鍵設(shè)置RTC,圖24.4.2.2 中的時間就是我們設(shè)置以后的時間。
總結(jié)
以上是生活随笔為你收集整理的RTC实时时钟实验(低功耗、纽扣电池供电)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win10系统中英文切换
- 下一篇: 多点电容触摸屏实验