前言
最近做的項目需要用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);
}
}