單片機獲取真實時間(即當前的年月日、時分秒等)通常需要依賴外部時間源或模塊,因為單片機本身沒有內置的實時時鐘(RTC)功能。
在 C 語言環境下,單片機獲取真實時間通常需要依賴?外部硬件模塊(如 RTC、GPS)或?網絡協議(如 NTP)。以下是幾種常見方法的詳細實現(基于 C 語言,不依賴 Arduino 庫):
1. 通過外部 RTC 模塊(如 DS3231)
硬件連接
-
使用?I2C?接口連接 RTC 模塊(如 SDA→P1.0, SCL→P1.1)。
-
為 RTC 模塊連接備用電池(如 CR2032)。
C 語言代碼示例
#include <stdint.h>
#include "i2c.h" // 假設已實現 I2C 底層驅動#define DS3231_ADDR 0x68 // DS3231 的 I2C 地址// 從 DS3231 讀取時間(BCD 格式)
void ds3231_get_time(uint8_t *sec, uint8_t *min, uint8_t *hour, uint8_t *day, uint8_t *month, uint8_t *year) {uint8_t buf[7];i2c_start();i2c_write(DS3231_ADDR << 1); // 寫模式i2c_write(0x00); // 從寄存器 0 開始讀i2c_stop();i2c_start();i2c_write((DS3231_ADDR << 1) | 1); // 讀模式for (int i = 0; i < 6; i++) buf[i] = i2c_read(1); // 帶 ACKbuf[6] = i2c_read(0); // 最后一個字節不帶 ACKi2c_stop();*sec = buf[0] & 0x7F; // 去掉最高位(時鐘停止標志)*min = buf[1];*hour = buf[2];*day = buf[4];*month = buf[5];*year = buf[6];
}// BCD 轉十進制
uint8_t bcd_to_dec(uint8_t bcd) {return (bcd >> 4) * 10 + (bcd & 0x0F);
}int main() {uint8_t sec, min, hour, day, month, year;ds3231_get_time(&sec, &min, &hour, &day, &month, &year);printf("20%02d-%02d-%02d %02d:%02d:%02d\n",bcd_to_dec(year), bcd_to_dec(month), bcd_to_dec(day),bcd_to_dec(hour), bcd_to_dec(min), bcd_to_dec(sec));return 0;
}
2. 通過網絡協議(NTP)獲取時間
適用于?ESP8266/ESP32?等帶網絡功能的單片機。
C 語言代碼示例(基于 ESP-IDF)
#include <stdio.h>
#include <time.h>
#include "esp_sntp.h"
#include "esp_wifi.h"
#include "nvs_flash.h"void initialize_sntp() {sntp_setoperatingmode(SNTP_OPMODE_POLL);sntp_setservername(0, "pool.ntp.org");sntp_init();
}void print_local_time() {time_t now;struct tm timeinfo;time(&now);localtime_r(&now, &timeinfo);printf("當前時間: %04d-%02d-%02d %02d:%02d:%02d\n",timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}void app_main() {nvs_flash_init();wifi_init_sta(); // 假設已實現 WiFi 連接initialize_sntp();while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) {vTaskDelay(1000 / portTICK_PERIOD_MS);}print_local_time();
}
3. 通過 GPS 模塊獲取時間
解析 GPS 模塊輸出的?NMEA 協議(如?$GPRMC
?語句)中的 UTC 時間。
C 語言-解析GPRMC語句代碼示例
#include <string.h>// 解析 GPRMC 語句中的時間(格式:$GPRMC,hhmmss.ss,A,ddmm.mm,N,...*hh)
void parse_gprmc_time(const char *nmea, uint8_t *hour, uint8_t *min, uint8_t *sec) {char *token = strtok((char *)nmea, ",");for (int i = 0; i < 7; i++) token = strtok(NULL, ","); // 跳過前7字段if (token != NULL && strlen(token) >= 6) {*hour = (token[0] - '0') * 10 + (token[1] - '0');*min = (token[2] - '0') * 10 + (token[3] - '0');*sec = (token[4] - '0') * 10 + (token[5] - '0');}
}int main() {const char *gprmc = "$GPRMC,123519.00,A,4807.038,N,01131.000,E,022.4,084.4,230394,,,*6A";uint8_t hour, min, sec;parse_gprmc_time(gprmc, &hour, &min, &sec);printf("UTC 時間: %02d:%02d:%02d\n", hour, min, sec);return 0;
}
GNRMC語句獲取時間, 具體實現代碼可以參考上篇文章https://blog.csdn.net/kivenx/article/details/147441407?fromshare=blogdetail&sharetype=blogdetail&sharerId=147441407&sharerefer=PC&sharesource=kivenx&sharefrom=from_link
4. 手動設置時間(無外部模塊)
如果無需高精度,可通過用戶輸入或編譯時間初始化:
#include <stdio.h>
#include <time.h>void set_manual_time() {struct tm manual_time = {.tm_year = 124, // 2024 - 1900.tm_mon = 4, // 5月(0-based).tm_mday = 1,.tm_hour = 12,.tm_min = 0,.tm_sec = 0};time_t t = mktime(&manual_time);printf("手動設置時間: %s", ctime(&t));
}
關鍵點總結
方法 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
RTC 模塊 | 高精度,掉電不丟失 | 需額外硬件 | 離線設備(如電子鐘) |
NTP | 自動同步網絡時間 | 依賴網絡 | 聯網設備(如 IoT) |
GPS | 全球可用,自帶定位 | 功耗高,需戶外信號 | 車載/戶外設備 |
手動設置 | 無需外部模塊 | 不精確,需人工干預 | 調試或簡單應用 |
根據需求選擇合適方案,并注意?時區轉換?和?數據格式處理(如 BCD 編碼)。