博客 / 詳情

返回

《ESP32-S3使用指南—IDF版 V1.6》第二十三章 RTC實驗

第二十三章 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 例程功能

  1. 通過LCD實時顯示RTC時間
  2. LED閃爍,指示程序正在運行

    23.2.2 硬件資源

  3. LED
    LED0 - IO1
  4. XL9555
    IIC_SDA-IO41
    IIC_SCL-IO42
  5. SPILCD
    CS-IO21
    SCK-IO12
    SDA-IO11
    DC-IO40(在P5端口,使用跳線帽將IO_SET和LCD_DC相連)
    PWR- IO1_3(XL9555)
    RST- IO1_2(XL9555)
  6. 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顯示效果圖

user avatar wuduyouou_5de642de9c1e5 頭像 akziyuanzhan 頭像
2 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.