STM32电子万年历制作详解(RTC实战)

您所在的位置:网站首页 电脑万年历设置 STM32电子万年历制作详解(RTC实战)

STM32电子万年历制作详解(RTC实战)

2024-07-12 02:04| 来源: 网络整理| 查看: 265

 

首先附上效果图:

 

 

 

 

 

博主在重温了STM32的RTC后心血来潮,决定用RTC在做个万年历,其实也不算万年历,就是可以实时显示当前时间,而且大家也都知道,STM32自带的RTC的精度实在让人不好意思说,大概20分钟会有40S的误差,不过对于体验理解还是十分有帮助的,这个作品大概耗时2小时左右(汉字字库生成耗掉我大半精力呀T_T)所以我们一起来记录一下这个作品。但是由于精力有限,所以只写出部分主要问题和易错代码,若有同学需要完整工程请站内私聊。

 

 

首先是一个问题:器件选型,博主今天碰到一个很奇怪的事,相同的代码在不同型号的f103中不一定能运行(都配置好了),我也曾考虑是不是寄存器地址都不太对应,其实有这个可能,但是我往同型号烧相同代码有时候也会卡死(进不了main函数),遇到相同问题的同学可以抱团取暖,最终解决方法比较暴力,新建一个rct6的工程重新来T_T。

 

下面我们来把主要代码讲解一下:

int main() { delay_init(); //延时函数初始化 NVIC_Configuration(); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 LED_Init(); //LED端口初始化 OLED_Init(); //初始化OLED OLED_Clear() ; USART_Config(); Key_GPIO_Config(); /* 配置RTC秒中断优先级 */ RTC_NVIC_Config(); RTC_CheckAndConfig(&systmtime); while (1) { /* 每过1s 更新一次时间*/ if (TimeDisplay == 1) { /* 当前时间 */ Time_Display( RTC_GetCounter(),&systmtime); TimeDisplay = 0; } //按下按键,通过串口修改时间 if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) { struct rtc_time set_time; /*使用串口接收设置的时间,输入数字时注意末尾要加回车*/ Time_Regulate_Get(&set_time); /*用接收到的时间设置RTC*/ Time_Adjust(&set_time); //向备份寄存器写入标志 BKP_WriteBackupRegister(RTC_BKP_DRX, RTC_BKP_DATA); } } }

主函数就这么多,比较简单,就是初始化了一下OLED,配置了一下RTC,这些代码都比较好找到,下面来看看Time_display的具体代码:

/* * 函数名:Time_Display * 描述 :显示当前时间值 * 输入 :-TimeVar RTC计数值,单位为 s * 输出 :无 * 调用 :内部调用 */ void Time_Display(uint32_t TimeVar,struct rtc_time *tm) { static uint32_t FirstDisplay = 1; uint32_t BJ_TimeVar; uint8_t str[200]; // 字符串暂存 /* 把标准时间转换为北京时间*/ BJ_TimeVar =TimeVar + TIME_ZOOM; to_tm(BJ_TimeVar, tm);/*把定时器的值转换为北京时间*/ if((!tm->tm_hour && !tm->tm_min && !tm->tm_sec) || (FirstDisplay)) { GetChinaCalendar((u16)tm->tm_year, (u8)tm->tm_mon, (u8)tm->tm_mday, str); printf("\r\n 今天新历:%0.2d%0.2d,%0.2d,%0.2d", str[0], str[1], str[2], str[3]); OLED_ShowString(1,0,"RTC",16); OLED_ShowCHinese(28,0,0);//景 OLED_ShowCHinese(46,0,1);//园 OLED_ShowCHinese(64,0,2);//电 OLED_ShowCHinese(82,0,3);//子 OLED_ShowString(1,2,"Design by ZF",16); // OLED_ShowCHinese(88,0,5);//科 // OLED_ShowCHinese(104,0,6);//技 GetChinaCalendarStr((u16)tm->tm_year,(u8)tm->tm_mon,(u8)tm->tm_mday,str); printf("\r\n 今天农历:%s\r\n", str); if(GetJieQiStr((u16)tm->tm_year, (u8)tm->tm_mon, (u8)tm->tm_mday, str)) printf("\r\n 今天农历:%s\r\n", str); FirstDisplay = 0; } /* 输出时间戳,公历时间 */ printf(" UNIX时间戳 = %d 当前时间为: %d年(%s年) %d月 %d日 (星期%s) %0.2d:%0.2d:%0.2d\r",TimeVar, tm->tm_year, zodiac_sign[(tm->tm_year-3)%12], tm->tm_mon, tm->tm_mday, WEEK_STR[tm->tm_wday], tm->tm_hour, tm->tm_min, tm->tm_sec); OLED_ShowNum(0,4,tm->tm_year,4,16); OLED_ShowCHinese(33,4,4);//电 OLED_ShowNum(51,4,tm->tm_mon,2,16); OLED_ShowCHinese(69,4,5);//电 OLED_ShowNum(87,4,tm->tm_mday,2,16); OLED_ShowCHinese(105,4,6);//电 OLED_ShowNum(0,6,tm->tm_hour,2,16); OLED_ShowString(18,6,":",16); OLED_ShowNum(37,6,tm->tm_min,2,16); OLED_ShowString(55,6,":",16); OLED_ShowNum(74,6,tm->tm_sec,2,16); }

用过OLED的都知道,这块代码就是把寄存器的数值读出来,通过计算换算为我们的时间(PS:理论数字上线,时间计时最多为130年,所以从0年到2018年是不可能的,我们把1970年定义为元年,所以大家会发现很少有万年历会比1970年更早)

 

下面就是用串口来设置时间(大部分开发板上有个纽扣电池的位置,RTC在不掉电的情况下可以一直计数,一旦掉电就会重新再来,类似于电脑主板和单片机上的电池位置,都是用来获取时间的,所以上面有没有电池都无所谓,博主做的这个用的是最小系统板,没有电池位置,只能一直接口插电了,一旦断电就得重新设置。。。反正是个玩具。。)

/* * 函数名:Time_Regulate_Get * 描述 :保存用户使用串口设置的时间, * 以便后面转化成时间戳存储到RTC 计数寄存器中。 * 输入 :用于读取RTC时间的结构体指针 * 注意 :在串口调试助手输入时,输入完数字要加回车 */ void Time_Regulate_Get(struct rtc_time *tm) { uint32_t temp_num = 0; uint8_t day_max=0 ; printf("\r\n=========================设置时间=================="); do { printf("\r\n 请输入年份(Please Set Years),范围[1970~2038],输入字符后请加回车:"); scanf("%d",&temp_num); if(temp_num 2038) { printf("\r\n 您输入的数字是:%d,不符合要求",temp_num); } else { printf("\n\r 年份被设置为: %d\n\r", temp_num); tm->tm_year = temp_num; break; } }while(1); do { printf("\r\n 请输入月份(Please Set Months):范围[1~12],输入字符后请加回车:"); scanf("%d",&temp_num); if(temp_num 12) { printf("\r\n 您输入的数字是:%d,不符合要求",temp_num); } else { printf("\n\r 月份被设置为: %d\n\r", temp_num); tm->tm_mon = temp_num; break; } }while(1); /*根据月份计算最大日期*/ switch(tm->tm_mon) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: day_max = 31; break; case 4: case 6: case 9: case 11: day_max = 30; break; case 2: /*计算闰年*/ if((tm->tm_year%4==0) && ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && (tm->tm_mon>2)) { day_max = 29; } else { day_max = 28; } break; } do { printf("\r\n 请输入日期(Please Set Months),范围[1~%d],输入字符后请加回车:",day_max); scanf("%d",&temp_num); if(temp_num day_max) { printf("\r\n 您输入的数字是:%d,不符合要求",temp_num); } else { printf("\n\r 日期被设置为: %d\n\r", temp_num); tm->tm_mday = temp_num; break; } }while(1); do { printf("\r\n 请输入时钟(Please Set Hours),范围[0~23],输入字符后请加回车:"); scanf("%d",&temp_num); if( temp_num >23) { printf("\r\n 您输入的数字是:%d,不符合要求",temp_num); } else { printf("\n\r 时钟被设置为: %d\n\r", temp_num); tm->tm_hour = temp_num; break; } }while(1); do { printf("\r\n 请输入分钟(Please Set Minutes),范围[0~59],输入字符后请加回车:"); scanf("%d",&temp_num); if( temp_num >59) { printf("\r\n 您输入的数字是:%d,不符合要求",temp_num); } else { printf("\n\r 分钟被设置为: %d\n\r", temp_num); tm->tm_min = temp_num; break; } }while(1); do { printf("\r\n 请输入秒钟(Please Set Seconds),范围[0~59],输入字符后请加回车:"); scanf("%d",&temp_num); if( temp_num >59) { printf("\r\n 您输入的数字是:%d,不符合要求",temp_num); } else { printf("\n\r 秒钟被设置为: %d\n\r", temp_num); tm->tm_sec = temp_num; break; } }while(1);

上面的代码就是我们一旦断电后需要设置时间,具体的使用不再截图。

下一篇我们来讲讲这个OLED的汉字显示,链接后续附上:点击打开链接

要工程的同学比较多哈,这里贴上下载链接:https://download.csdn.net/download/VCA821/12899468



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3