一种可行的STM32F103外设RTC使用方法

前言

最近做的项目需要用RTC功能,记录掉上电时间。然后就开始琢磨STM32的RTC,在使用的过程中出现各种问题。搞的很是头痛。几经折腾,终于弄出一种稳定的使用方法。刚开始最大的问题就是掉电后时钟不走,代码改来该去,最后发现不管是第一次初始化还是每次上电运行,都需要打开PWR和BKP时钟。下面就把我的代码全贴出来,可以直接调用。

代码

#ifndef BSP_RTC_H
#define BSP_RTC_H

#include "stm32f10x.h"
#include 
//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);
      }
}

你可能感兴趣的:(STM32,技术实践)