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-STM32F1-RTC and BKP
最新推荐文章于 2024-08-27 14:51:20 发布