《ESP32S3 Arduino开发指南 V1.0 》第十八章 RTC实验

第十八章 RTC实验


       本章,我们将介绍如何使用ESP32-S3的系统时间,实现一个简单的实时时钟。

       本章分为如下几个小节:

       18.1 RTC介绍

       18.2 硬件设计

       18.3 软件设计

       18.4 下载验证


        18.1 RTC介绍

       RTC,Real Time Clock,实时时钟,专门用来记录时间的。

       在ESP32-S3中,并没有像STM32芯片一样,具有RTC外设,但是存在一个系统时间,利用系统时间,也可以实现实时时钟的功能效果。

       ESP32-S3使用两种硬件时钟源建立和保持系统时间。根据应用目的及对系统时间的精度要求,既可以仅使用其中一种时钟源,也可以同时使用两种时钟源。这两种硬件时钟源为RTC定时器和高分辨率定时器。默认情况下,是使用这两种定时器。

       在Arduino开发中,我们需要下载一个“ESP32Time”软件库,该库用于设置和读取ESP32-S3内部RTC时间,我们就是基于这个库进行开发RTC功能。这个库,可以在Arduino IDE中库管理搜索到,具体下载操作如下图所示。


图18.1.1 ESP32Time库下载过程


       假如在线安装不成功,也可以尝试使用手动安装方法,跟16.1.2小节描述的步骤是一样的。“ESP32Time.zip”压缩包在“资料盘→6,软件资料→2,Arduino软件包”文件夹下,下载成功后,便可以在“C:\Users\用户名\ Documents\Arduino\libraries\”路径下找到该文件。

       ESP32Time库提供有三个示例工程,如下图所示:


图18.1.2 ESP32Time库示例工程


       最快速了解库使用方法莫过于查看示例工程了,大家可以自行查看学习,下载看现象。

       在ESP32Time库中,总的来说,分为三类函数:设置时间、获取日期时间、提取日期时间的某个数据(年/月/日/时/分/秒)。

       要使用该库的函数,所以需要先定义ESP32Time对象实例,操作如下:

ESP32Time rtc;
ESP32Time rtc(offset);  // create an instance with a specifed offset in seconds

       这里有两种方式,一种是不带参数,另一种是带参数的。参数offset的意思是时间的偏移,单位为秒。这两种有什么区别?在我的理解看来,不带参数的操作属于以自我为参照,带参数的操作属于以其他为参照。若只需要显示一个时间时,就可以使用不带参数的,即“ESP32Time rtc”;若要显示多个时间,则参考的还是使用“ESP32Time rtc”进行操作,而第二个就需要使用“ESP32Time rtc1(offset)”,offset表明跟参考时间的关系。

       以上的设计考虑,其实是因为:每个国家都会有自己的时间时,比如中国的北京时间,英国的伦敦时间,日本的东京时间。它们处于不同的时区,但是都可以以格林尼治时间(GMT)为参考,向东的时区以依次为GMT+1,GMT+2,……,GMT+12;向西的时区依次为GMT-1,GMT-2,……,GMT-12。中国通用的北京时间处于GMT+8。

       打个比方,我们显示两个时区,rtc一开始以格林尼治时间为标准进行设置,然后rtc1设置为北京时间。

ESP32Time rtc;           // 格林尼治时间(伦敦时间)
ESP32Time rtc1(28800); // 北京时间,跟rtc相差8小时,8*60*60=28800秒

       这样定义ESP32Time对象实例,只需要rtc通过setTime函数设置好时间,rtc1就会在它的基础上+8小时,而不需要rtc1自己设置时间。简单理解,该功能就是可以设置相对时间。

       接下来介绍一下,ESP32Time库的函数,如下所示。

setTime(30, 24, 15, 17, 1, 2021);   // 17th Jan 2021 15:24:30
setTime(1609459200);       // 1st Jan 2021 00:00:00
setTimeStruct(time);       // set with time struct
 
getTime()                //  (String) 15:24:38
getDate()               //  (String) Sun, Jan 17 2021
getDate(true)           //  (String) Sunday, January 17 2021
getDateTime()           //  (String) Sun, Jan 17 2021 15:24:38
getDateTime(true)       //  (String) Sunday, January 17 2021 15:24:38
getTimeDate()           //  (String) 15:24:38 Sun, Jan 17 2021
getTimeDate(true)       //  (String) 15:24:38 Sunday, January 17 2021
 
getMicros()           //  (unsigned long)    723546
getMillis()             //  (unsigned long)    723
getEpoch()              //  (unsigned long)    1609459200
getLocalEpoch()         //  (unsigned long)    1609459200
getSecond()             //  (int)     38    (0-59)
getMinute()             //  (int)     24    (0-59)
getHour()               //  (int)     3     (0-12)
getHour(true)           //  (int)     15    (0-23)
getAmPm()               //  (String)  PM
getAmPm(true)          //  (String)  pm
getDay()                //  (int)     17    (1-31)
getDayofWeek()          //  (int)     0     (0-6)
getDayofYear()          //  (int)     16    (0-365)
getMonth()              //  (int)     0     (0-11)
getYear()               //  (int)     2021
 
getTime("%A, %B %d %Y %H:%M:%S") //(String)returns time with specified format

       通过看前面注释的说明,我们也比较清楚函数的使用了。

       本例程主要用到setTime函数和getTimeStruct函数,使用方法如下:

rtc.setTime(00, 51, 17, 1, 12, 2023);  /* 2023年12月1日17:52:00 */
struct tm timeinfo = rtc.getTimeStruct();

       使用getTimeStruct函数,把时间信息存进timeinfo结构体。通过这个结构体就可以获取年、月、日、时、分、秒等信息,该结构体类型如下:

struct tm
{
  int tm_sec;
  int tm_min;
  int tm_hour;
  int tm_mday;
  int tm_mon;
  int tm_year;
  int tm_wday;
  int tm_yday;
  int tm_isdst;
};


        18.2 硬件设计


       1. 例程功能

       通过LCD显示模块实时显示RTC时间,包括年、月、日、时、分、秒等信息。


       2. 硬件资源


       1)USART0

              U0TXD-IO43     U0RXD-IO44


       2)XL9555

              IIC_SDA-IO41    IIC_SCL-IO42


       3)SPILCD

              CS-IO21         SCK-IO12          SDA-IO11

              DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)

              PWR- IO1_3(XL9555)              RST- IO1_2(XL9555)


       3. 原理图

       RTC为ESP32-S3的片上资源,因此并没有相应的连接原理图。


        18.3 软件设计


       18.3.1 程序流程图

       下面看看本实验的程序流程图:


图18.3.1 程序流程图


       18.3.2 程序解析


       1. 12_rtc.ino代码

       ESP32Time库的函数已经满足了例程需求,所以没有另外写rtc.cpp和rtc.h,所以这里直接介绍12_rtc.ino文件。

       在12_rtc.ino里面编写如下代码:

#include "uart.h"
#include "xl9555.h"
#include "spilcd.h"
#include    /* 需要安装ESP32Time库 */
 
 
ESP32Time rtc;
uint8_t tbuf[100];         /* 存放RTC信息 */
 
/**
 * @brief   当程序开始执行时,将调用setup()函数,通常用来初始化变量、函数等
 * @param   无
 * @retval 无
 */
void setup() 
{
    uart_init(0, 115200); /* 串口0初始化 */
    xl9555_init();           /* IO扩展芯片初始化 */
    lcd_init();              /* LCD初始化 */
    rtc.setTime(00, 51, 17, 1, 12, 2023);  /* 2023年12月1日17:52:00 */
    lcd_show_string(30, 50, 200, 16, LCD_FONT_16, "ESP32-S3", RED);
    lcd_show_string(30, 70, 200, 16, LCD_FONT_16, "RTC TEST", RED);
    lcd_show_string(30, 90, 200, 16, LCD_FONT_16, "ATOM@ALIENTEK", RED);
}
 
/**
 * @brief    循环函数,通常放程序的主体或者需要不断刷新的语句
 * @param  无
 * @retval 无
 */
void loop() 
{
    struct tm timeinfo = rtc.getTimeStruct();
    /* 根据time.h头文件中tm结构体的定义进行调整显示 */
sprintf((char *)tbuf, "Time:%02d:%02d:%02d", 
timeinfo.tm_hour-1, timeinfo.tm_min, timeinfo.tm_sec);    
    lcd_show_string(30, 130, 210, 16, LCD_FONT_16, (char *)tbuf, RED);
sprintf((char *)tbuf, "Date:%04d-%02d-%02d", 
timeinfo.tm_year+1900, timeinfo.tm_mon+1, timeinfo.tm_mday);
    lcd_show_string(30, 150, 210, 16, LCD_FONT_16, (char *)tbuf, RED);
    sprintf((char *)tbuf, "Week:%d", timeinfo.tm_wday);
    lcd_show_string(30, 170, 210, 16, LCD_FONT_16, (char *)tbuf, RED);
 
    delay(1000);
}

       在setup函数中,调用uart_init函数完成串口初始化,调用xl9555_init函数完成XL9555初始化,调用lcd_init函数完成LCD初始化,然后调用rtc.setTime函数设置年月日时分秒信息,然后LCD显示实验信息。

       在loop函数中,调用rtc.getTimeStruct函数实现时间信息的获取,通过sprintf函数组合成字符串,然后调用lcd_show_string函数进行显示,这个过程是间隔一秒进行。


        18.4 下载验证

       下载代码后,LCD显示RTC实验信息,然后间隔一秒更新时间信息。


图18.4.1 RTC实验测试图


请使用浏览器的分享功能分享到微信等