最近在实现一个万年历的功能,其中遇到了无法设置时间以及掉电时间清零的问题。
.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);
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);
#endif
.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;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
u8 RTC_Init(void)
{
u8 temp=0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5080)
{
BKP_DeInit();
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1;
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForLastTask();
RTC_WaitForSynchro();
RTC_ITConfig(RTC_IT_SEC, ENABLE);
RTC_WaitForLastTask();
RTC_EnterConfigMode();
RTC_SetPrescaler(32767);
RTC_WaitForLastTask();
RTC_Set(2021,3,20,7,32,00);
RTC_ExitConfigMode();
BKP_WriteBackupRegister(BKP_DR1, 0X5080);
}
else
{
RTC_WaitForSynchro();
RTC_ITConfig(RTC_IT_SEC, ENABLE);
RTC_WaitForLastTask();
}
RTC_NVIC_Config();
RTC_Get();
return 0;
}
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_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);
RTC_WaitForLastTask();
}
u8 Is_Leap_Year(u16 year)
{
if(year%4==0)
{
if(year%100==0)
{
if(year%400==0)return 1;
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};
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;
}
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_BackupAccessCmd(ENABLE);
RTC_SetCounter(seccount);
RTC_WaitForLastTask();
RTC_Get();
return 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;
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)
{
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;
if (yearH>19)yearL+=100;
temp2=yearL+yearL/4;
temp2=temp2%7;
temp2=temp2+day+table_week[month-1];
if (yearL%4==0&&month<3)temp2--;
return(temp2%7);
}
使用正点原子提供代码
在代码中可以看到修改时间的操作函数
RTC_Set(2021,3,20,7,32,00);
但在修改时间后程序运行后发现时间并没有改变,查看代码后发现该函数运行在if语句中,便猜测可能是if语句的条件不成立。
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5080)
{
BKP_DeInit();
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1;
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForLastTask();
RTC_WaitForSynchro();
RTC_ITConfig(RTC_IT_SEC, ENABLE);
RTC_WaitForLastTask();
RTC_EnterConfigMode();
RTC_SetPrescaler(32767);
RTC_WaitForLastTask();
RTC_Set(2021,3,20,7,32,00);
RTC_ExitConfigMode();
BKP_WriteBackupRegister(BKP_DR1, 0X5080);
}
因为本实验的内容显示在LCD显示屏上,于是我便在if语句后添加了一条在LCD屏上显示任意内容的语句,编译烧录后发现该条语句的内容并没有显示出来,于是便确定了是if语句的条件不成立,因为不知道该如何修改条件,便上网搜索,查找到相似的问题解决方法是将if语句的条件后0x5080的值进行任意修改。
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5080)
修改后将程序下载到开发板,运行后发现时间确实修改成功,但又发现掉电后时间会回到设置的值,于是又进行检索,然而并没有找到解决方法。于是便开始自己找问题所在,由于设置的RTC时钟源是由低速外部晶振(LSE)提供,掉电后时间继续运行是因为LSE有外部电源(纽扣电池)供电,便猜想是否是因为纽扣电池没电了,于是卸下纽扣电池用万用表测量,测量结果表明问题不在于电池。
得知问题不在于电池时,便想问题一定在于程序,出现问题的原因不一定是掉电后RTC时钟没有计数,也有可能是程序重启时对时间又进行了一次设置。检索不出解决办法,便只能从《STM32参考手册》好好了解下RTC的工作方式。
从该部分内容得知要想修改时间必须执行以上两点,换言之为了使不能修改时间只能使上面两点不能全部成立,再对应代码分析
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5080)
{
...
...
...
RCC_RTCCLKCmd(ENABLE);
...
...
...
}
易得知只有RTC时钟的使能在if语句中,即只要使if语句的条件不成立便不会对时间进行修改,查找if语句条件中的函数在stm32f10x_bkb.c文件中查找到对该函数的定义
uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR)
{
__IO uint32_t tmp = 0;
assert_param(IS_BKP_DR(BKP_DR));
tmp = (uint32_t)BKP_BASE;
tmp += BKP_DR;
return (*(__IO uint16_t *) tmp);
}
该函数描述为从指定的数据备份寄存器读取数据,在if语句中即为从BKP_DR1寄存器中读取数据,即条件为对比BKP_DR1寄存器中的数据是否为后边的值,相等则不执行时间设置语句,不相等则执行时间设置语句,对于该寄存器中的值为多少,阅读代码在if语句结尾发现如下语句
BKP_WriteBackupRegister(BKP_DR1, 0X5080);
该语句为对BKP_DR1寄存器写入数据,即只要使得这两个语句中的数值相等即可
BKP_ReadBackupRegister(BKP_DR1) != 0x5080
将两个数字改为一样后,开发板确实不会掉电时间清零,并且掉电后时间仍然在计数。
小结:
RTC实时时钟修改时间时需要将以上两个数值设置为与原数据不相同,即每次修改时间时将两个数据均做修改且为相同数字,即可使得设置一次时间,掉电后仍然计数。
心得:
遇到问题时不能直接就去检索,一是不能锻炼自己解决问题和分析问题的能力;二是一些问题检索不到会耗费大量的时间。应该在自己分析完后仍然不能解决问题时再去检索。分析问题时要软硬件结合,多参照参考手册,深入理解每一句代码的功能。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)