/* * 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 #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 // ///** // * @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 // * 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 \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 ); // ///** // * @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 // /** // * @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);