第二十三章 RTC實驗
1)實驗平台:正點原子DNESP32S3開發板
2)章節摘自【正點原子】ESP32-S3使用指南—IDF版 V1.6
3)購買鏈接:https://detail.tmall.com/item.htm?&id=768499342659
4)全套實驗源碼+手冊+視頻下載地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32S3.html
5)正點原子官方B站:https://space.bilibili.com/394620890
6)正點原子DNESP32S3開發板技術交流羣:132780729
本章介紹ESP32-S3實時時鐘(RTC)的使用,實時時鐘能為系統提供一個準確的時間,即時系統復位或主電源斷電,RTC依然能夠運行,因此RTC也經常用於各種低功耗場景。通過本章的學習,讀者將學習到RTC的使用。
本章分為如下幾個小節:
23.1 RTC時鐘簡介
23.2 硬件設計
23.3 程序設計
23.4 下載驗證
23.1 RTC時鐘簡介
RTC(實時時鐘)是指安裝在電子設備或實現其功能的IC(集成電路)上的時鐘。當您在數字電路中稱其為“時鐘”時,您可能會想到週期信號,但在英語中,clock也意味着“時鐘”。
那為什麼我們需要一個單獨的RTC?
原因是CPU的定時器時鐘功能只在“啓動”即“通電時”運行,斷電時停止。當然,如果時鐘不能連續跟蹤時間,則必須手動設置時間。
通常,RTC配備一個單獨分離的電源,如鈕釦電池(備用電池),即使開發板電源關閉,它也能保持運作,隨時可以實時顯示時間。然後,當開發板再次打開時,計算機內置的定時器時鐘從RTC讀取當前時間,並在此基礎上供電的同時,時間在其自身機制下顯示。順便説一句,由於鈕釦電池相對便宜且使用壽命長,因此RTC可以以極低的成本運行。基於此這個作用,它也可以用作內存。
1,ESP32-S3的RTC
在ESP32-S3中,並沒有像STM32芯片一樣,具有RTC外設,但是存在一個系統時間,利用系統時間,也可以實現實時時鐘的功能效果。
ESP32-S3使用兩種硬件時鐘源建立和保持系統時間。根據應用目的及對系統時間的精度要求,既可以僅使用其中一種時鐘源,也可以同時使用兩種時鐘源。這兩種硬件時鐘源為RTC定時器和高分辨率定時器。默認情況下,是使用這兩種定時器。下面我們將逐一介紹。
23.2 硬件設計
23.2.1 例程功能
- 通過LCD實時顯示RTC時間
-
LED閃爍,指示程序正在運行
23.2.2 硬件資源
- LED
LED0 - IO1 - XL9555
IIC_SDA-IO41
IIC_SCL-IO42 - SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在P5端口,使用跳線帽將IO_SET和LCD_DC相連)
PWR- IO1_3(XL9555)
RST- IO1_2(XL9555) -
RTC
23.2.3 原理圖
本章實驗使用的RTC為ESP32-S3的片上資源,因此沒有相應的連接原理圖。
23.3 程序設計
23.3.1 程序流程圖
程序流程圖能幫助我們更好的理解一個工程的功能和實現的過程,對學習和設計工程有很好的主導作用。下面看看本實驗的程序流程圖:
圖23.3.1.1 RTC實驗程序流程圖23.3.2 RTC函數解析
由於ESP32並未給出RTC相關的API函數,因而筆者在設計例程時調用了C庫中的一些函數來配置RTC時鐘,這些函數的描述及其作用如下:
1,獲取當前時間
該函數用於獲取當前時間,其函數原型如下所示:
struct tm *localtime(const time_t *timer);
該函數的形參描述,如下表所示:
表23.3.2.1 函數localtime ()形參描述
返回值:無。
2,設置當前時間
該函數用於設置當前時間,其函數原型如下所示:
int settimeofday(const struct timeval *tv, const struct timezone *tz);
該函數的形參描述,如下表所示:
表23.3.2.2 函數settimeofday ()形參描述
返回值:無。
23.3.3 RTC驅動解析
在IDF版的13_rtc例程中,作者在13_rtc \components\BSP路徑下新增了一個RTC文件夾,分別用於存放esp_rtc.c、esp_rtc.h兩個文件。其中,esp_rtc.h文件負責聲明RTC,而esp_rtc.c文件則實現了RTC的驅動代碼。下面,我們將詳細解析這兩個文件的實現內容。
1,esp_rtc.h文件
/* 時間結構體, 包括年月日周時分秒等信息 */
typedef struct
{
uint8_t hour; /* 時 */
uint8_t min; /* 分 */
uint8_t sec; /* 秒 */
/* 公曆年月日周 */
uint16_t year; /* 年 */
uint8_t month; /* 月 */
uint8_t date; /* 日 */
uint8_t week; /* 周 */
}_calendar_obj;
extern _calendar_obj calendar; /* 時間結構體 */
2,esp_rtc.c文件
calendar_obj calendar; /* 時間結構體 */
/**
* @brief RTC設置時間
* @param year :年
* @param mon :月
* @param mday :日
* @param hour :時
* @param min :分
* @param sec :秒
* @retval 無
*/
void rtc_set_time(int year,int mon,int mday,int hour,int min,int sec)
{
struct tm datetime;
/* 設置時間 */
datetime.tm_year = year - 1900;
datetime.tm_mon = mon - 1;
datetime.tm_mday = mday;
datetime.tm_hour = hour;
datetime.tm_min = min;
datetime.tm_sec = sec;
datetime.tm_isdst = -1;
/* 獲取1970.1.1以來的總秒數 */
time_t second = mktime(&datetime);
struct timeval val = { .tv_sec = second, .tv_usec = 0 };
/* 設置當前時間 */
settimeofday(&val, NULL);
}
/**
* @brief 獲取當前的時間
* @param 無
* @retval 無
*/
void rtc_get_time(void)
{
struct tm *datetime;
time_t second;
/* 返回自(1970.1.1 00:00:00 UTC)經過的時間(秒) */
time(&second);
datetime = localtime(&second);
calendar.hour = datetime->tm_hour; /* 時 */
calendar.min = datetime->tm_min; /* 分 */
calendar.sec = datetime->tm_sec; /* 秒 */
/* 公曆年月日周 */
calendar.year = datetime->tm_year + 1900; /* 年 */
calendar.month = datetime->tm_mon + 1; /* 月 */
calendar.date = datetime->tm_mday; /* 日 */
/* 周 */
calendar.week = rtc_get_week(calendar.year, calendar.month, calendar.date);
}
/**
* @brief 將年月日時分秒轉換成秒鐘數
* @note 輸入公曆日期得到星期(起始時間為: 公元0年3月1日開始, 輸入往後的任何日期,都 可以獲取正確的星期)
* 使用 基姆拉爾森計算公式 計算, 原理説明見此貼:
* https://www.cnblogs.com/fengbohello/p/3264300.html
* @param syear : 年份
* @param smon : 月份
* @param sday : 日期
* @retval 0, 星期天; 1 ~ 6: 星期一 ~ 星期六
*/
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day)
{
uint8_t week = 0;
if (month < 3)
{
month += 12;
--year;
}
week = (day + 1 + 2 * month + 3 * (month + 1) /
5 + year + (year >> 2) - year /
100 + year / 400) % 7;
return week;
}
以上三個獲取、設置RTC時間、日期的函數,均是對ESP IDF中RTC驅動的簡單封裝。
23.3.4 CMakeLists.txt文件
打開本實驗BSP下的CMakeLists.txt文件,其內容如下所示:
set(src_dirs
IIC
LCD
LED
SPI
RTC
XL9555)
set(include_dirs
IIC
LCD
LED
SPI
RTC
XL9555)
set(requires
driver
newlib)
idf_component_register(SRC_DIRS ${src_dirs}
INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
上述的紅色RTC驅動以及newlib依賴庫需要由開發者自行添加,以確保RTC驅動能夠順利集成到構建系統中。這一步驟是必不可少的,它確保了RTC驅動的正確性和可用性,為後續的開發工作提供了堅實的基礎。
23.3.5 實驗應用代碼
打開main/main.c文件,該文件定義了工程入口函數,名為app_main。該函數代碼如下。
i2c_obj_t i2c0_master;
/* 定義字符數組用於顯示周 */
char* weekdays[]={"Sunday","Monday","Tuesday","Wednesday",
"Thursday","Friday","Saterday"};
/**
* @brief 程序入口
* @param 無
* @retval 無
*/
void app_main(void)
{
esp_err_t ret;
uint8_t tbuf[40];
uint8_t t = 0;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret ==ESP_ERR_NVS_NO_FREE_PAGES
|| ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
led_init(); /* 初始化LED */
i2c0_master = iic_init(I2C_NUM_0); /* 初始化IIC0 */
spi2_init(); /* 初始化SPI2 */
xl9555_init(i2c0_master); /* IO擴展芯片初始化 */
lcd_init(); /* 初始化LCD */
rtc_set_time(2023,8,26,00,00,00); /* 設置RTC時間 */
lcd_show_string(10, 40, 240, 32, 32, "ESP32",RED);
lcd_show_string(10, 80, 240, 24, 24, "RTC Test",RED);
lcd_show_string(10, 110, 240, 16, 16, "ATOM@ALIENTEK",RED);
while (1)
{
t++;
if ((t % 10) == 0) /* 每100ms更新一次顯示數據 */
{
rtc_get_time();
sprintf((char *)tbuf, "Time:%02d:%02d:%02d",
calendar.hour, calendar.min, calendar.sec);
printf("Time:%02d:%02d:%02d\r\n", calendar.hour,
calendar.min, calendar.sec);
lcd_show_string(10, 130, 210, 16, 16, (char *)tbuf,BLUE);
sprintf((char *)tbuf, "Date:%04d-%02d-%02d",
calendar.year, calendar.month, calendar.date);
printf("Date:%02d-%02d-%02d\r\n", calendar.year,
calendar.month, calendar.date);
lcd_show_string(10, 150, 210, 16, 16, (char *)tbuf,BLUE);
sprintf((char *)tbuf, "Week:%s", weekdays[calendar.week]);
lcd_show_string(10, 170, 210, 16, 16, (char *)tbuf,BLUE);
}
if ((t % 20) == 0)
{
LED_TOGGLE(); /* 每200ms,翻轉一次LED */
}
vTaskDelay(10);
}
}
從上面的代碼中可以看到,在初始化完RTC後便每間隔100毫秒獲取一次RTC的時間和日期,並在LCD上進行顯示。
23.4 下載驗證
在完成編譯和燒錄操作後,可以看到LCD上實時地顯示着RTC的時間,並且可以看到LED在RTC週期性喚醒的驅動下以0.5Hz的頻率閃爍着。
圖23.3.1 SPI LCD顯示效果圖