NRF52832學習筆記(8)——RTC實時時鐘使用

一、簡介

NRF52832 中的 RTCReal-time Counter 實時計數器,而不是 Real-time Clock 實時時間 。所以爲了實現實時時鐘,需要創建一個1秒定時器增加時間戳的值。

1.1 選用RTC2

實現萬年曆功能有三種方式:

  1. 新建一個1秒定時的APP_TIMER。
  • 優點:創建方便。
  • 缺點:實時性太差。
    APP_TIMER採用輪詢執行而非搶佔的方式,假如其它APP_TIMER耗時較長,例如有一個APP_TIMER在採集心率,萬年曆的APP_TIMER就必須等採集心率完成才能執行。
  1. 在APP_TIMER的中斷中插入計算時間戳的代碼
  • 優點:修改代碼較少。
  • 缺點:代碼耦合性高,易出現未知問題。
    我們都知道APP_TIMER的實現就是使用了RTC1,那麼我們就可以利用上RTC1的特點,適當修改代碼來實現我們的需求,但這樣容易造成代碼耦合性高,不易維護和管理。
  1. 用一個新的RTC來實現。
  • 優點:不會影響原來APP_TIMER的功能,模塊獨立,方便管理。
  • 缺點:需要深入瞭解芯片的RTC,根據RTC特性來實現自己的功能。

綜上所述,採用了第三種方案來實現。用 RTC2 來實現。

1.2 RTC內部結構

RTC 模塊具有一個 24 位計數器、12 位預分頻器、捕獲/比較寄存器和一個滴答事件生成器用於低功耗、無滴答 RTOS 的實現。RTC 模塊需要低頻時鐘源提供運行時鐘,因此在使用 RTC 之前,軟件必須明確啓動 LFCLK 時鐘。

1.3 RTC時鐘源

實時計數器 RTC 運行於 LFCLK 下。COUNTER 的分辨率爲30.517us。當 HFCLK 關閉和 PCLK16M 也不用時,RTC 也可以運行。
因爲在協議棧工程也是用到 LFCLK 時鐘,所以在 ble_stack_init 函數中 LFCLK 已經被使能了。

1.4 頻率計算方式


如實現8HZ的頻率,則PRESCALER 寄存器應該設爲
32768/8-1 = 4095

1.5 RTC中斷事件

  • TICK 滴答中斷 ,可選擇爲 RTOS 提供常規中斷源,而無需使用 ARM SysTick 功能。使用 RTC TICK 事件而不是 SysTick 可以在關閉 CPU 的同時保持 RTOS 調度處於活動狀態。在每個時鐘滴答(即COUNTER計數一次)都會產生這個事件,COUNTER的技術值就會加1。
  • Overflow 溢出中斷 ,在 COUNTER 溢出時產生。
  • COMPARE 比較中斷,COUNTER 計數值與 cc[0-3] 中的值相等時產生, COMPARE 的一些 task, 如 clear,stop,start 存在 us 級和 ns 級的延遲,使用 RTC 來計時應該考慮這些可能的延遲。

綜合考慮,採用 TICK 中斷來實現。
由圖可知當 PRESCALER 設爲1時,則 TICK 中斷頻率爲 f= (32768/(1+1))
我們要實現 8Hz,則 PRESCALER = 32768/8-1 = 4095;PRESCALER 寄存器只有 12bit,2^12-1 = 4095,所以最大也只有 8Hz 了。

二、移植文件

注意:以下出現缺失common.h文件錯誤,去除即可。uint8改爲uint8_t或unsigned char或自己宏定義
鏈接:https://pan.baidu.com/s/1AGAImL-ydFAJmjuXAgw_HA 提取碼:o45p
board_rtc.cboard_rtc.h 兩個文件加入工程的Application文件夾下

2.1 board_rtc.c

/*********************************************************************
 * INCLUDES
 */
#include <time.h>
#include "nrf_drv_rtc.h"
#include "nrf_drv_clock.h"
#include "nrfx_rtc.h"

#include "board_rtc.h"
#include "common.h"

static void rtcCallbackFunc(nrf_drv_rtc_int_type_t interruptType);

/*********************************************************************
 * GLOBAL VARIABLES
 */
volatile uint32 g_timestamp = 1412800000;								// 時間戳

/*********************************************************************
 * LOCAL VARIABLES
 */
static const nrf_drv_rtc_t s_rtcHandle = NRF_DRV_RTC_INSTANCE(2);		// Declaring an instance of nrf_drv_rtc for RTC2.
static uint8 s_timeCount1second = 0;

/*********************************************************************
 * PUBLIC FUNCTIONS
 */
/**
 @brief RTC時間的初始化函數
 @param 無
 @return 無
*/
void RTC_Init(void)
{
    ret_code_t errCode;
        
    nrf_drv_rtc_config_t rtcConfig = NRF_DRV_RTC_DEFAULT_CONFIG;		//Initialize RTC instance
    rtcConfig.prescaler = 4095; 										// 如實現8HZ的頻率,則PRESCALER寄存器應該設爲32768/8-1 = 4095
    
	errCode = nrf_drv_rtc_init(&s_rtcHandle, &rtcConfig, rtcCallbackFunc);
    APP_ERROR_CHECK(errCode);

    nrf_drv_rtc_tick_enable(&s_rtcHandle, true);						// Enable tick event & interrupt 
    nrf_drv_rtc_enable(&s_rtcHandle);									// Power on RTC instance
}

/**
 @brief 設置RTC時間
 @param timestampNow -[in] 當前時間戳
 @return 無
*/
void RTC_SetTime(uint32 timestampNow)
{
	g_timestamp = timestampNow;
}

/**
 @brief 獲取RTC時間
 @param 無
 @return 當前時間戳
*/
uint32 RTC_GetTime(void)
{
    return g_timestamp;
}


/*********************************************************************
 * LOCAL FUNCTIONS
 */
/**
 @brief RTC計數回調函數
 @param interruptType - [in] 中斷類型
 @return 無
*/
static void rtcCallbackFunc(nrf_drv_rtc_int_type_t interruptType)
{
    if(interruptType == NRF_DRV_RTC_INT_COMPARE0)						// 中斷類型:比較中斷
    {
    }
    else if(interruptType == NRF_DRV_RTC_INT_TICK)						// 中斷類型:滴答中斷
    {
        if(s_timeCount1second >= 7)										// 125ms * 8 = 1s 
        {
            s_timeCount1second = 0;
            g_timestamp++;		
        }
        else
        {
            s_timeCount1second++;
        }
    }
}

/****************************************************END OF FILE****************************************************/

2.2 board_rtc.h

#ifndef _BOARD_RTC_H_
#define _BOARD_RTC_H_

/*********************************************************************
 * INCLUDES
 */
#include "common.h"

/*********************************************************************
 * GLOBAL VARIABLES
 */
extern volatile uint32 g_timestamp;

/*********************************************************************
 * API FUNCTIONS
 */
void RTC_Init(void);
void RTC_SetTime(uint32 timestampNow);
uint32 RTC_GetTime(void);

#endif /* _BOARD_RTC_H_ */

三、API調用

需包含頭文件 board_rtc.h

RTC_Init

功能 初始化RTC實時時鐘驅動
函數定義 void RTC_Init(void)
參數
返回

RTC_SetTime

功能 設置RTC時間戳
函數定義 void RTC_SetTime(uint32 timestampNow)
參數 timestampNow:時間戳
返回

RTC_GetTime

功能 獲取RTC時間戳
函數定義 uint32 RTC_GetTime(void)
參數
返回 當前時間戳

四、SDK配置

點擊 sdk_config.h 文件

選擇 Configuration Wizard

nRF_Drivers 中勾選RTC2相關選項

五、使用例子

1)添加頭文件

#include "board_rtc.h"

2)添加初始化代碼(SDK15.3 中 ble_peripheral 的 ble_app_template 工程 main() 函數中)
加入 RTC_Init() 並在初始化後調用 RTC_SetTime 配置時間,在需要用RTC時間時調用 RTC_GetTime 獲取時間戳

int main(void)
{
	bool erase_bonds;

    /*-------------------------- 外設驅動初始化 ---------------------------*/
	// Initialize.
    log_init();																	// 日誌驅動初始化																	
    timers_init();																// 定時器驅動初始化(在此加入自定義定時器)
	RTC_Init();																// RTC驅動初始化
	
	/*-------------------------- 藍牙協議棧初始化 ---------------------------*/
    power_management_init();
    ble_stack_init();															// 協議棧初始化
    gap_params_init();
    gatt_init();
    advertising_init();															// 廣播初始化
    services_init();															// 服務初始化
    conn_params_init();															// 連接參數初始化
    peer_manager_init();
	
	/*-------------------------- 開啓應用 ---------------------------*/
	// Start execution.
    NRF_LOG_INFO("Template example started."); 
    advertising_start(erase_bonds);												// 開啓廣播	
	application_timers_start();													// 定時器應用開啓(在此開啓自定義定時器)	
	RTC_SetTime(g_timestamp);                                                   // 設置RTC時間
	
    // Enter main loop.
    for(;;)
    {
        idle_state_handle();
    }
}

3)獲取當前幾點

uint32 GetHourNow(void)
{
	struct tm *pLocaclTime;
	pLocaclTime = localtime((time_t*)&g_timestamp);

    return pLocaclTime->tm_hour;
}

4)tm時間結構體

struct tm 
{
    int tm_sec;      /* seconds after the minute   - [0,59]  */
    int tm_min;      /* minutes after the hour     - [0,59]  */
    int tm_hour;     /* hours after the midnight   - [0,23]  */
    int tm_mday;     /* day of the month           - [1,31]  */
    int tm_mon;      /* months since January       - [0,11]  */
    int tm_year;     /* years since 1900                     */
    int tm_wday;     /* days since Sunday          - [0,6]   */
    int tm_yday;     /* days since Jan 1st         - [0,365] */
    int tm_isdst;    /* Daylight Savings Time flag           */
};

• 由 Leung 寫於 2020 年 1 月 8 日

• 參考:青風電子社區
    Nordic系列芯片講解七 (Nordic 52832 RTC 實現萬年曆功能)

發佈了89 篇原創文章 · 獲贊 134 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章