一、簡介
NRF52832 中的 RTC 是 Real-time Counter 實時計數器,而不是 Real-time Clock 實時時間 。所以爲了實現實時時鐘,需要創建一個1秒定時器增加時間戳的值。
1.1 選用RTC2
實現萬年曆功能有三種方式:
- 新建一個1秒定時的APP_TIMER。
- 優點:創建方便。
- 缺點:實時性太差。
APP_TIMER採用輪詢執行而非搶佔的方式,假如其它APP_TIMER耗時較長,例如有一個APP_TIMER在採集心率,萬年曆的APP_TIMER就必須等採集心率完成才能執行。
- 在APP_TIMER的中斷中插入計算時間戳的代碼
- 優點:修改代碼較少。
- 缺點:代碼耦合性高,易出現未知問題。
我們都知道APP_TIMER的實現就是使用了RTC1,那麼我們就可以利用上RTC1的特點,適當修改代碼來實現我們的需求,但這樣容易造成代碼耦合性高,不易維護和管理。
- 用一個新的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.c 和 board_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 日