前言
最近做的项目需要用RTC功能,记录掉上电时间。然后就开始琢磨STM32的RTC,在使用的过程中出现各种问题。搞的很是头痛。几经折腾,终于弄出一种稳定的使用方法。刚开始最大的问题就是掉电后时钟不走,代码改来该去,最后发现不管是第一次初始化还是每次上电运行,都需要打开PWR和BKP时钟。下面就把我的代码全贴出来,可以直接调用。
代码
#ifndef BSP_RTC_H
#define BSP_RTC_H
#include "stm32f10x.h"
#include <time.h>
//BCD码表示时间
typedef struct systemtime_tag
{
unsigned char year;
unsigned char month;
unsigned char day;
unsigned char hour;
unsigned char minute;
unsigned char second;
}SystemTime_Type;
void bsp_RTC_Init(void);//RTC初始化
void Time_SetUnixTime(time_t t);//将给定的unix时间戳写入RTC
u32 Time_GetUnixTime(void);//从RTC取当前unix 时间格式值
struct tm Time_ConvUnixToCalendar(time_t t);//转换unix时间戳为日历时间
u32 Time_ConvCalendarToUnix(struct tm t);//将给定的公元格式时间转换为unix时间戳
void Time_SetCalendarTime(struct tm t);//设置当前日历时间
void SetSysTime(SystemTime_Type time);//设置系统时间
void GetSysTime(SystemTime_Type* sys_time);//获取系统时间
uint8_t RTC_ByteToBcd2(uint8_t Value);//1字节转BCD码
uint8_t RTC_Bcd2ToByte(uint8_t Value);//BCD码转字节
#endif
RTC初始化时要写一个备份寄存器的标志位,以便容易识别是否是第一次初始化。rtc只需要初始化一次就可以了。当然也可以写一个标志在FLASH中,同样的作为初始化标志。该时间换算,使用的是unix系统时间戳。具体的介绍可以百度一下。该换算的时间没有星期几表示。因为我不需要这个显示,也就没有定义它。
#include "bsp_rtc.h"
void bsp_RTC_Init(void)
{
SystemTime_Type time={0x17,0x06,0x22,0x15,0x10,0x00};
if(BKP_ReadBackupRegister(BKP_DR1)!=0xAA55)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP| RCC_APB1Periph_PWR,ENABLE);
PWR_BackupAccessCmd(ENABLE);//使能RTC和后备寄存器访问
BKP_DeInit();//
RCC_LSEConfig(RCC_LSE_ON);//启动外部低速晶振
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET);//等待外部低速晶振重启
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_WaitForSynchro(); //等待RTC寄存器同步完成
RTC_WaitForLastTask(); //等待最后一次对RTC的寄存器写操作完成
RTC_ITConfig(RTC_IT_SEC,ENABLE); //使能秒中断
RTC_WaitForLastTask();
RTC_SetPrescaler(32767);//设置RTC时钟分频值
RTC_WaitForLastTask();
SetSysTime(time);//初始化系统时钟
BKP_WriteBackupRegister(BKP_DR1, 0xAA55);//目的是标识是否是第一次配置
}
else
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP| RCC_APB1Periph_PWR,ENABLE);
/* Wait for RTC registers synchronization */
RTC_WaitForSynchro();
/* Enable the RTC Second */
RTC_ITConfig(RTC_IT_SEC, ENABLE);
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
}
PWR_BackupAccessCmd(ENABLE);//
/* Clear reset flags */
RCC_ClearFlag();
}
/*******************************************************************************
******************************************************************************/
void Time_SetUnixTime(time_t t)
{
RTC_WaitForLastTask();
RTC_EnterConfigMode();
RTC_SetCounter((u32)t);
RTC_WaitForLastTask();
return ;
}
/*******************************************************************************
******************************************************************************/
u32 Time_GetUnixTime(void)
{
return (u32)RTC_GetCounter();
}
/*******************************************************************************
******************************************************************************/
struct tm Time_ConvUnixToCalendar(time_t t)
{
struct tm* t_tm;
t_tm=localtime(&t);
t_tm->tm_year +=1900;
return *t_tm;
}
/*******************************************************************************
******************************************************************************/
u32 Time_ConvCalendarToUnix(struct tm t)
{
t.tm_year -= 1900;
return mktime(&t);
}
/*******************************************************************************
******************************************************************************/
void Time_SetCalendarTime(struct tm t)
{
u32 tm;
tm=Time_ConvCalendarToUnix(t);
Time_SetUnixTime(tm);
}
void SetSysTime(SystemTime_Type time)
{
struct tm unix_time={0};
unix_time.tm_year=RTC_Bcd2ToByte(time.year)+2000;//转换unix时间
unix_time.tm_mon =RTC_Bcd2ToByte(time.month)-1;//time.h中定义的month是以0开始计算
unix_time.tm_mday=RTC_Bcd2ToByte(time.day);
unix_time.tm_hour=RTC_Bcd2ToByte(time.hour);
unix_time.tm_min =RTC_Bcd2ToByte(time.minute);
unix_time.tm_sec =RTC_Bcd2ToByte(time.second);
Time_SetCalendarTime(unix_time);
}
void GetSysTime(SystemTime_Type* sys_time)
{
u32 CurrenTime=0;
struct tm time_now={0};
CurrenTime=Time_GetUnixTime();
time_now=Time_ConvUnixToCalendar(CurrenTime);
sys_time->year =RTC_ByteToBcd2(time_now.tm_year%2000);//转换unix时间
sys_time->month =RTC_ByteToBcd2((uint8_t)time_now.tm_mon+1);//time.h中定义的month是以0开始计算
sys_time->day =RTC_ByteToBcd2((uint8_t)time_now.tm_mday);
sys_time->hour =RTC_ByteToBcd2((uint8_t)time_now.tm_hour);
sys_time->minute=RTC_ByteToBcd2((uint8_t)time_now.tm_min);
sys_time->second=RTC_ByteToBcd2((uint8_t)time_now.tm_sec);
}
/**
* @brief Converts a 2 digit decimal to BCD format.
* @param Value: Byte to be converted.
* @retval Converted byte
*/
uint8_t RTC_ByteToBcd2(uint8_t Value)
{
uint8_t bcdhigh = 0;
while (Value >= 10)
{
bcdhigh++;
Value -= 10;
}
return ((uint8_t)(bcdhigh << 4) | Value);
}
/**
* @brief Convert from 2 digit BCD to Binary.
* @param Value: BCD value to be converted.
* @retval Converted word
*/
uint8_t RTC_Bcd2ToByte(uint8_t Value)
{
uint8_t tmp = 0;
tmp = ((uint8_t)(Value & (uint8_t)0xF0) >> (uint8_t)0x4) * 10;
return (tmp + (Value & (uint8_t)0x0F));
}
秒中断时,要更新时间结构体。device.SysTime改成你自己的变量名。系统时间就保存在这里
void RTC_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_SEC)!=RESET)//秒中断
{
RTC_ClearITPendingBit(RTC_IT_SEC);
RTC_WaitForLastTask();
GetSysTime(&device.SysTime);
}
if(RTC_GetITStatus(RTC_IT_ALR)!=RESET)//闹钟中断
{
RTC_ClearITPendingBit(RTC_IT_ALR);
}
}