STM32F103ZET6之RTC实时时钟实验


文章目录

  • STM32F103ZET6之RTC实时时钟实验
  • 前言
  • 一、简介
  • 二、相关寄存器及配置过程
  • 三、程序源码
    • 1.rtc.h
    • 2.rtc.c
    • 3.main.c
  • 实验结果
  • 总结

前言

对于STM32的学习可分为3个版本。
1.寄存器版本
2.库函数版本
3.HAL库版本
由于个人原因,选择库函数版本来进行STM32的学习。


提示:软件安装等问题,不进行讲解!!!

一、简介

STM32 的实时时钟(RTC)是一个独立的定时器。 STM32 的 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC 模块和时钟配置系统(RCC_BDCR 寄存器)是在后备区域,即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变。但是在系统复位后,会自动禁止访问后备寄存器和 RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前, 先要取消备份区域(BKP)写保护。

原理图如下:

二、相关寄存器及配置过程



三、程序源码

1.rtc.h

代码如下:

#ifndef __RTC_H
#define __RTC_H
//时间结构体
typedef struct
{vu8 hour;vu8 min;vu8 sec;          //公历日月年周vu16 w_year;vu8  w_month;vu8  w_date;vu8  week;
}_calendar_obj;
extern _calendar_obj calendar;  //日历结构体extern u8 const mon_table[12];   //月份日期数据表
void Disp_Time(u8 x,u8 y,u8 size);//在制定位置开始显示时间
void Disp_Week(u8 x,u8 y,u8 size,u8 lang);//在指定位置显示星期
u8 RTC_Init(void);        //初始化RTC,返回0,失败;1,成功;
u8 Is_Leap_Year(u16 year);//平年,闰年判断
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);
u8 RTC_Get(void);         //更新时间
u8 RTC_Get_Week(u16 year,u8 month,u8 day);
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//设置时间
#endif

2.rtc.c

代码如下:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "rtc.h"          _calendar_obj calendar;//时钟结构体 static void RTC_NVIC_Config(void)
{   NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;     //RTC全局中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级1位,从优先级3位NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //先占优先级0位,从优先级4位NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //使能该通道中断NVIC_Init(&NVIC_InitStructure);        //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
//BKP->DR1用于保存是否第一次配置的设置
//返回0:正常
//其他:错误代码u8 RTC_Init(void)
{//检查是不是第一次配置时钟u8 temp=0;RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);  //使能PWR和BKP外设时钟   PWR_BackupAccessCmd(ENABLE);  //使能后备寄存器访问  if (BKP_ReadBackupRegister(BKP_DR1) != 0x5051)        //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎{                 BKP_DeInit();  //复位备份区域    RCC_LSEConfig(RCC_LSE_ON);  //设置外部低速晶振(LSE),使用外设低速晶振while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)  //检查指定的RCC标志位设置与否,等待低速晶振就绪{temp++;delay_ms(10);}if(temp>=250)return 1;//初始化时钟失败,晶振有问题     RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);     //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    RCC_RTCCLKCmd(ENABLE);    //使能RTC时钟  RTC_WaitForLastTask();   //等待最近一次对RTC寄存器的写操作完成RTC_WaitForSynchro();      //等待RTC寄存器同步  RTC_ITConfig(RTC_IT_SEC, ENABLE);     //使能RTC秒中断RTC_WaitForLastTask();    //等待最近一次对RTC寄存器的写操作完成RTC_EnterConfigMode();/// 允许配置 RTC_SetPrescaler(32767); //设置RTC预分频的值RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成RTC_Set(2021,1,26,11,40,00);  //设置时间   RTC_ExitConfigMode(); //退出配置模式  BKP_WriteBackupRegister(BKP_DR1, 0X5051);   //向指定的后备寄存器中写入用户程序数据}else//系统继续计时{RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成RTC_ITConfig(RTC_IT_SEC, ENABLE);  //使能RTC秒中断RTC_WaitForLastTask();    //等待最近一次对RTC寄存器的写操作完成}RTC_NVIC_Config();//RCT中断分组设置                              RTC_Get();//更新时间   return 0; //ok}
//RTC时钟中断
//每秒触发一次
//extern u16 tcnt;
void RTC_IRQHandler(void)
{        if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断{                          RTC_Get();//更新时间   }if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断{RTC_ClearITPendingBit(RTC_IT_ALR);       //清闹钟中断     RTC_Get();              //更新时间   printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间   }                                                RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);       //清闹钟中断RTC_WaitForLastTask();
}
//判断是否是闰年函数
//月份   1  2  3  4  5  6  7  8  9  10 11 12
//闰年   31 29 31 30 31 30 31 31 30 31 30 31
//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 year)
{             if(year%4==0) //必须能被4整除{ if(year%100==0) { if(year%400==0)return 1;//如果以00结尾,还要能被400整除     else return 0;   }else return 1;   }else return 0;
}
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//返回值:0,成功;其他:错误代码.
//月份数据表
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
//平年的月份日期表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{u16 t;u32 seccount=0;if(syear<1970||syear>2099)return 1;       for(t=1970;t<syear;t++)    //把所有年份的秒钟相加{if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数else seccount+=31536000;           //平年的秒钟数}smon-=1;for(t=0;t<smon;t++)      //把前面月份的秒钟数相加{seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数     }seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 seccount+=(u32)hour*3600;//小时秒钟数seccount+=(u32)min*60;     //分钟秒钟数seccount+=sec;//最后的秒钟加上去RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);  //使能PWR和BKP外设时钟  PWR_BackupAccessCmd(ENABLE);   //使能RTC和后备寄存器访问 RTC_SetCounter(seccount);   //设置RTC计数器的值RTC_WaitForLastTask();  //等待最近一次对RTC寄存器的写操作完成   return 0;
}//初始化闹钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//syear,smon,sday,hour,min,sec:闹钟的年月日时分秒
//返回值:0,成功;其他:错误代码.
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{u16 t;u32 seccount=0;if(syear<1970||syear>2099)return 1;       for(t=1970;t<syear;t++)    //把所有年份的秒钟相加{if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数else seccount+=31536000;           //平年的秒钟数}smon-=1;for(t=0;t<smon;t++)      //把前面月份的秒钟数相加{seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数     }seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 seccount+=(u32)hour*3600;//小时秒钟数seccount+=(u32)min*60;     //分钟秒钟数seccount+=sec;//最后的秒钟加上去              //设置时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);  //使能PWR和BKP外设时钟   PWR_BackupAccessCmd(ENABLE);  //使能后备寄存器访问  //上面三步是必须的!RTC_SetAlarm(seccount);RTC_WaitForLastTask();   //等待最近一次对RTC寄存器的写操作完成   return 0;
}//得到当前的时间
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{static u16 daycnt=0;u32 timecount=0; u32 temp=0;u16 temp1=0;     timecount=RTC_GetCounter();   temp=timecount/86400;   //得到天数(秒钟数对应的)if(daycnt!=temp)//超过一天了{     daycnt=temp;temp1=1970; //从1970年开始while(temp>=365){              if(Is_Leap_Year(temp1))//是闰年{if(temp>=366)temp-=366;//闰年的秒钟数else {temp1++;break;}  }else temp-=365;      //平年 temp1++;  }   calendar.w_year=temp1;//得到年份temp1=0;while(temp>=28)//超过了一个月{if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份{if(temp>=29)temp-=29;//闰年的秒钟数else break; }else {if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年else break;}temp1++;  }calendar.w_month=temp1+1; //得到月份calendar.w_date=temp+1;     //得到日期 }temp=timecount%86400;          //得到秒钟数        calendar.hour=temp/3600;        //小时calendar.min=(temp%3600)/60;   //分钟    calendar.sec=(temp%3600)%60;   //秒钟calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期   return 0;
}
//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//输入参数:公历年月日
//返回值:星期号
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{   u16 temp2;u8 yearH,yearL;yearH=year/100;   yearL=year%100; // 如果为21世纪,年份数加100  if (yearH>19)yearL+=100;// 所过闰年数只算1900年之后的  temp2=yearL+yearL/4;temp2=temp2%7; temp2=temp2+day+table_week[month-1];if (yearL%4==0&&month<3)temp2--;return(temp2%7);
}

3.main.c

代码如下:

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "usmart.h"
#include "rtc.h" int main(void){   u8 t=0;   delay_init();            //延时函数初始化    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级uart_init(115200);     //串口初始化为115200LED_Init();                //LED端口初始化LCD_Init();              usmart_dev.init(SystemCoreClock/1000000);   //初始化USMART RTC_Init();             //RTC初始化POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(60,50,200,16,16,"Elite STM32"); LCD_ShowString(60,70,200,16,16,"RTC TEST");   LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(60,110,200,16,16,"2015/1/14");      //显示时间POINT_COLOR=BLUE;//设置字体为蓝色LCD_ShowString(60,130,200,16,16,"    -  -  ");      LCD_ShowString(60,162,200,16,16,"  :  :  ");           while(1){                                   if(t!=calendar.sec){t=calendar.sec;LCD_ShowNum(60,130,calendar.w_year,4,16);                                    LCD_ShowNum(100,130,calendar.w_month,2,16);                                     LCD_ShowNum(124,130,calendar.w_date,2,16);     switch(calendar.week){case 0:LCD_ShowString(60,148,200,16,16,"Sunday   ");break;case 1:LCD_ShowString(60,148,200,16,16,"Monday   ");break;case 2:LCD_ShowString(60,148,200,16,16,"Tuesday  ");break;case 3:LCD_ShowString(60,148,200,16,16,"Wednesday");break;case 4:LCD_ShowString(60,148,200,16,16,"Thursday ");break;case 5:LCD_ShowString(60,148,200,16,16,"Friday   ");break;case 6:LCD_ShowString(60,148,200,16,16,"Saturday ");break;  }LCD_ShowNum(60,162,calendar.hour,2,16);                                     LCD_ShowNum(84,162,calendar.min,2,16);                                      LCD_ShowNum(108,162,calendar.sec,2,16);LED0=!LED0;}  delay_ms(10);                                 };  }

实验结果


总结

1.看完视频,一定自己写一遍程序。
2.烧写程序前,对程序进行分析,推理实验现象。
3.若实验现象与推理不一致,一定要认真分析程序。

STM32学习笔记(十九)RTC实时时钟实验相关推荐

  1. STM32学习心得十九:电容触摸按键实验及相关代码解读

    记录一下,方便以后翻阅~ 主要内容 1) 电容触摸按键原理: 2)部分实验代码解读. 实验内容 手触摸按键后,LED1灯翻转. 硬件原理图 上图,TPAD与STM_ADC用跳线帽相连,即TPAD与PA ...

  2. (实验15)单片机,STM32F4学习笔记,代码讲解【RTC实时时钟实验】【正点原子】【原创】

    文章目录 其它文章链接,独家吐血整理 实验现象 主程序 RTC初始化程序 代码讲解 其它文章链接,独家吐血整理 (实验3)单片机,STM32F4学习笔记,代码讲解[按键输入实验][正点原子][原创] ...

  3. 25 linux ndk 头文件_正点原子Linux第二十五章RTC实时时钟实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第二十五章RTC实时时钟实验 实时时钟是很常用的一个外设 ...

  4. 【正点原子STM32连载】 第二十七章 RTC实时时钟实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

  5. Polyworks脚本开发学习笔记(十九)-将数据对象与参考对象对齐的方法

    Polyworks脚本开发学习笔记(十九)-将数据对象与参考对象对齐的方法 把开发手册理了一遍,发现还有几个点没有记录下来,其中一个就是使用点对的粗对齐和使用参考目标的精确对齐.为了把这个学习笔记凑够 ...

  6. STM32学习心得二十一:实时时钟RTC和备份寄存器BKP特征、原理及相关实验代码解读

    记录一下,方便以后翻阅~ 主要内容 1) RTC特征与原理: 2) BKP备份寄存器特征与原理: 3) RTC常用寄存器+库函数介绍: 4) 相关实验代码解读. 实验内容: 因为没有买LCD屏,所以计 ...

  7. Mr.J-- jQuery学习笔记(十九)--自定义动画实现图标特效

    之前有写过自定义动画Mr.J-- jQuery学习笔记(十八)--自定义动画 这次实现一个小demo 图标特效 页面渲染 <!DOCTYPE html> <html lang=&qu ...

  8. RTC实时时钟实验(低功耗、纽扣电池供电)

    目录 I.MX6U RTC 简介 硬件原理分析 实验程序编写 修改文件MCIMX6Y2.h 编写实验程序 编译下载验证 编写Makefile 和链接脚本 编译下载 实时时钟是很常用的一个外设,通过实时 ...

  9. STM32 学习笔记2-智能小车循迹实验

    特斯拉镇楼 1.什么是小车循迹? 将小车放在黑色跑道上面,小车沿着黑色跑道运动 → 循迹 黑色跑道 2.小车循迹基本原理 原理: 介绍原理之前,突然记起来,在电子爱好者上  做过 一个循迹小车的项目, ...

最新文章

  1. java gui 选项_【Java-GUI】04 菜单
  2. oracle重新恢复数据库,重新安装oracle根据原数据文件恢复数据库
  3. python闭包、装饰器
  4. webpack — 概述介绍
  5. Cacti迁移RRA数据迁移脚本
  6. ASP.NET Core 借助 K8S 玩转容器编排
  7. git入门_绝对入门的Git
  8. 缓存世界中的三大问题及解决方案
  9. C++ 析构函数不要抛出异常
  10. 如何编写一个Makefile文件(手把手的教你)
  11. 科技爱好者周刊(第 167 期):广告拦截器太过分了
  12. linux 分区表 修复工具,介绍一个 GPL 的分区表修复工具 TestDisk
  13. 我的 Android 开发实战经验总结
  14. 每天学一点英文:Espresso 20210811
  15. 墨珩科技超衡联盟链通过中国赛宝实验室测试
  16. Java map集合实现上一条(篇),下一条(篇)新闻翻篇功能
  17. win7工作组计算机无法连接打印机,win7无法连接打印机,操作失败,错误码为0x000003e...-win7无法连接到打印机,win7无法连接打印机拒绝...
  18. python中的调试器dbg
  19. OpenCppCoverage 引起测试程序Crash问题
  20. VsCode简单使用,配置c/c++编译环境,个人感受

热门文章

  1. 重塑云通讯,斩马视觉智能
  2. 不能访问网络位置的解决方法:
  3. java通讯录概要设计,基于JAVA的通讯录管理系统的设计与实现
  4. 光场相机如何实现四维采样
  5. DOSBox下玩DOS经典GameZ或ZED游戏
  6. web第一课模拟淘宝设置静态网页——html+css布局设计
  7. c语言冯诺依曼原理,详解冯诺依曼结构运行原理
  8. 证券法律法规体系所有法律_告诉我法律
  9. 专项资金项目管理系统——建设目标
  10. which命令用法举例