一種可行的STM32F103外設RTC使用方法

前言

最近做的項目需要用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);
      }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章