stm32 利用备份寄存器保存实时时钟数据

在实际应用中,会出现许多复位或者掉电的情况,下面提供了一种方法使即使是在掉电和复位事件发生时,仍旧可以利用低功耗模式继续对于实时时钟进行供电,保证时钟的正常运行!

stm32 利用备份寄存器保存实时时钟数据_第1张图片

//bsp_rtc.h
#ifndef _BSP_RTC_H
#define _BSP_RTC_H
#include "misc.h"


/*全局变量*/	   
 
uint8_t RTCInterruptFlag=0;	 //RTC 中断标志

uint32_t RTC_TimeNum=0;			  // 设置时间变量


uint16_t Year;

uint8_t Month;

uint8_t Day;

/* RTC hardware init*/
void RTC_NVIconfigration(void);

void RTC_configration(void);

void RTC_Init(void);

/*日历及时间输入*/
uint8_t RTC_InputTime(uint32_t border);

uint32_t RTC_TimeCollate(void);

void RTC_RxIntHandler(void); 

 /*获得年月日*/
uint16_t GetYear();

uint8_t GetMonth();

uint8_t GetDay();

void CalenderSet(void);

void  CalenderCount(void);

static uint8_t Choice_MonthDay(uint16_t temp_year,uint8_t temp_month);

/*显示*/
void RTC_TimeDisplay(uint32_t TimeVar);

 /*测试*/
void Text_RTC(void); 


#endif/*_BSP_RTC_H*/
 


bsp_rtc.c

#include "bsp_rtc.h"

#define RTCClockSource_LSE

uint32_t TimeDisplay=0;	// 用于显示测试 每次进入中断改变  

/***************************************************************************************************
*\Function      RTC_NVIconfigration()
*\Description   设置RTC中断优先级
*\Parameter     void
*\Return        void
*\Note          
*\Log          	2014年7月24日
***************************************************************************************************/
void RTC_NVIconfigration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);	  //已经在bsp_usart.c进行了设置

    /* Enable the RTC Interrupt */
    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);
}

/***************************************************************************************************
*\Function      RTC_configration(void)
*\Description   RTC配置函数
*\Parameter     void
*\Return        void
*\Note          
*\Log          	2014年7月24日
***************************************************************************************************/
void RTC_configration(void)
{
    /* 使能 PWR 和 BKP 的时钟 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

    PWR_DeInit();
    /* 允许访问BKP区域 */
    PWR_BackupAccessCmd(ENABLE);

    /* 复位BKP */
    BKP_DeInit();

#ifdef RTCClockSource_LSI
    /* 使能内部RTC时钟 */
    RCC_LSICmd(ENABLE);
    /* 等待RTC内部时钟就绪 */
    while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
    {
    }
    /* 选择RTC内部时钟为RTC时钟 */
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
#elif defined	RTCClockSource_LSE
    /* 使能RTC外部时钟 */
    RCC_LSEConfig(RCC_LSE_ON);
    /* 等待RTC外部时钟就绪 */
    while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
    {
    }

    /* 选择RTC外部时钟为RTC时钟 */
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
#endif
    /* 使能RTC时钟 */
    RCC_RTCCLKCmd(ENABLE);


#ifdef RTCClockOutput_Enable
    /* Disable the Tamper Pin */
    BKP_TamperPinCmd(DISABLE); /* To output RTCCLK/64 on Tamper pin, the tamper
                               functionality must be disabled */

    /* 使能在TAMPER脚输出RTC时钟 */
    BKP_RTCCalibrationClockOutputCmd(ENABLE);
#endif

    /* 等待RTC寄存器同步 */
    RTC_WaitForSynchro();

    /* 等待写RTC寄存器完成 */
    RTC_WaitForLastTask();

    /* 使能RTC秒中断 */
    RTC_ITConfig(RTC_IT_SEC, ENABLE);

    /* 等待写RTC寄存器完成 */
    RTC_WaitForLastTask();

    /* 设置RTC预分频 */
#ifdef RTCClockSource_LSI
    RTC_SetPrescaler(31999);            /* RTC period = RTCCLK/RTC_PR = (32.000 KHz)/(31999+1) */
#elif defined	RTCClockSource_LSE
    RTC_SetPrescaler(32767);            /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
#endif

    /* 等待写RTC寄存器完成 */
    RTC_WaitForLastTask();
}
/***************************************************************************************************
*\Function      RTC_Init(void)
*\Description   RTC初始化
*\Parameter     void
*\Return        void
*\Note          
*\Log          	2014年7月24日
***************************************************************************************************/
void RTC_Init(void)
{
    if (BKP_ReadBackupRegister(BKP_DR1)!=0x0229)
    {
        //printf("\n RTC not yet configured....");

        RTC_configration();

        printf("\n RTC实时时钟设置...");
		 /*日历设置操作*/
		 CalenderSet();

		 BKP_WriteBackupRegister(BKP_DR2,GetYear());

	     BKP_WriteBackupRegister(BKP_DR3,GetMonth());  

	     BKP_WriteBackupRegister(BKP_DR4,GetDay());


        /*实时时钟设置操作:*/
        RTC_WaitForLastTask();

        RTC_SetCounter(RTC_TimeCollate());	// 进行设置

        RTC_WaitForLastTask();

        BKP_WriteBackupRegister(BKP_DR1,0x0229);

		
    }
    else
    {
        if (RCC_GetFlagStatus(RCC_FLAG_PORRST)!=RESET)
        {

            printf("\r\n\n 掉电重启事件....");
        }
        else  if (RCC_GetFlagStatus(RCC_FLAG_PINRST)!=RESET)
        {

            printf("\r\n\n 外部复位事件....");
        }
        printf("\r\n 等待时间同步....");
       
		 /*在备份寄存器中重新读出年月日*/
	   	 Year=BKP_ReadBackupRegister(BKP_DR2);

		 Month=BKP_ReadBackupRegister(BKP_DR3);

	   	 Day=BKP_ReadBackupRegister(BKP_DR4);

	    /*在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待
         RTC_CRL寄存器中的RSF 位(寄存器同步标志)被硬件置’1’ 。
        注:    RTC的 APB1 接口不受WFI和WFE等低功耗模式的影响。 */
        RTC_WaitForSynchro(); //  同步

        RTC_WaitForLastTask();

        RTC_ITConfig(RTC_IT_SEC,ENABLE);// 使能秒中断

        RTC_WaitForLastTask();

    }
    RCC_ClearFlag();
}
/////////硬件初始化////////////////////////////////////////////////////////////////
/***************************************************************************************************
*\Function      GetYear()
*\Description   获得年数
*\Parameter     void
*\Return        uint16_t Year
*\Note          
*\Log          2014年7月5日
*     
***************************************************************************************************/
uint16_t GetYear()
{
	 return Year;

}
/***************************************************************************************************
*\Function      GetMonth()
*\Description   获得月数
*\Parameter     void
*\Return        uint8_t Month
*\Note          
*\Log          2014年7月5日
*     
***************************************************************************************************/
uint8_t GetMonth()
{
	 return Month;

}
/***************************************************************************************************
*\Function      GetDay()
*\Description   获得月数
*\Parameter     void
*\Return        uint8_t Day
*\Note          
*\Log          2014年7月5日
*     
***************************************************************************************************/
uint8_t GetDay()
{  
	  return Day; 
}
/***************************************************************************************************
*\Function      Choice_MonthDay(uint16_t temp_year,uint8_t temp_month)
*\Description   选择月份天数
*\Parameter     uint16_t temp_year  
*\Parameter     uint8_t temp_month
*\Return        Month_day
*\Note          
*\Log          2014年7月5日
*     
***************************************************************************************************/
static uint8_t Choice_MonthDay(uint16_t temp_year,uint8_t temp_month)
{
	uint8_t	Month_day=0;
	switch(temp_month)
	{
	   	case 1:case 3:case 5:case 7:case 8:	case 10:case 12:
		Month_day=31;
		break;
		case 4:	case 6:	case 9:	case 11:
		Month_day=30;
		break;
		case 2:
		if(temp_year%4==0&&temp_year%100!=0||temp_year%400==0)
		Month_day=29;
		else
		Month_day=28;
		default:
		break;		 	
	}
	return Month_day;
}

/***************************************************************************************************
*\Function      CalenderCount()
*\Description   日历计数函数
*\Parameter     void
*\Return        void
*\Note          
*\Log          2014年7月5日
*     
***************************************************************************************************/
void  CalenderCount(void)
{
	if(Day>0&&Day<Choice_MonthDay(Year,Month))
	{		 	
	   Day++;
	}
	else
	{
	  Month++;
	  if(Month>12)
	  {
	  	 Month=1;
		 Year++;
	  }
	  Day=1;	
	} 
}
/***************************************************************************************************
*\Function      CalenderInput()
*\Description   用户日历校对输入函数
*\Parameter     void
*\Return        void
*\Note          
*\Log          2014年7月5日
*     
***************************************************************************************************/
uint8_t CalenderInput()
{
   
    RTCInterruptFlag=0;
    RTC_TimeNum=0;
    RTC_ITConfig(RTC_IT_SEC,DISABLE);   // 失能能秒中断

	/* 防止第一个字符无法发出,关闭发送中断  */
    if (USART_GetITStatus(USART2,USART_IT_TXE)!=RESET)
    {
        USART_ITConfig(USART2,USART_IT_TXE,DISABLE);
    }
	/*等待用户输入数据*/
    while (!RTCInterruptFlag)
    {
        printf("");	
    }   
 
    RTC_ITConfig(RTC_IT_SEC,ENABLE);// 失能能秒中断	

    return RTC_TimeNum;
}		   
/***************************************************************************************************
*\Function      RTC_InputTime(uint32_t border)
*\Description   用户时间校时输入函数
*\Parameter     uint32_t border	   边界大小
*\Return        uint8_t
*\Note          
*\Log          	2014年7月24日
***************************************************************************************************/
uint8_t RTC_InputTime(uint32_t border)
{
   
    RTCInterruptFlag=0;
    RTC_TimeNum=0;
    RTC_ITConfig(RTC_IT_SEC,DISABLE);   // 失能能秒中断

	/* 防止第一个字符无法发出,关闭发送中断  */
    if (USART_GetITStatus(USART2,USART_IT_TXE)!=RESET)
    {
        USART_ITConfig(USART2,USART_IT_TXE,DISABLE);
    }
	/*等待用户输入数据*/
    while (!RTCInterruptFlag)
    {
        printf("");	// 个人理解,因为RTC时间设置和数据输入都是用的USAET2 这样做是为了防止优先级被占用,不加的话while将不起作用
    }  
    printf("\n您键入的数值是:");   
    if (RTC_TimeNum > border)
    {
        printf("\n\r请键入0到%d之间的数字", border);
        return 0xFF;
    }
    RTC_ITConfig(RTC_IT_SEC,ENABLE);// 失能能秒中断	

    return RTC_TimeNum;
}

/***************************************************************************************************
*\Function      void RTC_RxIntHandler()
*\Description   RTC中断处理函数
*\Parameter     void
*\Return        void
*\Note          
*\Log          	2014年7月24日
*               放在中断处理中
***************************************************************************************************/
void RTC_RxIntHandler(void)
{

    uint32_t temp_data;
    RTCInterruptFlag=1;
    if (USART_GetFlagStatus(USART2,USART_IT_RXNE)!=RESET)
    {
        temp_data=(USART_ReceiveData(USART2));
        RTC_TimeNum=(temp_data>>4)*10+(temp_data&0x0F);	// 设置时间变量
      //  printf("当前的temp_data %d",RTC_Num);
    }					   
    if (USART_GetITStatus(USART2,USART_IT_TXE)!=RESET)
    {
        USART_ITConfig(USART2,USART_IT_TXE,DISABLE);
    }			   
}

///////////以上为底层函数//////////////////////////////////////////////////////////////////////////////////////////

/***************************************************************************************************
*\Function      CalenderSet(void)
*\Description   日历设置函数
*\Parameter     void
*\Return        void
*\Note         用于串口输入 
*\Log          2014年7月5日
*     
***************************************************************************************************/
void CalenderSet(void)
{
	 printf("\n=========设置年月日=================:");
	 printf("\n请输入年份:");
	 Year=(uint16_t)CalenderInput()*100;
	 Year=Year+CalenderInput();
	 printf("\n您输入的年份是:%d",Year);
	 printf("\n\n请输入月份:");
	 Month=CalenderInput();
	 printf("\n您输入的月份是:%d",Month);
	 printf("\n\n请输入日期:");
	 Day=CalenderInput();
	 printf("\n您输入的日期是:%d",Day);
}

/****************************************************************************
* 名    称:uint32_t RTC_TimeCollate(void)
* 功    能:时间校正函数
* 入口参数:无
* 出口参数:uint32_t
* 说    明:用于串口输入
* 调用方法:
****************************************************************************/
uint32_t RTC_TimeCollate(void)
{
    uint32_t Tmp_HH = 0xFF, Tmp_MM = 0xFF, Tmp_SS = 0xFF; 
    printf("\r\n==============时间设置=========================");
    printf("\r\n  请输入小时:");  
    while (Tmp_HH == 0xFF)
    {
        Tmp_HH = RTC_InputTime(23);
    }
    printf(":  %d", Tmp_HH);
    printf("\r\n  请输入分钟:");
    while (Tmp_MM == 0xFF)
    {
        Tmp_MM = RTC_InputTime(59);
    }
    printf(":  %d", Tmp_MM);
    printf("\r\n  请输入秒数:");
    while (Tmp_SS == 0xFF)
    {
        Tmp_SS = RTC_InputTime(59);
    }
    printf(":  %d", Tmp_SS);		  
    /* 返回保存在RTC计数寄存器里的值 */
    return((Tmp_HH*3600 + Tmp_MM*60 + Tmp_SS));
}	  



/****************************************************************************
* 名    称:void RTC_TimeDisplay(uint32_t TimeVar)
* 功    能:显示当前时间
* 入口参数:无
* 出口参数:无
* 说    明:
* 调用方法:
****************************************************************************/
void RTC_TimeDisplay(uint32_t TimeVar)
{
    uint32_t THH = 0, TMM = 0, TSS = 0;

    /* 计算小时 */
    THH = TimeVar/3600;
    /* 计算分钟 */
    TMM = (TimeVar % 3600)/60;
    /* 计算秒 */
    TSS = (TimeVar % 3600)% 60;
    printf("\n%d年 %d月 %d日  ",Year,Month,Day);
    printf("Time: %0.2d:%0.2d:%0.2d\r\n",THH, TMM, TSS);
}


 ///////////以上为应用层函数/////////////////////////////////////////////////////////////////





/***************************************************************************************************
*\Function      RTC_text(void)
*\Description   时间显示
*\Parameter     void
*\Return        void
*\Note          
*\Log          	时间
*              
***************************************************************************************************/
void RTC_text(void)
{
    printf("\n\r");
    while (1)
    {
        /* 秒更新发生 */
        if(TimeDisplay == 1)
        {
            /* 显示当前时间 */
            RTC_TimeDisplay(RTC_GetCounter());
            TimeDisplay = 0;
        }
    }
}



stm32f10x_it.c
/*******************************************************************************
* Function Name  : RTC_IRQHandler
* Description    : This function handles RTC global interrupt request.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void RTC_IRQHandler(void)
{
  if(RTC_GetITStatus(RTC_IT_SEC) != RESET)				 //读取秒中断状态
  {
    RTC_ClearITPendingBit(RTC_IT_SEC);					 //清除秒中断标志			    

    /* 时钟更新标志置位 */
    TimeDisplay = 1;	  
    RTC_WaitForLastTask();							     //等待上一次对RTC寄存器的写操作是否已经完成    
    if(RTC_GetCounter() == 0x0001517F)				     //当前时间是23:59:59时 复位为0:0:0 	    
    {
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
	  PWR->CR|=1<<8;                  					 //取消备份区写保护

	  RTC_EnterConfigMode();						     //允许配置 	  				
	  RTC_WaitForLastTask();                             //等待上一次对RTC寄存器的写操作是否已经完成 
      RTC_SetCounter(0x0);								 //写入复位值
      RTC_WaitForLastTask();							 //等待上一次对RTC寄存器的写操作是否已经完成 
	  CalenderCount();
	  BKP_WriteBackupRegister(BKP_DR2,GetYear());
	  BKP_WriteBackupRegister(BKP_DR3,GetMonth());  
	  BKP_WriteBackupRegister(BKP_DR4,GetDay());
	   
    }
	else if(RTC_GetCounter() > 0x0001517F)				 //当再次上电后计数值超过0x00015180, 复位为当前值取模0x00015180。	    
    {
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
	  PWR->CR|=1<<8;                                     //取消备份区写保护
	  RTC_EnterConfigMode();			                 //允许配置 
	  RTC_WaitForLastTask();                             //等待上一次对RTC寄存器的写操作是否已经完成    
      RTC_SetCounter(RTC_GetCounter()%0x0001517F);		 //写入复位值
      RTC_WaitForLastTask();							 //等待上一次对RTC寄存器的写操作是否已经完成 
	   CalenderCount();
	  BKP_WriteBackupRegister(BKP_DR2,GetYear());
	  BKP_WriteBackupRegister(BKP_DR3,GetMonth());  
	  BKP_WriteBackupRegister(BKP_DR4,GetDay());
	  
	     
    }
  }
        
}


你可能感兴趣的:(stm32,ARM,RTC,pwr,bkp)