首先附上效果图:

博主在重温了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 <1970 || 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 <1 || 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 <1 || 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

STM32电子万年历制作详解(RTC实战)相关推荐

  1. STM32汉字字模制作详解(OLED实战)

    之前没有用过OLED来显示汉字,今天心血来潮想做一个RTC的万年历,想着就用OLED来用汉字显示一下,(stm32万年历制作链接:点击打开链接),之前想的确实很简单,不就是制作字模然后调用显示嘛!但是 ...

  2. STM32—— AHB、APB详解

     STM32-- AHB.APB详解 2016-07-14 20:35 590人阅读 评论(0) 收藏 举报 本文章已收录于: 版权声明:本文为博主原创文章,未经博主允许不得转载. 一.概括 首先 ...

  3. STM32开发 -- 低功耗模式详解(2)

    如需转载请注明出处:https://juyou.blog.csdn.net/article/details/98631012 上一篇文章 STM32开发 – 低功耗模式详解(1) 简单的总结了一下低功 ...

  4. STM32开发 -- 低功耗模式详解

    很多单片机都有低功耗模式,STM32 也不例外.当 CPU 不需继续运行时,可以利用多个低功耗模式来节省功耗. 这部分不是我负责,但是也是有必要看一下的. 参看: STM32F1开发指南-库函数版本_ ...

  5. 如何用c语言编写stm32的程序吗,STM32入门C语言详解

    <STM32入门C语言详解>由会员分享,可在线阅读,更多相关<STM32入门C语言详解(6页珍藏版)>请在人人文库网上搜索. 1.最新 料推荐阅读 flash : 芯片内部存储 ...

  6. t检验特征筛选详解及实战

    t检验特征筛选详解及实战 数据的种类 我们都知道,一般数据可以分为两类,即定量数据(数值型数据)和定性数据(非数值型数据),定性数据很好理解,例如人的性别,姓名这些都是定性数据.定量数据可以分为以下几 ...

  7. 使用pickle保存机器学习模型详解及实战(pickle、joblib)

    使用pickle保存机器学习模型详解及实战 pickle模块实现了用于序列化和反序列化Python对象结构的二进制协议. "Pickling"是将Python对象层次结构转换为字节 ...

  8. 数据不平衡(class_weight、评估方法、上采样、下采样)、详解及实战

    数据不平衡(class_weight.评估方法.上采样.下采样).详解及实战 核心学习函数方法: np.random.choice() np.where() np.concatenate() np.v ...

  9. 标称变量(Categorical Features)或者分类变量(Categorical Features​​​​​​​)缺失值填补、详解及实战

    标称变量(Categorical Features)或者分类变量(Categorical Features)缺失值填补.详解及实战 核心学习函数或者方法: KNeighborsClassifier() ...

最新文章

  1. python 回溯法 01背包问题_Python回溯法解决01背包问题
  2. 经典算法题每日演练——第二十一题 十字链表
  3. 直接获取submission结果
  4. nginx 直接在配置文章中设置日志分割
  5. ASP.NET MVC 入门4、Controller与Action
  6. 5g虚拟技术旅游_5G造就文旅新时代,驴迹科技探索智慧出行创新领域
  7. Android 系统(208)---OTA升级后概率性出现卡在开机动画界面无法退出
  8. 演讲(2)----十大范畴法
  9. VC++删除浮动工具条中“关闭”按钮
  10. ASP.NET控件在2.0和1.1下运行的一个微小的差别
  11. 串口通讯WaitCommEvent 、GetLastError、ClearCommError、...
  12. 前后端分离实现文件下载功能
  13. A股主要指数的市盈率(PE)估值高度
  14. 锂电池充电器设计技巧:从太阳能电池获取更大功率
  15. canvas制作印章
  16. css之如何清除浮动
  17. 计算机应用基础实训报告excel,excel实验报告模板
  18. verilog always块中case相关问题
  19. 虚拟的超级计算机和云计算,概念PK:云计算与高性能计算(HPC)
  20. ISO 14443 非接触式射频卡标准学习

热门文章

  1. 认知升维: 道、法、术、器、势
  2. 通俗理解“极大似然估计”
  3. 英语中有些单词缩写后面为什么要加个点
  4. 城市轨道交通的GoA
  5. 记录自己第一个java学习博客
  6. 他华丽转身,却永远留下了六个字——在中国,为中国
  7. 梅州英才外语学校计算机,梅州英才外语学校
  8. 单片机原理及应用知识总结(持续更新)
  9. 《Adobe Flash CS6中文版经典教程》——1.7 使用“工具”面板
  10. 消息称华为麒麟 A2 处理器已有量产能力,海思将率先在可穿戴设备领域回归