完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
实验目的
本实验旨在掌握STM32的实时时钟RTC的使用,利用其测量日期时间,数据手册请参看16章。 实验简介 STM32的实时时钟(RTC)是一个独立的定时器。STM32的RTC模块拥有一组连续计数的计数器,在相应软件配置下,可以提供时钟日历的功能,修改计数器的值可以重新设置系统当前的时间和日期。 RTC模块和时钟配置系统(RCC_BDCR寄存器)是在后备区域,即在系统复位或从待机模式唤醒后RTC的设置和时间维持不变 系统复位后,会自动禁止访问后备寄存器和RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前,先要取消备份区域(BKP)写保护, STM32的RTC具有掉电继续运行的特性,在主电源VDD断开时,为了RTC外设掉电继续运行,必须给STM32芯片通过VBAT引脚接上锂电池,当主电源VDD有效时,由VDD给RTC外设供电。当VDD掉电后,由VBAT给RTC外设供电,但无论由什么电源供电,RTC中的数据都保存在属于RTC的备份区域中,若主电源VDD和VBAT都掉电,那么备份域中保存的所有数据将丢失。备份域除了RTC模块的寄存器,还有42个16位的寄存器可以在VDD掉电的情况下保存用户程序的数据,系统复位或电源复位时,这些数据也不会复位。 从RTC的定时器特性来说,它是一个32位的计数器,只能向上计数。它使用的时钟源有三种,分别为高速外部时钟的128分频:HSE/128;低速内部时钟LSI;使用HSE分频时钟或LSI的话,在主电源VDD掉电的情况下,这两个时钟源都会受到影响,因此没法保证RTC正常工作。因此RTC一般使用低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,这是因为32768=2的15次方,分频容易实现,所以它被广泛应用到RTC模块。在主电源VDD有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式 HAL库代码 main.c #include "MyIncludes.h" u16 sys_cnt = 0; void systick_isr(void) { if(sys_cnt <100 ) sys_cnt++; else { sys_cnt = 0; HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4|GPIO_PIN_5); } } void rtc_disp(void) { char date[15]; //用来存储日期 char time[15]; //用来存储时间 sprintf((char*)date,"20%d%d/%d%d/%d%d ",Calendar.RTC_Year%10,Calendar.RTC_Year%10,Calendar.RTC_Mon/10,Calendar.RTC_Mon%10,Calendar.RTC_Mday/10,Calendar.RTC_Mday%10); //sprintf函数打印到字符串中(将数值转换成对应字符串形式,就是变换成ASCALL码) sprintf((char*)time,"%d%d:%d%d:%d%dn",Calendar.RTC_Hour/10,Calendar.RTC_Hour%10,Calendar.RTC_Min/10,Calendar.RTC_Min%10,Calendar.RTC_Sec/10,Calendar.RTC_Sec%10); printf(date); //发送日期 printf(time); //发送时间 } int main() { System_Init(); //系统初始化 LED_Init(); //LED初始化 SysTick_Init(systick_isr); //SysTick初始化 USART1_Init(115200,NULL,NULL); //串口1初始化 波特率:115200 //发送完成回调为空 接收回调为空 RTC_Init(rtc_disp); //实时时钟初始化 while(1) { RTC_Process(); //RTC处理函数 } } rtc.h #ifndef __RTC_H #define __RTC_H #include "stm32f1xx.h" #include "stm32_types.h" #include "stm32f1xx_hal.h" typedef struct { u8 RTC_Sec; //秒 u8 RTC_Min; //分 u8 RTC_Hour; //时 u8 RTC_Mday; //日 u8 RTC_Mon; //月 u8 RTC_Year; //年 u8 RTC_Wday; //星期 }_Calendar_obj; //日历结构体 extern _Calendar_obj Calendar; //结构体声明 typedef struct { void (*Operation)(void); //函数指针 }_RTC_Fun; void RTC_SetDateTime(_Calendar_obj datatime); //设置RTC的日期时间函数 _Calendar_obj RTC_GetDateTime(void); //获取RTC日期时间函数 void RTC_Init(void(*rtc)(void)); //RTC初始化函数 void RTC_Process(void); //RTC处理 #endif rtc.c #include "rtc.h" _Calendar_obj Calendar; //日历时钟结构体变量声明 RTC_HandleTypeDef RTCHandle; //时间句柄结构体变量声明 _RTC_Fun rtc_fun; //用户函数结构体变量声明 u8 const table_week[12] = {0,3,3,6,1,4,6,2,5,0,3,5}; //月修正表数据 /*假设1月1日是星期一,那么2月1日是星期四,(4-1=3),故是3 3月1日是星期四,(4-1=3),4月1日是星期日(7-1=6),依次类推, 前提这一年是平年 */ u8 RTC_Get_Week(u16 year,u8 month,u8 day) //作用算出周几 { u16 temp2; u8 yearH,yearL; yearH = year/100; yearL = year%100; if(yearH >19 ) yearL+=100; //这几行算出本年距离1900年的差 temp2 = yearL + yearL/4; //算出除了一年中除正周外累计多出来的天数 365%7=1 闰年的个数:366-365=1 temp2 = temp2%7; //除去整周的天数 temp2 = temp2+day+table_week[month-1]; //现在temp2是除去整周的天数,加上本日的天数 在加上月修正表数据 if(yearL%4 == 0 && month <3 ) temp2--; //如果是闰年1月2月的话要减去1,因为yearl多出来的一天加上去了,也就是 //2月29日,多出来的一天再三月份以后才能加 return (temp2%7); //这个就是本周星期几 /*在这里我们举个栗子吧:假设时间是2020年2月29日,那么首先yearH=20, yearl=20,因为yearH = 20 >19 那么yeal+=100 那么yeal = 120天 temp2 = yearL + yearL/4; 120+30 = 150 天, 150%7 = 3, 3+29+3 = 35 35-1=34 34%7=6 也就是星期六,我们看下日历确实 是星期六*/ } void RTC_SetDateTime(_Calendar_obj datetime) //先看void RTC_Init(void (*rtc)(void))函数 //设置RTC的日期和时间 { RTC_DateTypeDef SetDate; //设置日期结构体声明 RTC_TimeTypeDef SetTime; //设置时间结构体声明 SetDate.Date = datetime.RTC_Mday; SetDate.Month = datetime.RTC_Mon; SetDate.Year = datetime.RTC_Year; //0~99 SetDate.WeekDay = datetime.RTC_Wday; SetTime.Hours = datetime.RTC_Hour; SetTime.Minutes = datetime.RTC_Min; SetTime.Seconds = datetime.RTC_Sec; HAL_RTC_SetDate(&RTCHandle,&SetDate,RTC_FORMAT_BIN); //设置日期 HAL_RTC_SetTime(&RTCHandle,&SetTime,RTC_FORMAT_BIN); //设置时间 return ; } _Calendar_obj RTC_GetDateTime(void) //先看void RTC_Process(void) //获取RTC日期时间 { RTC_DateTypeDef SetDate; RTC_TimeTypeDef SetTime; _Calendar_obj datetime; HAL_RTC_GetTime(&RTCHandle,&SetTime,RTC_FORMAT_BIN); //获取时间 HAL_RTC_GetDate(&RTCHandle,&SetDate,RTC_FORMAT_BIN); //获取日期 datetime.RTC_Sec = SetTime.Seconds; datetime.RTC_Min = SetTime.Minutes; datetime.RTC_Hour = SetTime.Hours; datetime.RTC_Mday = SetDate.Date; datetime.RTC_Mon = SetDate.Month; datetime.RTC_Year = SetDate.Year; datetime.RTC_Wday = SetDate.WeekDay; return datetime; } void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc) //在HAL_RTC_Init中调用 { RCC_OscInitTypeDef RCC_OscInitStruct; //RCC内部/外部振荡器(HSE、HSI、LSE和LSI)配置结构变量声明 RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; //RCC扩展时钟结构变量声明 __HAL_RCC_PWR_CLK_ENABLE(); //备份电源使能 HAL_PWR_EnableBkUpAccess(); //电源使能唤醒 __HAL_RCC_BKP_CLK_ENABLE(); //使能BKP时钟 //配置时钟源 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; //要配置的振荡器 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; //PLL的状态 未配置 RCC_OscInitStruct.LSEState = RCC_LSE_ON; //振荡器状态 振荡器开启 HAL_RCC_OscConfig(&RCC_OscInitStruct); //RCC配置 PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; //要配置的拓展时钟 外部时钟 0x0000001 PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; //指定RTC时钟源 用作RTC时钟的LSE振荡器时钟 HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); //配置拓展时钟 __HAL_RCC_RTC_ENABLE(); // 使能RTC } void RTC_Init(void (*rtc)(void)) { _Calendar_obj DateTime; //日历结构体变量声明 RTCHandle.Instance = RTC; //寄存器基址 RTC RTCHandle. Init.AsynchPrediv = RTC_AUTO_1_SECOND; //指定BIC 异分频系数 0xffffffff 自动获取1s时机基 RTCHandle.Init.OutPut = RTC_OUTPUTSOURCE_NONE; //指定那个信号路由到RTC针 0x00000000 //篡改针上没有输出 HAL_RTC_Init(&RTCHandle); //初始化RTC参数 DateTime.RTC_Hour = 23; DateTime.RTC_Min = 59; DateTime.RTC_Sec = 50; DateTime.RTC_Year = 16; DateTime.RTC_Mon = 8; DateTime.RTC_Mday = 21; DateTime.RTC_Wday = RTC_Get_Week(DateTime.RTC_Year+2000,DateTime.RTC_Mon,DateTime.RTC_Mday); //2016年8月21日23时59分50秒 RTC_SetDateTime(DateTime); //设置RTC的时间和日期 rtc_fun.Operation = rtc; return ; } u8 last_sec = 0; void RTC_Process(void) //RTC处理 { Calendar = RTC_GetDateTime(); if(last_sec != Calendar.RTC_Sec) { last_sec = Calendar.RTC_Sec; if(rtc_fun.Operation != NULL) rtc_fun.Operation(); //每一秒刷新一次 } } 寄存器代码 main.c #include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "timer.h" #include "key.h" #include "rtc.h" int main(void) { char t[20]; Stm32_Clock_Init(9); //ϵͳʱÖÓÉèÖà uart_init(72,115200); //´®¿Ú³õʼ»¯Îª115200 delay_init(72); //ÑÓʱ³õʼ»¯ LED_Init(); //³õʼ»¯ÓëLEDÁ¬½ÓµÄÓ²¼þ½Ó¿Ú while(RTC_Init()) { } while(1) { LED2=!LED2; delay_ms(100); sprintf((char*)t,"%d:%d:%drn",calendar.w_year,calendar.w_month,calendar.w_date); printf(t); } } RTC.h #ifndef __RTC_H #define __RTC_H #include "sys.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; //日历结构体 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 失败返回1 成功返回0 u8 Is_Leap_Year(u16 year); //平年瑞年判断 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); //设置时间 u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec); //设置闹钟 #endif |
|
|
|
RTC.C
#include "delay.h" #include "usart.h" #include "rtc.h" #include "LED.h" _calendar_obj calendar; //时钟结构体 u8 RTC_Init(void) { u8 temp=0; //检查是不是第一次配置时钟 if(BKP->DR1!=0X5050)//第一次配置 { RCC->APB1ENR|=1<<28; //使用电源时钟 RCC->APB1ENR|=1<<27; //使能备份时钟 PWR->CR|=1<<8; //取消备份区写保护 RCC->BDCR|=1<<16; //备份区域软复位 RCC->BDCR&=~(1<<16); //备份区域软复位结束 RCC->BDCR|=1<<0; //开启外部低速振荡器 while((!(RCC->BDCR&0X02))&&temp<250) //等待外部时钟就绪 { temp++; delay_ms(10); }; if(temp>=250)return 1; //初始化时钟失败,晶振有问题 RCC->BDCR|=1<<8; //LSE作为RTC时钟 RCC->BDCR|=1<<15; //RTC时钟使能 while(!(RTC->CRL&(1<<5))); //等待RTC寄存器操作完成 while(!(RTC->CRL&(1<<3))); //等待RTC寄存器同步 RTC->CRH|=0X01; //允许秒中断 RTC->CRH|=0X02; //允许闹钟中断 while(!(RTC->CRL&(1<<5))); //等待RTC寄存器操作完成 RTC->CRL|=1<<4; //允许配置 RTC->PRLH=0X0000; RTC->PRLL=32767; //时钟周期设置 RTC_Set(2015,1,14,17,42,55); //设置时间 RTC->CRL&=~(1<<4); //配置更新 while(!(RTC->CRL&(1<<5))); //等待RTC寄存器操作完成 BKP->DR1=0X5050; printf("FIRST TIMEn"); }else//系统继续计时 { while(!(RTC->CRL&(1<<3))); //等待RTC寄存器同步 RTC->CRH|=0X01; //允许秒中断 while(!(RTC->CRL&(1<<5))); //等待RTC寄存器操作完成 printf("OKn"); } MY_NVIC_Init(0,0,RTC_IRQn,2); //优先级设置 RTC_Get(); //更新时间; return 0; } //RTC时钟中断 //每秒触发一次 void RTC_IRQHandler(void) { if(RTC->CRL&0x0001) //秒中断 { RTC_Get(); //更新时间 } if(RTC->CRL&0x0002) //闹钟中断 { RTC->CRL&=~(0x0002); //清除闹钟中断 RTC_Get(); //更新时间 printf("Alarm Time:%d-%d-%d %d:%d:%dn",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec); } RTC->CRL&=0X0FFA; //清除溢出,秒钟中断标志 while(!(RTC->CRL&(1<<5))); //等待RTC寄存器操作完成 } //判断是否是不是闰年 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; } 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}; //平年得月份日期表 //RTC设置 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 if(Is_Leap_Year(t))seccount+=31622400; //闰年的秒钟数 else seccount+=31536000; //平年的秒钟数 } smon-=1; for(t=0;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->APB1ENR|=1<<28; //使能电源时钟 RCC->APB1ENR|=1<<27; //使能备份时钟 PWR->CR|=1<<8; //取消备份区写保护 RTC->CRL|=1<<4; //允许配置 RTC->CNTL=seccount&0xffff; RTC->CNTH=seccount>>16; RTC->CRL&=~(1<<4); //配置更新 while(!(RTC->CRL&(1<<5))); //等待RTC寄存器操作完成 RTC_Get(); //设置完之后更新一下数据 return 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 if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数 else seccount+=31536000; //平年的秒钟数 } smon-=1; for(t=0;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->APB1ENR|=1<<28;//使能电源时钟 RCC->APB1ENR|=1<<27;//使能备份时钟 PWR->CR|=1<<8; //取消备份区写保护 RTC->CRL|=1<<4; //允许配置 RTC->ALRL=seccount&0xffff; RTC->ALRH=seccount>>16; RTC->CRL&=~(1<<4);//配置更新 while(!(RTC->CRL&(1<<5))); //等待RTC寄存器操作完成 return 0; } //得到当前时间 u8 RTC_Get(void) { static u16 daycnt=0; u32 timecount=0; u32 temp=0; u16 temp1=0; timecount=RTC->CNTH; //得到计数器中的值(秒钟数) timecount<<=16; timecount+=RTC->CNTL; 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 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; } //获得现在是星期几 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); } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1801 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1629 浏览 1 评论
1096 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
735 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1684 浏览 2 评论
1944浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
745浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
578浏览 3评论
601浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
565浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 08:04 , Processed in 0.839981 second(s), Total 80, Slave 64 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号