1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > STM32学习笔记(十九)RTC实时时钟实验

STM32学习笔记(十九)RTC实时时钟实验

时间:2022-12-22 14:51:47

相关推荐

STM32学习笔记(十九)RTC实时时钟实验

STM32F103ZET6之RTC实时时钟实验


文章目录

STM32F103ZET6之RTC实时时钟实验前言一、简介二、相关寄存器及配置过程三、程序源码1.rtc.h2.rtc.c3.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(,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);//初始化USMARTRTC_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,"/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.若实验现象与推理不一致,一定要认真分析程序。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。