目录
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 ****/