GD32F470的RTC时间设置和获取,以及其中BCD码的坑

6bb6ef607ca206b846707633ba7713e8.png

一,背景知识

1、RTC时钟源:有三种:IRC32K,内部低速时钟源,不精确,温漂大;LXTAL:外部低速时钟源,32.768KHz,精度高;HXTAL:外部高速时钟源。

2、实时时钟 (RTC) 是一个独立的 BCD 定时器/计数器;32 位寄存器包含 BCD 格式的秒、分钟、小时(12 或 24 小时制)、星期几、日期、月份和年份。

3、BCD进制:BCD进制是便于人们快速进行二进制和十进制之间的转换产生的,是由4位bit表示十进制中的0~9。4位bit可以表示的范围是2^4=16,所以BCD进制也有几种分类:

8421码:因为从左到右这4位bit,每位为1其他位为0时分别对应十进制数值8/4/2/1,以此得名。

9d919b8b150a4c7bc5b7353ee66a141c.png

余3码:在8421码的基础上偏移3

011f71880e047a8676c71446d48e5e26.png

2421码:

beec6ea0da8d0f6f961884d59ba3b04d.png

二、RTC初始化

初始化部分按照GD的RTC例程,去掉提示信息printf这些不需要的东西。初始化先随意设置一个时间,第二章节会讲解如何按照用户需求设定时间。

1、宏定义和全局变量:

选定RTC的时钟源--LXTAL,定义RTC的句柄全局变量。

#define RTC_CLOCK_SOURCE_LXTAL  //使用外部32.768K晶振
#define BKP_VALUE    0x32F1
 
rtc_parameter_struct   rtc_initpara; //RTC句柄

2、RTC的初始化: 

使能寄存器写入,选择时钟源,设置分频值产生1Hz时钟频率:时钟源频率/( prescaler_a * prescaler_s)=32768/(0x7f * 0xff)=1,使能外设时钟。 

void RTC_Init(void)
{
    /* enable PMU clock */
    rcu_periph_clock_enable(RCU_PMU);
    /* enable the access of the RTC registers */
    pmu_backup_write_enable();
 
    rtc_pre_config();
    /* get RTC clock entry selection */
    RTCSRC_FLAG = GET_BITS(RCU_BDCTL, 8, 9);
 
    /* check if RTC has aready been configured */
    if ((BKP_VALUE != RTC_BKP0) || (0x00 == RTCSRC_FLAG))
    {
        /* backup data register value is not correct or not yet programmed
        or RTC clock source is not configured (when the first time the program 
        is executed or data in RCU_BDCTL is lost due to Vbat feeding) */
        rtc_setup(); 
    }
 
    rcu_all_reset_flag_clear();
}
 
/*!
    \brief      RTC configuration function
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rtc_pre_config(void)
{
    #if defined (RTC_CLOCK_SOURCE_IRC32K) 
          rcu_osci_on(RCU_IRC32K);
          rcu_osci_stab_wait(RCU_IRC32K);
          rcu_rtc_clock_config(RCU_RTCSRC_IRC32K);
  
          prescaler_s = 0x13F;
          prescaler_a = 0x63;
    #elif defined (RTC_CLOCK_SOURCE_LXTAL)
          rcu_osci_on(RCU_LXTAL);
          rcu_osci_stab_wait(RCU_LXTAL);
          rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
    
          prescaler_s = 0xFF;
          prescaler_a = 0x7F;
    #else
    #error RTC clock source should be defined.
    #endif /* RTC_CLOCK_SOURCE_IRC32K */
 
    rcu_periph_clock_enable(RCU_RTC);
    rtc_register_sync_wait();
}
 
/*!
    \brief      use hyperterminal to setup RTC time and alarm
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rtc_setup(void)
{
    /* setup RTC time value */
    uint32_t tmp_hh = 0x00, tmp_mm = 0x00, tmp_ss = 0x00;
 
    rtc_initpara.factor_asyn = prescaler_a;
    rtc_initpara.factor_syn = prescaler_s;
    rtc_initpara.year = 0x16;
    rtc_initpara.day_of_week = 0;
    rtc_initpara.month = RTC_APR;
    rtc_initpara.date = 0x30;
    rtc_initpara.display_format = RTC_24HOUR;
    rtc_initpara.am_pm = RTC_AM;
 
    
        rtc_initpara.hour = tmp_hh;
        rtc_initpara.minute = tmp_mm;
        rtc_initpara.second = tmp_ss;
    
 
    /* RTC current time configuration */
    if(ERROR != rtc_init(&rtc_initpara))
    {
        RTC_BKP0 = BKP_VALUE;
    }
}

三、设置时间和获取时间

1、BCD和二进制转换函数:

/**
  * @brief  Convert a 2 digit decimal to BCD format.
  * @param  Value: Byte to be converted
  * @retval Converted byte
  */
uint8_t byte_to_bcd(uint8_t Value)
{
  uint32_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 bcd_to_byte(uint8_t Value)
{
  uint32_t tmp = 0;
  tmp = ((uint8_t)(Value & (uint8_t)0xF0) >> (uint8_t)0x4) * 10;
  return (tmp + (Value & (uint8_t)0x0F));
}

 2、设置指定时间:

注意一定要将年月日时分秒等参数从二进制转换成BCD,再赋值给rtc_initpara

typedef struct
{
uint8_t year;
uint8_t month;
uint8_t date;
uint8_t hour;
uint8_t minute;
uint8_t second;
}S_RTCTimeInfo;//均为二进制格式
 
S_RTCTimeInfo RTCTime;//RTC全局变量,存储外界输入的待设定的时间
 
 
/*需要设定时间时调用此函数,将时间赋值给RTC句柄*/
void RtcSetTime(S_RTCTimeInfo time)
{
   rtc_initpara.year = byte_to_bcd(time.year);
   rtc_initpara.month= byte_to_bcd(time.month);
   rtc_initpara.date= byte_to_bcd(time.date);
   rtc_initpara.hour= byte_to_bcd(time.hour);
   rtc_initpara.minute= byte_to_bcd(time.minute);
   rtc_initpara.second= byte_to_bcd(time.second);
 
   rtc_init(&rtc_initpara);
}

3、获取当前时间:

void RtcGetTime(void)
{
  rtc_current_time_get(&rtc_initpara);
 
  RTCTime.year = bcd_to_byte(rtc_initpara.year);
  RTCTime.month= bcd_to_byte(rtc_initpara.year);
  RTCTime.date= bcd_to_byte(rtc_initpara.year);
  RTCTime.hour= bcd_to_byte(rtc_initpara.year);
  RTCTime.minute= bcd_to_byte(rtc_initpara.year);
  RTCTime.second= bcd_to_byte(rtc_initpara.year);
}

四、遇到的问题

1、如果不使用周几,可以将rtc_initpara.day_of_week设为0,这样就不启用这项了。如果初始化时设置了day_of_week,后续修改设定时间时没有将这项对应修改,会造成设置的时间不准。因为设定时间赋值后会进行rtc_init,day_of_week会影响reg_date。

/*gd32f4xx_rtc.c文件中*/
ErrStatus rtc_init(rtc_parameter_struct *rtc_initpara_struct)
{
    ErrStatus error_status = ERROR;
    uint32_t reg_time = 0U, reg_date = 0U;
 
    reg_date = (DATE_YR(rtc_initpara_struct->year) | \
                DATE_DOW(rtc_initpara_struct->day_of_week) | \
                DATE_MON(rtc_initpara_struct->month) | \
                DATE_DAY(rtc_initpara_struct->date));
...省略部分代码
}

2、当没有使用宏定义,而是使用数字给rtc_initpara 的年月日时分秒赋值时,一定要进行二进制到BCD的转换。对于此问题,STM32H7的HAL库就封装的很人性化,HAL库函数提供了可以选择使用哪种进制,,用户只需要选择对应的进制,传入参数即可,不需要自己进行进制转换。

/*stm32h7xx_hal_rtc.c文件*/
 
/**
  * @brief  Set RTC current date.
  * @param  hrtc: RTC handle
  * @param  sDate: Pointer to date structure
  * @param  Format: specifies the format of the entered parameters.
  *          This parameter can be one of the following values:
  *            @arg RTC_FORMAT_BIN: Binary data format 
  *            @arg RTC_FORMAT_BCD: BCD data format
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format);

==========

往期回顾:

嵌入式软件工程师常用的

单片机的RTC获取网络时间

我都不知道的PWM的很偏的问题

一个小细节,精度提供一大截

蓝桥杯物联网教程汇总

==========

原文:点击阅读原文

作者:半路程序媛

平台:CSDN

70a098f0269d645da9102bc69b8a4938.png

e0431cd1a5b675bbf2ce1687950169f6.png

d5bb140820b67ebe7c0530b258a1abde.png

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值