OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 38|回复: 0

《ESP32-P4开发指南— V1.0》第二十五章 RTC实验

[复制链接]

1227

主题

1241

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5280
金钱
5280
注册时间
2019-5-8
在线时间
1347 小时
发表于 前天 09:19 | 显示全部楼层 |阅读模式
第二十五章 RTC实验

1)实验平台:正点原子DNESP32P4开发板

2)章节摘自【正点原子】ESP32-P4开发指南— V1.0

3)购买链接:https://detail.tmall.com/item.htm?id=873309579825

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子DNESP32S3开发板技术交流群:132780729


2.jpg

3.png

本章将学习如何使用ESP32-P4的系统时间,实现一个简单的实时时钟。
本章分为如下几个小节:
25.1 RTC介绍
25.2 硬件设计
25.3 程序设计
25.4 下载验证


25.1 RTC介绍
RTC,Real Time Clock,实时时钟,专门用来记录时间的。
在ESP32-P4中,并没有像STM32芯片一样,具有RTC外设,但是存在一个系统时间,利用系统时间,也可以实现实时时钟的功能效果。
ESP32-P4使用两种硬件时钟源建立和保持系统时间。根据应用目的及对系统时间的精度要求,既可以仅使用其中一种时钟源,也可以同时使用两种时钟源。这两种硬件时钟源为RTC定时器和高分辨率定时器。
RTC定时器:RTC定时器在任何睡眠模式下及在任何复位后均可保持系统时间(上电复位除外,因为上电复位会重置RTC定时器)。时钟频率偏差取决于RTC定时器时钟源,该偏差只会在水面模式下影响时间精度。睡眠模式下,时间分辨率为6.667us。
高分辨率定时器:高分辨率定时器在睡眠模式下及在复位后不可用,但其时间精度更高。该定时器使用APB_CLK时钟源(通常为80MHz),时钟频率偏差小于±10ppm,时间分辨率为1us。
默认情况下,是使用这两种定时器。

25.2 硬件设计

25.2.1 例程功能
通过LCD屏实时显示RTC时间,包括年、月、日、时、分、秒等信息。

25.2.2 硬件资源
1)LED灯
        LED        0        - IO51
2)RGBLCD / MIPILCD(引脚太多,不罗列出来)

25.2.3 原理图
RTC使用ESP32-P4内部定时器实现,而定时器属于片上资源,因此没有对应的连接原理图。

25.3 程序设计

25.3.1 RTC的驱动接口
RTC数据的获取,可通过使用POSIX函数或使用标准C库函数。使用时间相关功能函数,必须先导入以下头文件:
  1. #include <sys/time.h>
复制代码
接下来,作者将介绍一些常用的函数,这些函数的描述及其作用如下:
1,获取时间函数time
该函数用于从系统中获取时间,其函数原型如下:
  1. time_t time(time_t *_Time);
复制代码
函数形参:

1.png
表25.3.1.1 time函数形参描述

函数返回值:
成功时,返回time_t类型的当前日历时间。错误时,返回-1。
注意:time_t类型最终是long int类型,这个会跟运行硬件平台相关,库中也有很多平台相关的编译开关。
2,得到struct tm格式及本地时间表达的日历时间函数localtime
该函数用于获取struct tm格式的时间,其函数原型如下:
  1. struct tm localtime(const time_t *_Time);
复制代码
函数形参:

2.png
表25.3.1.2 tm_localtime函数形参描述

函数返回值:
成功时为指向内部静态struct tm对象的指针,否则为NULL。
struct tm结构体其定义如下:
  1. struct tm {
  2.   int tm_sec;                /* 秒,范围从0到59 */
  3.   int tm_min;                /* 分,范围从0到59 */
  4.   int tm_hour;                /* 小时,范围从0到23 */
  5.   int tm_mday;                /* 一个月中的第几天,范围从1到31 */
  6.   int tm_mon;                /* 月份,范围从0到11 */
  7.   int tm_year;                /* 自1900年起的年数 */
  8.   int tm_wday;                /* 一周中的第几天,范围从0到6 */
  9.   int tm_yday;                /* 一年中的第几天,范围从0到365 */
  10.   int tm_isdst;                /* 夏令时 */
  11. };
复制代码
3,得到总秒数函数mktime
该函数得到总秒数,其函数原型如下:
  1. time_t mktime(struct tm *_Tm);
复制代码
函数形参:

3.png
表25.3.1.3 mktime函数形参描述

函数返回值:
返回自1970年1月1日以来持续时间的秒数,发生错误时返回-1。
4,设置当前时间函数settimeofday
该函数会把时间设置成由tv所指向的信息,当地时区信息则设置成tz,其函数原型如下:
  1. int settimeofday(const struct timeval *tv, const struct timezone *tz);
复制代码
函数形参:

4.png
表25.3.1.4 settimeofday函数形参描述

函数返回值:
成功返回0,失败返回-1。        
tv是指向struct timeval结构体指针。struct timeval结构体中各个成员,如下代码所示:
  1. struct timeval {
  2.     time_t      tv_sec;     /* 秒 */
  3.     suseconds_t tv_usec;    /* 微妙 */
  4. };
复制代码
注意:获取时区tz信息依赖于系统,有的系统无法获取,所以一般调用时该函数第二个参数为NULL。对于ESP32-P4而言,该函数就是更新内部定时器时间。

25.3.2 程序流程图

第二十五章 RTC实验2299.png
图25.3.2.1 RTC实验程序流程图

25.3.3 程序解析
在16_rtc例程中,作者在16_rtc \components\BSP路径下新建了1个文件夹RTC,并且需要更改CMakeLists.txt内容,以便在其他文件上调用。
1. RTC驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。RTC驱动源码包括两个文件:esp_rtc.c和esp_rtc.h。
下面先解析esp_rtc.h的程序。定义了一个_calendar_obj结构体类型,其余都是函数声明,代码如下:
  1. <font size="3">/* 时间结构体, 包括年月日周时分秒等信息 */</font>
  2. <font size="3">typedef struct</font>
  3. <font size="3">{</font>
  4. <font size="3">    uint8_t hour;       /* 时 */</font>
  5. <font size="3">    uint8_t min;        /* 分 */</font>
  6. <font size="3">    uint8_t sec;        /* 秒 */</font>
  7. <font size="3">    /* 公历年月日周 */</font>
  8. <font size="3">    uint16_t year;      /* 年 */</font>
  9. <font size="3">    uint8_t  month;     /* 月 */</font>
  10. <font size="3">    uint8_t  date;      /* 日 */</font>
  11. <font size="3">    uint8_t  week;      /* 周 */</font>
  12. <font size="3">} _calendar_obj;</font>
复制代码
该结构体类型将会被用来存放时钟的年月日时分秒等信息。在esp_rtc.c文件中定义了_calendar_obj结构体类型的变量calendar,方便其他函数调用。
接下来,解析一下esp_rtc.c的程序,首先来看一下设置时间函数rtc_set_time,代码如下:
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief             RTC设置时间</font>
  3. <font size="3"> * [url=home.php?mod=space&uid=271674]@param[/url]             year         :年</font>
  4. <font size="3"> * @param             mon         :月</font>
  5. <font size="3"> * @param            mday    :日</font>
  6. <font size="3"> * @param             hour    :时</font>
  7. <font size="3"> * @param             min     :分</font>
  8. <font size="3"> * @param             sec     :秒</font>
  9. <font size="3"> * @retval            无</font>
  10. <font size="3"> */</font>
  11. <font size="3">void rtc_set_time(int year, int mon, int mday, int hour, int min, int sec)</font>
  12. <font size="3">{</font>
  13. <font size="3">    struct tm time_set; /* 存放时间的各个量(年月日时分秒)结构体 */</font>
  14. <font size="3">    time_t second;      /* 存放时间的总秒数 */</font>

  15. <font size="3">    time_set.tm_year  = year - 1900;</font>
  16. <font size="3">    time_set.tm_mon   = mon - 1;</font>
  17. <font size="3">    time_set.tm_mday  = mday;</font>
  18. <font size="3">    time_set.tm_hour  = hour;</font>
  19. <font size="3">    time_set.tm_min   = min;</font>
  20. <font size="3">    time_set.tm_sec   = sec;</font>
  21. <font size="3">    time_set.tm_isdst = -1;</font>

  22. <font size="3">    second = mktime(&time_set);                /* 将时间转换为自1970年1月1日以来持续时间秒数 */</font>

  23. <font size="3">    struct timeval val = { </font>
  24. <font size="3">        .tv_sec = second, </font>
  25. <font size="3">        .tv_usec = 0 </font>
  26. <font size="3">    };</font>

  27. <font size="3">    settimeofday(&val, NULL);                  /* 设置当前时间 */</font>
  28. <font size="3">}</font>
复制代码
在rtc_set_time函数中,首先定义struct tm结构体类型的变量time_set,根据要设置的时间对其成员进行赋值,这里需要注意成员的取值范围,然后调用mktime函数将time_set存放的时间信息转换为总秒数,最后通过settimeofday函数设置当前时间。
接下来看一下获取当前时间函数rtc_get_time,代码如下
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief            获取当前的时间</font>
  3. <font size="3"> * @param            无</font>
  4. <font size="3"> * @retval          无</font>
  5. <font size="3"> */</font>
  6. <font size="3">void rtc_get_time(void)</font>
  7. <font size="3">{</font>
  8. <font size="3">    struct tm *time_block;  /* 存放时间的各个量(年月日时分秒)结构体 */</font>
  9. <font size="3">    time_t second;          /* 存放时间的总秒数 */</font>

  10. <font size="3">    time(&second);                                                /* 获取当前的系统时间总秒数 */</font>
  11. <font size="3">    time_block = localtime(&second);         /* 把总秒数格式化存放到struct tm变量 */</font>

  12. <font size="3">    /* 公历年 月 日 星期 时 分 秒 */</font>
  13. <font size="3">    calendar.year  = time_block->tm_year + 1900;    /* 年(从1900开始) */</font>
  14. <font size="3">    calendar.month = time_block->tm_mon  + 1;       /* 月(从0(1月)到11(12月)) */</font>
  15. <font size="3">    calendar.date  = time_block->tm_mday;           /* 日(从1到31) */</font>
  16. <font size="3">    calendar.week  = time_block->tm_wday;           /* 周几(从0(周日)到6(周六)) */</font>
  17. <font size="3">    calendar.hour  = time_block->tm_hour;           /* 时(从0到23) */</font>
  18. <font size="3">    calendar.min   = time_block->tm_min;            /* 分(从0到59) */</font>
  19. <font size="3">    calendar.sec   = time_block->tm_sec;            /* 秒(从0到59) */</font>
  20. <font size="3">}</font>
复制代码
在rtc_get_time函数中,首先通过time函数获取当前系统时间换算成的总秒数,然后通过localtime函数得到struct tm结构体类型的数据,同时更新到calendar结构体变量中,方便其他函数调用。
2. CMakeLists.txt文件
本例程的功能实现主要依靠RTC驱动。要在main函数中,成功调用RTC文件中的内容,就得需要修改BSP文件夹下的CMakeLists.txt文件,修改如下:
  1. <font size="3">set(src_dirs</font>
  2. <font size="3">                   LED</font>
  3. <font size="3">                        LCD</font>
  4. <font size="3">                   RTC)</font>

  5. <font size="3">set(include_dirs</font>
  6. <font size="3">                  LED</font>
  7. <font size="3">                        LCD</font>
  8. <font size="3">                   RTC)</font>

  9. <font size="3">set(requires</font>
  10. <font size="3">                   driver</font>
  11. <font size="3">                   esp_lcd</font>
  12. <font size="3">                        esp_common</font>
  13. <font size="3">                   newlib)</font>

  14. <font size="3">idf_component_register(        SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})</font>

  15. <font size="3">component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)</font>
复制代码
3. main.c驱动代码
在main.c里面编写如下代码。
  1. <font size="3">/* 定义字符数组用于显示星期 */</font>
  2. <font size="3">char *weekdays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", </font>
  3. <font size="3">"Friday", "Saturday"};</font>

  4. <font size="3">void app_main(void)</font>
  5. <font size="3">{</font>
  6. <font size="3">    esp_err_t ret;</font>
  7. <font size="3">    uint8_t tbuf[40];</font>
  8. <font size="3">    uint8_t t = 0;</font>

  9. <font size="3">    ret = nvs_flash_init();           /* 初始化NVS */</font>
  10. <font size="3">    if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)</font>
  11. <font size="3">    {</font>
  12. <font size="3">        ESP_ERROR_CHECK(nvs_flash_erase());</font>
  13. <font size="3">        ESP_ERROR_CHECK(nvs_flash_init());</font>
  14. <font size="3">    }</font>

  15. <font size="3">    led_init();                             /* LED初始化 */</font>
  16. <font size="3">    lcd_init();                             /* LCD屏初始化 */</font>

  17. <font size="3">    rtc_set_time(2025, 1, 1, 8, 8, 00);   /* 设置RTC时间 */</font>

  18. <font size="3">    lcd_show_string(10, 50,  200, 16, 16, "ESP32-P4", RED);</font>
  19. <font size="3">    lcd_show_string(10, 70,  200, 16, 16, "RTC TEST", RED);</font>
  20. <font size="3">    lcd_show_string(10, 90,  200, 16, 16, "ATOM@ALIENTEK", RED);</font>

  21. <font size="3">    while (1)</font>
  22. <font size="3">    {</font>
  23. <font size="3">        t++;</font>

  24. <font size="3">        if ((t % 10) == 0)      /* 每100ms更新一次显示数据 */</font>
  25. <font size="3">        {</font>
  26. <font size="3">            rtc_get_time();</font>
  27. <font size="3">            sprintf((char *)tbuf, "Time:%02d:%02d:%02d", calendar.hour,</font>
  28. <font size="3">                                   calendar.min, calendar.sec);</font>
  29. <font size="3">            lcd_show_string(10, 130, 210, 16, 16, (char *)tbuf,BLUE);</font>
  30. <font size="3">            sprintf((char *)tbuf, "Date:%04d-%02d-%02d", calendar.year,</font>
  31. <font size="3">                                   calendar.month, calendar.date);</font>
  32. <font size="3">            lcd_show_string(10, 150, 210, 16, 16, (char *)tbuf,BLUE);</font>
  33. <font size="3">            sprintf((char *)tbuf, "Week:%s", weekdays[calendar.week]);</font>
  34. <font size="3">            lcd_show_string(10, 170, 210, 16, 16, (char *)tbuf,BLUE);</font>
  35. <font size="3">        }</font>

  36. <font size="3">        if ((t % 20) == 0)</font>
  37. <font size="3">        {</font>
  38. <font size="3">            LED0_TOGGLE();</font>
  39. <font size="3">        }</font>

  40. <font size="3">        vTaskDelay(pdMS_TO_TICKS(10));</font>
  41. <font size="3">    }</font>
  42. <font size="3">}</font>
复制代码
在app_main函数中,首先通过rtc_set_time函数设置时间,然后在循环中,每隔10毫秒调用rtc_get_time函数,然后调用lcd_show_string函数对时间信息进行显示。LED0闪烁,以提示程序正在运行。

25.4 下载验证
将程序下载到开发板后,可以看到LED0不停闪烁,提示程序已经在运行了,可看到LCD显示的内容如图25.4.1所示:

第二十五章 RTC实验7103.png
图25.4.1 RTC实验程序运行效果图
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-1-2 11:46

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表