| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- /*
- * Copyright (c) 2024, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2024-xx-xx user first version
- */
- #define DBG_TAG "rtc"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include "rtc.h"
- /* RTC句柄 */
- RTC_HandleTypeDef hrtc;
- /* 时间打印线程相关 */
- #define RTC_PRINT_THREAD_STACK_SIZE (256 + 128) // 太大没必要
- #define RTC_PRINT_THREAD_PRIORITY 5
- #define RTC_PRINT_THREAD_TICK 10
- static rt_thread_t rtc_print_tid = RT_NULL;
- struct rtc_time time_now = {0};
- RTC_DateTypeDef GetDate;
- RTC_TimeTypeDef GetTime;
- /**
- * @brief 判断是否为闰年
- * @param year 年份(2000-2099)
- * @return 1:闰年, 0:平年
- */
- static rt_uint8_t is_leap_year(rt_uint16_t year)
- {
- if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
- return 1;
- return 0;
- }
- /**
- * @brief 将年月日转换为星期
- * @param year 年份(2000-2099)
- * @param month 月份(1-12)
- * @param date 日期(1-31)
- * @return 星期(0=周日, 1=周一, ..., 6=周六)
- */
- static rt_uint8_t calc_weekday(rt_uint16_t year, rt_uint8_t month, rt_uint8_t date)
- {
- /* Zeller公式计算星期 */
- rt_int16_t y = year;
- rt_int8_t m = month;
- rt_int8_t d = date;
- rt_int8_t c = y / 100;
- y = y % 100;
- if (m < 3)
- {
- m += 12;
- y -= 1;
- }
- rt_int8_t w = (y + y / 4 + c / 4 - 2 * c + 26 * (m + 1) / 10 + d - 1) % 7;
- return (w < 0) ? (w + 7) : w;
- }
- /**
- * @brief 检查日期时间是否有效
- * @param time 时间结构体指针
- * @return 1:有效, 0:无效
- */
- static rt_uint8_t is_time_valid(const struct rtc_time *time)
- {
- const rt_uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- rt_uint8_t max_day;
- if (time->year < 2000 || time->year > 2099)
- return 0;
- if (time->month < 1 || time->month > 12)
- return 0;
- max_day = days_in_month[time->month - 1];
- if (time->month == 2 && is_leap_year(time->year))
- max_day = 29;
- if (time->date < 1 || time->date > max_day)
- return 0;
- if (time->hour > 23)
- return 0;
- if (time->minute > 59)
- return 0;
- if (time->second > 59)
- return 0;
- return 1;
- }
- /**
- * @brief 将BCD码转换为二进制
- */
- static rt_uint8_t bcd_to_bin(rt_uint8_t bcd)
- {
- return ((bcd >> 4) * 10) + (bcd & 0x0F);
- }
- /**
- * @brief 将二进制转换为BCD码
- */
- static rt_uint8_t bin_to_bcd(rt_uint8_t bin)
- {
- return ((bin / 10) << 4) | (bin % 10);
- }
- /**
- * @brief 获取RTC时间
- * @param time 时间结构体指针
- * @return 0:成功, -1:失败
- */
- int rtc_get_time(struct rtc_time *time)
- {
- RTC_DateTypeDef sdate;
- RTC_TimeTypeDef stim;
- if (time == RT_NULL)
- return -1;
- /* 获取时间和日期 - 必须先获取时间再获取日期 */
- HAL_RTC_GetTime(&hrtc, &stim, RTC_FORMAT_BCD);
- HAL_RTC_GetDate(&hrtc, &sdate, RTC_FORMAT_BCD);
- /* 转换为标准格式 */
- time->year = 2000 + bcd_to_bin(sdate.Year);
- time->month = bcd_to_bin(sdate.Month);
- time->date = bcd_to_bin(sdate.Date);
- time->weekday = bcd_to_bin(sdate.WeekDay);
- time->hour = bcd_to_bin(stim.Hours);
- time->minute = bcd_to_bin(stim.Minutes);
- time->second = bcd_to_bin(stim.Seconds);
- return 0;
- }
- /**
- * @brief 设置RTC时间
- * @param time 时间结构体指针
- * @return 0:成功, -1:失败
- */
- int rtc_set_time(struct rtc_time *time)
- {
- RTC_DateTypeDef sdate;
- RTC_TimeTypeDef stim;
- if (time == RT_NULL || !is_time_valid(time))
- return -1;
- /* 填充日期结构体 */
- sdate.Year = bin_to_bcd(time->year - 2000);
- sdate.Month = bin_to_bcd(time->month);
- sdate.Date = bin_to_bcd(time->date);
- sdate.WeekDay = bin_to_bcd(calc_weekday(time->year, time->month, time->date));
- /* 填充时间结构体 */
- stim.Hours = bin_to_bcd(time->hour);
- stim.Minutes = bin_to_bcd(time->minute);
- stim.Seconds = bin_to_bcd(time->second);
- stim.TimeFormat = RTC_HOURFORMAT12_AM;
- stim.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
- stim.StoreOperation = RTC_STOREOPERATION_RESET;
- /* 设置日期和时间 */
- if (HAL_RTC_SetDate(&hrtc, &sdate, RTC_FORMAT_BCD) != HAL_OK)
- return -1;
- if (HAL_RTC_SetTime(&hrtc, &stim, RTC_FORMAT_BCD) != HAL_OK)
- return -1;
- return 0;
- }
- /**
- * @brief 将RTC时间转换为字符串
- * @param time 时间结构体指针
- * @param buf 缓冲区
- * @param size 缓冲区大小
- */
- void rtc_time_to_str(const struct rtc_time *time, char *buf, rt_size_t size)
- {
- const char *week_str[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
- if (time == RT_NULL || buf == RT_NULL || size < 32)
- return;
- rt_snprintf(buf, size, "%04d-%02d-%02d %02d:%02d:%02d %s",
- time->year, time->month, time->date,
- time->hour, time->minute, time->second,
- week_str[time->weekday % 7]);
- }
- /**
- * @brief 写入默认时间到RTC
- */
- static void rtc_write_default_time(void)
- {
- struct rtc_time time;
- time.year = 2000 + RTC_DEFAULT_YEAR;
- time.month = RTC_DEFAULT_MONTH;
- time.date = RTC_DEFAULT_DATE;
- time.hour = RTC_DEFAULT_HOUR;
- time.minute = RTC_DEFAULT_MINUTE;
- time.second = RTC_DEFAULT_SECOND;
- if (rtc_set_time(&time) == 0)
- {
- LOG_I("RTC default time set: %04d-%02d-%02d %02d:%02d:%02d",
- time.year, time.month, time.date,
- time.hour, time.minute, time.second);
- }
- else
- {
- LOG_E("RTC set default time failed!");
- }
- }
- /**
- * @brief 时间打印线程入口函数
- * @param parameter 线程参数
- */
- static void rtc_print_thread_entry(void *parameter)
- {
- char time_str[32];
- static char once = 3;
- while (1)
- {
- /* 获取当前时间 */
- if (rtc_get_time(&time_now) == 0)
- {
- GetDate.Year = time_now.year - 2000;
- GetDate.Month = time_now.month;
- GetDate.Date = time_now.date;
- GetTime.Hours = time_now.hour;
- GetTime.Minutes = time_now.minute;
- GetTime.Seconds = time_now.second;
- rtc_time_to_str(&time_now, time_str, sizeof(time_str));
- if (once)
- {
- once--;
- LOG_D(" Current Time: %s\n", time_str);
- }
- }
- else
- {
- LOG_I(" Get time failed!\n");
- }
- /* 延时1秒 */
- rt_thread_mdelay(1000);
- }
- }
- /**
- * @brief 创建时间打印线程
- */
- static void rtc_print_thread_create(void)
- {
- rtc_print_tid = rt_thread_create("rtc_s",
- rtc_print_thread_entry,
- RT_NULL,
- RTC_PRINT_THREAD_STACK_SIZE,
- RTC_PRINT_THREAD_PRIORITY,
- RTC_PRINT_THREAD_TICK);
- if (rtc_print_tid != RT_NULL)
- {
- rt_thread_startup(rtc_print_tid);
- //LOG_I("RTC print thread started");
- }
- else
- {
- LOG_E("RTC print thread create failed!");
- }
- }
- /**
- * @brief RTC硬件初始化
- * @return 0:成功, -1:失败
- */
- int rt_hw_rtc_init(void)
- {
- rt_uint32_t init_flag;
- /* 使能PWR时钟和备份域访问 */
- __HAL_RCC_PWR_CLK_ENABLE();
- HAL_PWR_EnableBkUpAccess();
- /* 使能RTC时钟 */
- __HAL_RCC_RTC_ENABLE();
- /* 配置RTC */
- hrtc.Instance = RTC;
- hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
- /* LSE = 32.768kHz, 分频计算:
- * 32768 / 128 / 256 = 1Hz
- */
- hrtc.Init.AsynchPrediv = 127; /* 128分频 (127 + 1) */
- hrtc.Init.SynchPrediv = 255; /* 256分频 (255 + 1) */
- hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
- hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
- hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
- /* 读取备份寄存器中的初始化标记 */
- init_flag = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_FLAG);
- if (init_flag != RTC_INIT_FLAG)
- {
- /* 首次初始化或备份域丢失 */
- LOG_I("RTC first init or backup domain reset detected");
- /* 初始化RTC */
- if (HAL_RTC_Init(&hrtc) != HAL_OK)
- {
- LOG_E("RTC HAL_Init failed!");
- return -1;
- }
- /* 写入默认时间 */
- rtc_write_default_time();
- /* 在备份寄存器中写入初始化标记 */
- HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_FLAG, RTC_INIT_FLAG);
- LOG_I("RTC initialized with default time");
- }
- else
- {
- /* 已初始化过,直接读取当前时间 */
- struct rtc_time time;
- char time_str[32];
- /* 只需初始化HAL句柄,时间和日期已保持 */
- if (HAL_RTC_Init(&hrtc) != HAL_OK)
- {
- LOG_E("RTC HAL_Init failed!");
- return -1;
- }
- /* 读取并显示当前时间 */
- if (rtc_get_time(&time) == 0)
- {
- rtc_time_to_str(&time, time_str, sizeof(time_str));
- LOG_I("RTC already initialized, current time: %s", time_str);
- }
- }
- /* 创建时间打印线程 */
- rtc_print_thread_create();
- return 0;
- }
- /**
- * @brief 自动初始化 - 在系统启动时自动调用
- * 使用INIT_DEVICE_EXPORT确保在设备初始化阶段执行
- */
- INIT_ENV_EXPORT(rt_hw_rtc_init);
- // #ifdef RT_USING_FINSH
- // #include <finsh.h>
- //
- ///**
- // * @brief Finsh/MSH命令: 获取RTC时间
- // */
- // static int rtc_get(int argc, char **argv)
- //{
- // struct rtc_time time;
- // char time_str[32];
- //
- // (void)argc;
- // (void)argv;
- //
- // if (rtc_get_time(&time) == 0)
- // {
- // rtc_time_to_str(&time, time_str, sizeof(time_str));
- // rt_kprintf("RTC Time: %s\n", time_str);
- // return 0;
- // }
- //
- // rt_kprintf("Get RTC time failed!\n");
- // return -1;
- //}
- // MSH_CMD_EXPORT(rtc_get, Get RTC current time);
- //
- ///**
- // * @brief Finsh/MSH命令: 设置RTC时间
- // * Usage: rtc_set <year> <month> <date> <hour> <minute> <second>
- // * Example: rtc_set 2024 1 15 12 30 0
- // */
- // static int rtc_set(int argc, char **argv)
- //{
- // struct rtc_time time;
- //
- // if (argc != 7)
- // {
- // rt_kprintf("Usage: rtc_set <year> <month> <date> <hour> <minute> <second>\n");
- // rt_kprintf("Example: rtc_set 2024 1 15 12 30 0\n");
- // return -1;
- // }
- //
- // time.year = (rt_uint16_t)atoi(argv[1]);
- // time.month = (rt_uint8_t)atoi(argv[2]);
- // time.date = (rt_uint8_t)atoi(argv[3]);
- // time.hour = (rt_uint8_t)atoi(argv[4]);
- // time.minute = (rt_uint8_t)atoi(argv[5]);
- // time.second = (rt_uint8_t)atoi(argv[6]);
- //
- // if (rtc_set_time(&time) == 0)
- // {
- // rt_kprintf("RTC time set to: %04d-%02d-%02d %02d:%02d:%02d\n",
- // time.year, time.month, time.date,
- // time.hour, time.minute, time.second);
- // return 0;
- // }
- //
- // rt_kprintf("Set RTC time failed!\n");
- // return -1;
- //}
- // MSH_CMD_EXPORT(rtc_set, Set RTC time: rtc_set <year> <month> <date> <hour> <minute> <second>);
- //
- ///**
- // * @brief Finsh/MSH命令: 重置RTC为默认时间
- // */
- // static int rtc_reset(int argc, char **argv)
- //{
- // (void)argc;
- // (void)argv;
- //
- // /* 清除备份寄存器标记,下次重启将使用默认时间 */
- // HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_FLAG, 0);
- //
- // /* 立即写入默认时间 */
- // rtc_write_default_time();
- //
- // /* 重新写入标记 */
- // HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_FLAG, RTC_INIT_FLAG);
- //
- // rt_kprintf("RTC reset to default time\n");
- // return 0;
- //}
- // MSH_CMD_EXPORT(rtc_reset, Reset RTC to default time);
- //
- // #endif /* RT_USING_FINSH */
- void Get_Time_Stampe(struct TM *tm)
- {
- tm->tm_year = time_now.year - 2000;
- tm->tm_mon = time_now.month;
- tm->tm_mday = time_now.date;
- tm->tm_hour = time_now.hour;
- tm->tm_min = time_now.minute;
- tm->tm_sec = time_now.second;
- }
- // extern char ef_cfged;
- // #include <easyflash.h>
- // /**
- // * @brief 记录ecode
- // *
- // * @param code code值
- // * @param para 参数
- // * @param deta 描述
- // *
- // * V2.0 241030
- // */
- // void Recode_e_code(ecode_list code, uint16_t para, char *detail)
- // {
- // if (ef_cfged == 1)
- // {
- // Record ecode = {0};
- // uint32_t used = ef_log_get_used_size();
- // uint32_t cnt = used / sizeof(Record);
- // if (cnt > 0)
- // {
- // ef_log_read((cnt - 1) * sizeof(Record), (uint32_t *)&ecode, sizeof(Record));
- // }
- // LOG_I("e_code used %d cnt %d\n", used, cnt);
- // if ((used + sizeof(Record) + 0x0C) >= LOG_AREA_SIZE)
- // {
- // ef_log_clean();
- // Get_Time_Stampe(&ecode.Time_Stampe);
- // ecode.para = code;
- // ecode.code = e_clearecode;
- // ecode.num++;
- // snprintf(ecode.detail, sizeof(ecode.detail), "ecode存满后清空"); // 本次信息
- // }
- // else
- // {
- // Get_Time_Stampe(&ecode.Time_Stampe);
- // ecode.para = para;
- // ecode.code = code;
- // ecode.num++;
- // snprintf(ecode.detail, sizeof(ecode.detail), "%s", detail); // 本次信息
- // }
- // LOG_W("e_code SET %d c %d p %d", ef_log_write((uint32_t *)&ecode, sizeof(Record)), code, para);
- // // rt_kprintf("%d %d %d %s\n\n" , ecode.num , ecode.code , ecode.para , ecode.detail);
- // }
- // }
- // /**
- // * @brief printf_ecode
- // *
- // */
- // void printf_ecode(void)
- // {
- // RTC_DateTypeDef r_date = {0};
- // RTC_TimeTypeDef r_time = {0};
- // uint32_t used = ef_log_get_used_size();
- // uint32_t cnt = used / sizeof(Record);
- // LOG_I("e_code used %d cnt %d\n", used, cnt);
- // Record ecode = {0};
- // for (uint32_t a = 0; a < cnt; a++)
- // {
- // if (ef_log_read(a * sizeof(Record), (uint32_t *)&ecode, sizeof(Record)) == EF_NO_ERR)
- // {
- // LOG_I("%d - %02d/%02d/%02d %02d:%02d:%02d:(%d-%d) %s",
- // ecode.num,
- // ecode.Time_Stampe.tm_year, ecode.Time_Stampe.tm_mon, ecode.Time_Stampe.tm_mday,
- // ecode.Time_Stampe.tm_hour, ecode.Time_Stampe.tm_min, ecode.Time_Stampe.tm_sec,
- // ecode.code, ecode.para, ecode.detail);
- // memset(&ecode, 0, sizeof(ecode));
- // }
- // else
- // {
- // LOG_I("ef_log_read %d fail\n", a);
- // }
- // }
- // }
- // void recode_test(void)
- // {
- // Recode_e_code(e_test_fun, 6, "3-6是一条msh测试数据123456789");
- // }
- // MSH_CMD_EXPORT(recode_test, recode_test);
- // void pre(void)
- // {
- // printf_ecode();
- // }
- // MSH_CMD_EXPORT(pre, pre);
- // void delecode(void)
- // {
- // ef_log_clean();
- // }
- // MSH_CMD_EXPORT(delecode, delecode);
|