29_RTC实时时钟实验

目录

RTC工作原理框图

配置RTC寄存器

配置过程:

读RTC寄存器

RTC相关库函数讲解

RTC配置一般步骤

实验源码


RTC工作原理框图

 工作原理上一篇博客有详细讲解。

配置RTC寄存器

必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、 RTC_ALR寄存器。另外,对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是'1'时,才可以写入RTC寄存器。

配置过程:

1.查询RTOFF位,直到RTOFF的值变为'1”

2.置CNF值为1,进入配置模式

3.对一个或多个RTC寄存器进行写操作

4.清除CNF标志位,退出配置模式

5.查询RTOFF,直至RTOFF位变为'1'以确认写操作已经完成。

仅当CNF标志位被清除时,写操作才能进行,这个过程至少需要3个RTCCLK周期。

读RTC寄存器

RTC核完全独立于RTC APB1

软件通过APB1接口访问RTC的预分频值、计数器值和闹钟值。但是,相关的可读寄存器只在与RTC APB1时钟进行重新同步的RTC时钟的上升沿被更新。RTC标志也是如此的。"这意味着,如果APB1接门曾经被关闭,而读操作又是在刚刚重新开启APB1之后,则在第一次的内部寄存器更新之前,从APB1上读出的RTC寄存器数值可能被破坏了(通常读到0)。下述几种情况下能够发生这种情形:

发生系统复位或电源复位系统刚从待机模式唤醒。

系统刚从停机模式唤醒。

所有以上情况中APB1接口被禁止时(复位、无时钟或断电)RTC核仍保持运行状态。因此,若在读取RTC寄存器时, RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置'1'。

RTC相关库函数讲解

RTC时钟源和时钟操作函数:

void RCC_RTCCLKConfig(uint32_t CLKSource): //时钟源选择

void RCC_RTCCLKCmd(FunctionalState NewState)//时钟使能

RTC配置函数(预分频,计数值:

void RTC_SetPrescaler(uint32_t PrescalerValue);//预分频配置: PRLH/PRLL

void RTC_SetCounter(uint32_t CounterValue): //设置计数器值: CNTH/CNTL

void RTC SetAlarm(uint32 tAlarmValue); //闹钟设置: ALRH/ALRL

RTC中断设置函数:

void RTC_ITConfig(uint16_tRTC_IT, FunctionalState NewState);//CRH

RTC允许配置和退出配置函数:

void RTC_EnterConfigMode(void);//允许RTC配置:CRL位 CNF

void RTC_ExitConfigMode(void);//退出配置模式:CRL位 CNF

同步函数:

void RTC_WaitForLastTask(void); //等待上次操作完成: CRL位RTOFF

void RTC_WaitForSynchro(void); //等待时钟同步: CRL位RSF

相关状态位获取清除函数:

FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);

void RTC_ClearFlag(uint16_t RTC_FLAG);

ITStatus RTC_GetITStatus(uint16_t RTC_IT);

void RTC_ClearlTPendingBit(uint16_t RTC_IT);

其他相关函数(BKP等)

PWR_BackupAccessCmd();//BKP后备区域访问使能

RCC_APB1 PeriphClockCmd();//使能PWR和BKP时钟

RCC LSEConfig();//开启LSE, RTC选择LSE作为时钟源

RTC配置一般步骤

使能PWR和BKP时钟: RCC_APB1PeriphClockCmd();

使能后备寄存器访问: PWR_BackupAccessCmd();

配置RTC时钟源,使能RTC时钟:

RCC_RTCCLKConfig();

RCC_RTCCLKCmd();

如果使用LSE,要打开LSE: RCC_LSEConfig(RCC_LSE_ON);

设置RTC预分频系数: RTC_SetPrescaler();

设置时间: RTC_SetCounter();

开启相关中断(如果需要) RTC_ITConfig();

编写中断服务函数: RTC_IRQHandler();

部分操作要等待写操作完成和同步。

RTC_WaltForLastTask();//等待最近一次对RTC寄存器的写操作完成

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

实验源码

/**
  ******************************************************************************
  * @file           : user_rcc_config.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "user_rcc_config.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/

/*!
	\brief		RCC配置
	\param[in]	none
	\param[out]	none
	\retval 	none
*/
void Rcc_config(void)
{	
	/*使能PWR时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
	/*使能BKP外设时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
	/*使能后备寄存器才可以访问*/
	PWR_BackupAccessCmd(ENABLE);	  
	
	/*使能GPIOA时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	/*使能UART1时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

}

/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_gpio.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "user_gpio.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/

/*!
	\brief		GPIO初始化函数
	\param[in]	none
	\param[out]	none
	\retval 	none
*/
void Gpio_Init(void)
{	
	/*GPIO结构体*/
	GPIO_InitTypeDef GPIO_InitTypeDefstruct;
	
	/*UART1发送引脚配置*/
	GPIO_InitTypeDefstruct.GPIO_Mode  = GPIO_Mode_AF_PP;//推挽复用输出
	GPIO_InitTypeDefstruct.GPIO_Pin   = GPIO_Pin_9;
	GPIO_InitTypeDefstruct.GPIO_Speed =	GPIO_Speed_10MHz;
	/*写入结构体到GPIOA*/
	GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
	
	/*UART1接收引脚配置*/
	GPIO_InitTypeDefstruct.GPIO_Mode  = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitTypeDefstruct.GPIO_Pin   = GPIO_Pin_10;
	GPIO_InitTypeDefstruct.GPIO_Speed =	GPIO_Speed_10MHz;
	/*写入结构体到GPIOA*/	
	GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
		
}

/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_uart.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "user_uart.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/

extern uint16_t USART_RX_STA;
extern uint8_t USART_RX_BUF[200];


/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/
#if 1
#pragma import(__use_no_semihosting)  
/*实现Printf代码*/
struct __FILE 
{ 
	int handle; 

}; 
FILE __stdout;       

void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 




/*!
	\brief		UART1初始化
	\param[in]	none
	\param[out]	none
	\retval 	none
*/

void Uart1_Init(u32 bound)
{
	/*UART结构体*/
	USART_InitTypeDef USART_InitTypeDefstruct;
	
	/*UART结构体配置*/
	USART_InitTypeDefstruct.USART_BaudRate = bound; //波特率
	USART_InitTypeDefstruct.USART_HardwareFlowControl =USART_HardwareFlowControl_None; //不使用硬件流
	USART_InitTypeDefstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送接收使能
	USART_InitTypeDefstruct.USART_Parity = USART_Parity_No; //不使用奇偶校验
	USART_InitTypeDefstruct.USART_StopBits = USART_StopBits_1; //1个停止位
	USART_InitTypeDefstruct.USART_WordLength = USART_WordLength_8b; //8个数据位
	/*写入USART1*/
	USART_Init(USART1,&USART_InitTypeDefstruct);
	
	/*使能串口1*/
	USART_Cmd(USART1,ENABLE);

}


/*!
	\brief		UART1中断服务函数
	\param[in]	none
	\param[out]	none
	\retval 	none
*/

void USART1_IRQHandler(void)
{

}
	

/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_rtc.h
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Define to prevent recursive incluson---------------------------------------*/
#ifndef _USER_RTC_H__
#define _USER_RTC_H__

/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include "user_delay.h"
#include "user_uart.h"
/* Typedef 类型----------------------------------------------------------------*/
typedef struct
{
	uint8_t 	hour; //时
	uint8_t 	min;  //分
	uint8_t 	sec;  //秒
	
	uint16_t 	year; //年
	uint8_t		month;//月
	uint8_t 	date; //日
	uint8_t 	week; //周
}Calendar;

/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/


uint8_t Rtc_Init(void);

#endif

/************************************************************** END OF FILE ****/
/**
  ******************************************************************************
  * @file           : user_rtc.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "user_rtc.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/

/*时间结构体*/
Calendar timer;

/* Constants 常量--------------------------------------------------------------*/
/*月修正数据表*/
uint8_t const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; 	  
/*平年的月份日期表*/
const uint8_t mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
/* Function  函数--------------------------------------------------------------*/


/*!
	\brief		判断是否是闰年函数
	\param[in]	年份
	\param[in]	none
	\retval 	该年份是不是闰年.1,是.0,不是
*/
uint8_t 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;	
}
/*!
	\brief		获得现在是星期几函数
	\param[in]	输入公历日期得到星期(只允许1901-2099年)年月日 
	\param[in]	none
	\retval 	星期号
*/
uint8_t 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);
}	


/*!
	\brief		得到当前的时间函数
	\param[in]	none
	\param[in]	none
	\retval 	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++;  
		}   
		timer.year=temp1;//得到年份
		temp1=0;
		while(temp>=28)//超过了一个月
		{
			if(Is_Leap_Year(timer.year)&&temp1==1)//当年是不是闰年/2月份
			{
				if(temp>=29)temp-=29;//闰年的秒钟数
				else break; 
			}
			else 
			{
				if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
				else break;
			}
			temp1++;  
		}
		timer.month=temp1+1;	//得到月份
		timer.date=temp+1;  	//得到日期 
	}
	temp=timecount%86400;     		//得到秒钟数   	   
	timer.hour=temp/3600;     	//小时
	timer.min=(temp%3600)/60; 	//分钟	
	timer.sec=(temp%3600)%60; 	//秒钟
	timer.week=RTC_Get_Week(timer.year,timer.month,timer.date);//获取星期   
	return 0;
}	 




/*!
	\brief		设置时钟以1970年1月1日为基准
	\param[in]	年月日时分秒
	\param[in]	none
	\retval 	1是大于2099错误,0设置成功
*/
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;	    
}


/*!
	\brief		RTC初始化
	\param[in]	none
	\param[in]	none
	\retval 	1失败,0成功
*/
uint8_t Rtc_Init(void)
{	
	/*中断结构体*/
	NVIC_InitTypeDef NVIC_InitStructure;
	
	uint8_t Value = 0;
	
	/*RTC只需要初初始化一次,因为断电后备份寄存器还是会保存,
	这里的4040可以自己定义,根据上次初始化成功后写入的值判断
	是否还需要初始化*/
	if (BKP_ReadBackupRegister(BKP_DR1) != 0x4040)		
	{	
		/*复位备份区域 */
		BKP_DeInit();		
		/*设置外部低速晶振(LSE),使用外设低速晶振*/
		RCC_LSEConfig(RCC_LSE_ON);
		/*检查指定的RCC标志位设置与否,等待低速晶振就绪*/
		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&Value<250)	
		{
			/*等待2秒*/
			Value++;
			delay_ms(10);
		}
		if(Value >= 250)
		{
			/*初始化失败,晶振异常返回1*/
			return 1; 
		}
		/*设置RTC时钟(RTCCLK),选择LSE作为RTC时钟32.768kHz*/
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
		/*使能RTC时钟*/
		RCC_RTCCLKCmd(ENABLE);
		/*等待最近一次对RTC寄存器的写操作完成*/
		RTC_WaitForLastTask();
		/*等待RTC寄存器同步*/
		RTC_WaitForSynchro();
		/*使能RTC秒中断*/		
		RTC_ITConfig(RTC_IT_SEC, ENABLE);
		/*使能RTC秒中断*/
		RTC_ITConfig(RTC_IT_SEC, ENABLE);	
		/*等待最近一次对RTC寄存器的写操作完成*/
		RTC_WaitForLastTask();		
		/*允许配置*/
		RTC_EnterConfigMode();
		/*设置RTC预分频重装载值
		32.768Khz/32767+1 = 1Hz*/
		RTC_SetPrescaler(32767); 
		/*等待最近一次对RTC寄存器的写操作完成*/
		RTC_WaitForLastTask();
		/*设置时间*/
		Rtc_Set(2022,12,5,21,25,10);
		/*退出配置模式*/
		RTC_ExitConfigMode(); 
		/*备份寄存器1里写入0x4040*/
		BKP_WriteBackupRegister(BKP_DR1, 0x4040);
	}
	/*不需要重新配置*/
	else
	{
		/*等待最近一次对RTC寄存器的写操作完成*/
		RTC_WaitForSynchro();		
		/*使能RTC秒中断*/
		RTC_ITConfig(RTC_IT_SEC, ENABLE);	
		/*等待最近一次对RTC寄存器的写操作完成	*/
		RTC_WaitForLastTask();
	
	}
	/*中断NVIC配置*/
	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);	
	
	/*初始化*/
	return 0; 

}


/*!
	\brief		RTC时钟中断
	\param[in]	none
	\param[in]	none
	\retval 	none
*/
void RTC_IRQHandler(void)
{		 
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
	{	/*时间写入结构体*/						
		Rtc_Get();//更新时间 
		/*打印当前时间*/
		printf("当前时间:%d-%d-%d %d:%d:%d\r\n",timer.year,timer.month,timer.date,timer.hour,timer.min,timer.sec);
 	}
	/*清1s中断*/	  								 
	RTC_ClearITPendingBit(RTC_IT_SEC);
	/*等待最近一次对RTC寄存器的写操作完成*/	
	RTC_WaitForLastTask();	  	    						 	   	 
}

/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_mian.h
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "user_rtc.h"


/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
//最多一次接收200个字节
uint8_t USART_RX_BUF[200];
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
uint16_t USART_RX_STA=0;       //接收状态标记	 
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/


 int main(void)
 {	

	/*配置系统中断分组为2位抢占2位响应*/
	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	 /*延时函数初始化*/
	 delay_init();
	/*RCC配置*/
	 Rcc_config();
	/*GPIO初始化*/ 
	 Gpio_Init();
	/*USART1初始化*/
	 Uart1_Init(9600);
	 /*初始化RTC*/
	 if(Rtc_Init())
		{
			printf("初始化Rtc失败\r\n");
		}
	 
	/*死循环*/ 
	 while(1){
	 } 
		

}
 
 /************************************************************** END OF FILE ****/

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值