10-STM32F1-RTC and BKP

STM32F1-RTC and BKP

实验平台;正点原子精英板,部分内容来自原子哥的书

STM32 的实时时钟(RTC)是一个独立的定时器。 STM32 的 RTC 模块拥有一组连续计数的计数器,最大计数为4294967296 ,如果把计数器设置为秒计数,那么最大可以计时136年左右。这个计数器只是单纯的计数功能,如果要转化为年月日的话还是得手动转化,转换过程中还要考虑闰年问题等。
RTC 模块和时钟配置系统(RCC_BDCR 寄存器)是在后备区域,即在系统复位或从待机模式唤醒后 RTC 的设置和计数值(也就是当前时间)维持不变。但是在系统复位后,会自动禁止访问后备寄存器和 RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前, 先要取消备份区域(BKP)写保护。 
RTC由两部分组成,第一部分(APB1 接口)用来和 APB1 总线相连。此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。 APB1 接口由 APB1 总线时钟驱动,用来与 APB1 总线连接,所以设置RTC的时候要打开APB1时钟。
	另一部分(RTC 核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是 RTC 的预分频模块,它可编程产生 1 秒的 RTC 时间基准 TR_CLK。 RTC 的预分频模块包含了一个 20位的可编程分频器(RTC 预分频器)。如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个
TR_CLK 周期中 RTC 产生一个中断(秒中断)。第二个模块是一个 32 位的可编程计数器,就是上面所说的计数器。
RTC 还有一个闹钟寄存器 RTC_ALR,用于产生闹钟。系统时间按 TR_CLK 周期累加并与存储在 RTC_ALR 寄存器中的可编程时间相比较,如果 RTC_CR 控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。 
STM32F1的备份寄存器可以保存数据,这个数据掉电后是会丢失的,不能代替EEPROM。但是这个数据是用后备电源维持的,在主电源切断后还是不会丢失的。
RTC操作步骤;
1,打开APB1和PWR时钟
2,后备区域开锁
3,选择RTC时钟源,如果使用外部晶振的话还要打开使能等待相应的晶振起振
4,使能RTC配置允许
5,等待操作上一步完成
6,设置预分频
7,等待上一个操作完成
8,设置计数初值
9,等待上一个操作完成
10,关闭RTC配置允许
11,打开RTC中断(如果使能了中断的话)
12,配置完成,剩下的就是写中断函数了
13,中断函数;确认相应的中断,清除中断标志位。编写自己的中断处理函数

RTC配置举例;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);//要操作后备区域就得打开APB1和PWR时钟

	PWR_BackupAccessCmd(ENABLE);//操作后备区域是要开锁的

	BKP_DeInit();//配置前最好先复位下

这里使用的是外部的低速晶振,所以需要开启并等待晶振起振
	RCC_LSEConfig(RCC_LSE_ON);	//外部低速晶振是需要手动开启的
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==1);	//等待晶振起振

选择RTC时钟源,这里选择外部晶振
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);			
	RCC_RTCCLKCmd(ENABLE);	

在进行下一个操作前要等待上一个操作完成
	RTC_WaitForLastTask();
	RTC_WaitForSynchro();	 

进入RTC设置模式,这个和下面的关闭RTC设置模式是一对的,有开就有关
	RTC_EnterConfigMode();	

因为RTC使用的外部晶振是32.768k的,要产生1s的RTC时钟需要32768分频
	RTC_SetPrescaler(32767); 

老规矩,等待操作完成	
	RTC_WaitForLastTask();	

设置RTC计数器的值,这里只是单纯的计时,并没有转换为日历,所以这个值随便设,但是为了能计时更长的时间还是设置为0吧
	RTC_SetCounter(0);		

如果需要用RTC中断的话需要中断使能		
	RTC_ITConfig(RTC_IT_SEC, ENABLE);		

等待操作完成,
	RTC_WaitForLastTask();		
	RTC_ExitConfigMode();	

配置完成后就是中断函数了,
 void RTC_IRQHandler()
 {
	 if(RTC_GetFlagStatus(RTC_FLAG_SEC)==1)
	 {
		 RTC_ClearITPendingBit(RTC_FLAG_SEC);
		 //中断处理函数,该干嘛的就干嘛去
	 }
 }		

至此,RTC时钟初始化已经配置完毕了,下面是BKP的配置步骤
1,打开APB1和PWR时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);

2,后备区域开锁,好像这个锁不关也无所谓,反正复位后会自动上锁的
PWR_BackupAccessCmd(ENABLE);

3,往相应的备份寄存器写数据就好了,入口第一个参数是选择寄存器,第二个参数是数据
BKP_WriteBackupRegister(BKP_DR1,6666);

如果要读取备份寄存器的数据也很简单,入口就是选择读取哪个寄存器
BKP_ReadBackupRegister(BKP_DR1)

4,ok

本实验功能;设置RTC为秒计时并产生秒中断,在秒中断中通过串口向电脑发送从STM32F1上电开始到此刻的总秒数
代码如下;
#include "sys.h"
#include "delay.h"
#include "stdio.h"
/************************************************************
功能;RTC时钟产生秒中断,在中断中输出从上电开始到此刻的秒数

这里只是测试RTC秒中断功能,至于怎么把秒计时转化为年月日的话,这里就不演示了

串口接在PA9,PA10

****************************************************************/
void init__uart1();

void init_RTC()
{
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_InitStruct.NVIC_IRQChannel=RTC_IRQn;//中断通道设置为RTC
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;//子优先级
	NVIC_Init(&NVIC_InitStruct);//这个函数在misc.c文件里
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);//要操作后备区域就得打开APB1和PWR时钟
	PWR_BackupAccessCmd(ENABLE);//操作后备区域是要开锁的
	if(BKP_ReadBackupRegister(BKP_DR1)!=6666)//这个是为了避免非断电复位后重新配置RTC,但是在断电重启后还是会重新配置RTC的
	{
		BKP_DeInit();//配置前最好先复位下
		RCC_LSEConfig(RCC_LSE_ON);	//外部低速晶振是需要手动开启的
		while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==1);	//等待晶振起振
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);				//把外部晶振时钟作为RTC时钟
		RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_WaitForSynchro();		//等待RTC寄存器同步  
		RTC_EnterConfigMode();		/// 允许配置	
		RTC_SetPrescaler(32767); 	//设置RTC预分频的值,目前外部低速晶振就是32.768k的,所以32767分频后就是1s
		RTC_WaitForLastTask();		//等待最近一次对RTC寄存器的写操作完成
		RTC_SetCounter(0);				//这个值是年月日转换后的秒数,,,这里不使用年月日所以随便设置都无所谓,但是不能太大?
		RTC_ITConfig(RTC_IT_SEC, ENABLE);		//使能RTC秒中断
		RTC_WaitForLastTask();		//等待最近一次对RTC寄存器的写操作完成?
		RTC_ExitConfigMode();			//关闭允许配置
		
		BKP_WriteBackupRegister(BKP_DR1,6666);//备份寄存器的RTC配置标志位
	}
	else//如果RTC已经配置过了
	{
		RTC_WaitForSynchro();	//等待最近一次对RTC寄存器的写操作完成
		RTC_ITConfig(RTC_IT_SEC, ENABLE);	//使能RTC秒中断
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
	}
}


 int main(void)
 {		 
	delay_init();	//延时函数初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	init__uart1();//串口1初始化
	init_RTC();
	 
	while(1)
	{}//等待中断
 }
 
 void RTC_IRQHandler()
 {
	 static uint32_t sec_count=0;
	 if(RTC_GetFlagStatus(RTC_FLAG_SEC)==1)
	 {
		 RTC_ClearITPendingBit(RTC_FLAG_SEC);
		 printf("sec_count=%d\r\n",sec_count++);//串口输出从上电开始到目前所经过的秒数
	 }
 }
 
//*****************从这里分开,下面的部分是配置串口的
	 
 //串口中断处理函数
 void USART1_IRQHandler(void) 
 {
	 char re_data=0;//为了接收字符,还是定义为字符类型吧
	 if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==1)//确认下是不是串口1接收中断
	 {
			re_data=USART_ReceiveData(USART1);	//接收数据
			USART_SendData(USART1,re_data);			//发送接收到的数据
			while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); //等待数据发送完 成
	 }	 
	 //这个中断是不需要手动清除标志位的,因为读取数据后接收标志位会自动清零
 }
 
void init__uart1()
{
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef USART_InitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	//	串口IO配置,PA9,PA10
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//IO时钟打开
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//IO方式具体看《中文手册》8.1.11章节
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;//IO方式具体看《中文手册》8.1.11章节
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//配置串口1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//打开串口时钟
	
	USART_InitStruct.USART_BaudRate=115200;	//波特率115200
	USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;	//无硬件流
	USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;	//接收和发送都使能
	USART_InitStruct.USART_Parity=USART_Parity_No;						//无奇偶校验
	USART_InitStruct.USART_StopBits=USART_StopBits_1;					//停止位1位
	USART_InitStruct.USART_WordLength=USART_WordLength_8b;		//数据长度8位
	USART_Init(USART1,&USART_InitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;//虽然知道这个参数的意思,但是还真不知道这个参数放在哪里
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;//使能通道中断
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;//子优先级
	NVIC_Init(&NVIC_InitStruct);//这个函数在misc.c文件里
	
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//打开串口中断,第二个参数是选择中断类型,这里打开的是接收中断
	USART_Cmd(USART1,ENABLE);	//配置完成后一定要记得使能串口
}


//重定义fputc函数 ,想要使用printf函数得添加这个函数
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
 
		


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值