21-ESP32-S3实时时钟(RTC)

ESP32-S3实时时钟(RTC)的使用

ESP32-S3是一款高性能的Wi-Fi和蓝牙集成的系统级芯片(SoC),它包含一个实时时钟(RTC)模块,可以在系统的其他部分关闭时继续运行,以节省电能。下面是如何使用ESP32-S3的RTC模块的一些基本步骤。

RTC模块的基本功能

ESP32-S3的RTC模块具有以下主要功能:

  • 时间和日期:RTC模块可以提供实时的时间和日期信息。
  • 闹钟:可以设置RTC模块在特定的时间和日期发出信号。
  • 定时器:RTC模块可以作为一个定时器,用于在特定的时间间隔后发出信号。
  • 低功耗模式:当ESP32-S3的其他部分关闭时,RTC模块可以继续运行,以节省电能。

常用API

1.设置当前时间 settimeofday函数

settimeofday函数用于设置系统时间。它的原型如下:

int settimeofday(const struct timeval *tv, const struct timezone *tz);

其中,tv参数是一个指向timeval结构体的指针,该结构体包含了要设置的时间(以自Epoch(1970-01-01 00:00:00 +0000 (UTC))以来的秒数和微秒数)。tz参数在现代系统中通常被忽略,应设置为NULL。

如果函数执行成功,返回0;如果失败,返回-1,并设置errno以指示错误。

2.获取当前时间 localtime_r函数

localtime_r函数用于将time_t类型的时间(自Epoch以来的秒数)转换为本地时间表示的tm结构体。它的原型如下:

struct tm *localtime_r(const time_t *timer, struct tm *buf);

其中,timer参数是一个指向time_t类型的指针,表示要转换的时间;buf参数是一个指向tm结构体的指针,用于存储转换后的时间。

localtime_r函数是线程安全的,因为它使用用户提供的存储空间来存储结果,而不是使用静态存储空间。如果函数执行成功,返回指向结果的指针;如果失败,返回NULL。

3.设置时间环境变量

setenv是一个用于设置环境变量的函数。它的原型如下:

int setenv(const char *name, const char *value, int overwrite);

其中,name是要添加或修改的环境变量的名称,value是环境变量的新值,overwrite是一个标志,表示当环境变量name已经存在时是否覆盖其值。

  • 如果overwrite非零,那么无论环境变量name是否存在,都将其值设置为value
  • 如果overwrite为零,那么只有当环境变量name不存在时,才将其值设置为value

setenv函数返回0表示成功,返回-1表示失败。

例如,setenv("TZ", "CST-8", 1);这行代码的作用是设置环境变量TZ的值为CST-8,表示中国标准时间,即UTC+8。这样,当你获取或设置RTC模块的时间时,它就会按照中国标准时间来进行。

关于 协调时间时 的概念

UTC(Coodinated Universal Time),又称协调世界时,世界统一时间、世界标准时间、国际协调时间。由于英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。

UTC是现在全球通用的时间标准,全球各地都同意将各自的时间进行同步协调。UTC是以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统。

UTC基于国际原子时,并通过不规则的加入闰秒来抵消地球自转变慢的影响。闰秒在必要的时候会被插入到UTC中,以保证协调世界时(UTC)与世界时(UT1)相差不超过0.9秒。

如何设置RTC模块

写一个示例,设置当前时间然后获取当前时间 打在串口上

#include <time.h>  // 引入时间库

void setup() {
  // 初始化串口
  Serial.begin(115200);  // 设置串口波特率为115200

  // 设置当前时间
  struct tm timeinfo;  // 定义一个tm结构体变量timeinfo
  timeinfo.tm_year = 2024 - 1900;  // 设置年份
  timeinfo.tm_mon = 5 - 1;  // 设置月份
  timeinfo.tm_mday = 2;  // 设置日期
  timeinfo.tm_hour = 13;  // 设置小时
  timeinfo.tm_min = 45;  // 设置分钟
  timeinfo.tm_sec = 30;  // 设置秒
  setenv("TZ", "CST-8", 1);  // 设置时区为中国标准时间
  tzset();  // 使时区设置生效
  const time_t now = mktime(&timeinfo);  // 将tm结构体转换为time_t格式
  struct timeval tv = { .tv_sec = now };  // 定义一个timeval结构体变量tv,并设置其秒数
  settimeofday(&tv, NULL);  // 设置系统时间

  // 获取并打印当前时间
  time_t now_get;  // 定义一个time_t变量now_get
  struct tm timeinfo_get;  // 定义一个tm结构体变量timeinfo_get
  time(&now_get);  // 获取当前时间
  localtime_r(&now_get, &timeinfo_get);  // 将time_t格式的时间转换为tm结构体格式
  Serial.print("Current time: ");  // 打印字符串"Current time: "
  Serial.print(timeinfo_get.tm_hour);  // 打印小时
  Serial.print(":");  // 打印冒号
  Serial.print(timeinfo_get.tm_min);  // 打印分钟
  Serial.print(":");  // 打印冒号
  Serial.println(timeinfo_get.tm_sec);  // 打印秒数并换行
}

void loop() {
  // 延时1秒
  delay(1000);  // 暂停1秒
}

这个示例首先在setup()函数中设置了当前时间为2024年5月2日13:45:30,然后立即获取并打印当前时间。这样,你就可以在串口监视器上看到设置的当前时间了。

🚨注意setenv("TZ", "CST-8", 1); 这行代码的作用是设置时区为中国标准时间。,setenv函数用于设置环境变量。这里,设置了TZ环境变量的值为CST-8,表示中国标准时间,即UTC+8。这样,当你获取或设置RTC模块的时间时,它就会按照中国标准时间来进行。如果你想设置为其他时区,只需将CST-8替换为相应的时区即可。例如,如果你想设置为美国东部标准时间,可以将CST-8替换为EST5EDT

🚨注意在C语言的tm结构体中,tm_year字段表示的是自1900年以来的年数,tm_mon字段表示的是月份,但是它的范围是0(一月)到11(十二月)。所以,当我们设置tm_yeartm_mon字段的值时,需要进行相应的调整:

  • 对于年份,我们需要从实际年份中减去1900。例如,我们想要设置年份为2024,那么tm_year应该设置为2024 - 1900

  • 对于月份,我们需要从实际月份中减去1。例如,如果我们想要设置月份为5月,那么tm_mon应该设置为5 - 1

这样,2024 - 19005 - 1就分别表示了年份2024和5月。

总结

参考资料

ESP-IDF 系统时间
微软copilot

### 使用ESP32-S3实现按键设置闹钟 为了在ESP32-S3上通过按键来设置并触发闹钟,可以采用Arduino框架编写程序。此过程涉及配置实时时钟(RTC),读取按钮状态以及处理时间逻辑。 下面是一个简单的例子,展示了如何使用两个外部按钮分别用于增加时间和确认设定的时间作为闹钟时间: #### 初始化和定义变量 首先,在代码顶部初始化必要的库和支持函数,并声明全局变量存储当前时间和目标闹钟时间。 ```cpp #include <WiFi.h> #include <Wire.h> #include <RTClib.h> // 定义引脚编号 const int BUTTON_UP_PIN = 14; // 上升沿按钮连接到GPIO14 const int BUTTON_SET_PIN = 15; // 设置按钮连接到GPIO15 RTC_DS3231 rtc; DateTime now; unsigned long alarmTime = 0UL; // 存储闹钟时间戳 volatile bool buttonPressed = false; ``` #### 设定回调函数 当检测到按钮按下事件时调用这些函数更新`buttonPressed`标志位以便主循环能够响应变化。 ```cpp void IRAM_ATTR onButtonUpPress() { noInterrupts(); buttonPressed |= (digitalRead(BUTTON_UP_PIN) == LOW); interrupts(); } void IRAM_ATTR onButtonSetPress() { noInterrupts(); buttonPressed |= (digitalRead(BUTTON_SET_PIN) == LOW); interrupts(); } ``` #### 主函数setup() 在此部分完成硬件初始化工作,包括启动串口通信、配置RTC模块、安装中断服务例程和服务端口监听等操作。 ```cpp void setup () { Serial.begin(115200); if (!rtc.begin()) { Serial.println("Couldn&#39;t find RTC"); while (1); } pinMode(BUTTON_UP_PIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(BUTTON_UP_PIN), onButtonUpPress, FALLING); pinMode(BUTTON_SET_PIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(BUTTON_SET_PIN), onButtonSetPress, FALLING); Wire.setClock(400000L); // I2C速度提升至400kHz // 如果成功找到RTC,则同步系统时间为当前RTC时间 if (rtc.isrunning()) { now = rtc.now(); } else { Serial.println("RTC is NOT running!"); // 将RTC设为编译日期时间 DateTime compiled = __DATE__ " " __TIME__; rtc.adjust(DateTime(__DATE__, __TIME__)); } } ``` #### 循环函数loop() 这里实现了核心业务逻辑——持续监测按钮输入调整时间直到用户确认保存新的闹钟时间;一旦到达预设时刻就发出提示音或其他形式的通知动作。 ```cpp void loop(){ unsigned long currentTimeMillis = millis(); if(buttonPressed){ detachInterrupt(digitalPinToInterrupt(BUTTON_UP_PIN)); detachInterrupt(digitalPinToInterrupt(BUTTON_SET_PIN)); if(digitalRead(BUTTON_UP_PIN)==LOW){ now += TimeSpan(0, 0, 1, 0); // 增加一分钟 Serial.print("New Time:"); Serial.println(now.toString()); delay(200); // 防抖动延迟 }else if(digitalRead(BUTTON_SET_PIN)==LOW){ alarmTime = now.unixtime(); Serial.print("Alarm set at "); Serial.println(rtc.getEpoch(alarmTime).toString()); delay(200); // 防抖动延迟 } buttonPressed=false; attachInterrupt(digitalPinToInterrupt(BUTTON_UP_PIN),onButtonUpPress,FALLING); attachInterrupt(digitalPinToInterrupt(BUTTON_SET_PIN),onButtonSetPress,FALLING); } if(currentTimeMillis >= alarmTime * 1000 && alarmTime != 0){ Serial.println("ALARM! ALARM!"); // 这里可加入实际报警机制比如蜂鸣器发声或LED闪烁 tone(8, 1000, 1000); // 清除已发生的警报条件 alarmTime=0; } delay(1000); // 每秒钟检查一次是否达到闹钟时间 } ``` 上述代码片段提供了基本思路和技术细节说明[^1]。值得注意的是,具体实施过程中可能还需要考虑更多因素如电源管理策略、异常情况下的恢复措施等等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁子希

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值