目录

Mbed使用Hal库获得毫秒级时间戳

前言

硬件开发板:

STM32F429

软件链接:

编译调试工具 Mbed Studio

参考 Mbed RTC Api

根据Mbed RTC Api获得的时间戳只能精确到秒,并且在中断中无法使用time(NULL),无法满足实际需要,故设法直接使用Hal库获得毫秒级时间戳,并总结记录此文。

加载相关Hal库

#include “stm32f4xx_hal.h”

mbed-os\targets\TARGET_STM\TARGET_STM32F4\STM32Cube_FW\STM32F4xx_HAL_Driver目录下,主要使用HAL_RTC_GetDate与HAL_RTC_GetTime两个函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
  * @brief  Gets RTC current date.
  * @param  hrtc pointer to a RTC_HandleTypeDef structure that contains
  *                the configuration information for RTC.
  * @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
  * @note You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values
  * in the higher-order calendar shadow registers to ensure consistency between the time and date values.
  * Reading RTC current time locks the values in calendar shadow registers until Current date is read.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format);

/**
  * @brief  Gets RTC current time.
  * @param  hrtc pointer to a RTC_HandleTypeDef structure that contains
  *                the configuration information for RTC.
  * @param  sTime Pointer to Time 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
  * @note  You can use SubSeconds and SecondFraction (sTime structure fields returned) to convert SubSeconds
  *        value in second fraction ratio with time unit following generic formula:
  *        Second fraction ratio * time_unit= [(SecondFraction-SubSeconds)/(SecondFraction+1)] * time_unit
  *        This conversion can be performed only if no shift operation is pending (ie. SHFP=0) when PREDIV_S >= SS
  * @note You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values
  *        in the higher-order calendar shadow registers to ensure consistency between the time and date values.
  *        Reading RTC current time locks the values in calendar shadow registers until current date is read.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);

调用顺序应该是先调用HAL_RTC_GetTime,再调用HAL_RTC_GetDate,否则不能解锁,会被锁死。

#include “rtc_api_hal.h”

mbed-os\targets\TARGET_STM目录下,主要是获取PREDIV_S_VALUE相关定义。

1
2
3
4
5
6
7
/* PREDIV_A : 7-bit asynchronous prescaler */
/* PREDIV_A is set to the maximum value to improve the consumption */
#define PREDIV_A_VALUE 127

/* PREDIV_S : 15-bit synchronous prescaler */
/* PREDIV_S is set in order to get a 1 Hz clock */
#define PREDIV_S_VALUE (RTC_CLOCK / (PREDIV_A_VALUE + 1) - 1)

Mbed 默认PREDIV_A_VALUE为3,PREDIV_S_VALUE为8191,RTC_PRER_PREDIV_S为32767

若调整同步/异步预分频值,需要重新初始化。

1
2
3
4
5
6
7
8
9
PREDIV_A_VALUE = 0x1F;  //31
PREDIV_S_VALUE = 0x3FF; //1023
RtcHandle.Instance = RTC;
RtcHandle.Init.HourFormat     = RTC_HOURFORMAT_24;
RtcHandle.Init.AsynchPrediv   = PREDIV_A_VALUE;   //默认RtcHandle.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
RtcHandle.Init.SynchPrediv    = PREDIV_S_VALUE;
if (HAL_RTC_Init(&RtcHandle) != HAL_OK) {
    error("RTC initialization failed\n");
}

获得秒级时间戳

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
uint32_t get_timestamp() {
  uint32_t Result;
  if (HAL_OK == HAL_RTC_GetTime(&hRTC, &RTC_Time, RTC_FORMAT_BIN) && HAL_OK == HAL_RTC_GetDate(&hRTC, &RTC_Date, RTC_FORMAT_BIN)) {
    uint16_t Year = RTC_Date.Year + 1900 + 68; //使Year获得值为当前年份
    Result = (Year - 1970) * 365 * 24 * 3600 +(monDays[RTC_Date.Month - 1] + RTC_Date.Date) * 24 * 3600 +(RTC_Time.Hours) * 3600 + RTC_Time.Minutes * 60 + RTC_Time.Seconds;
    Result += (RTC_Date.Month > 2 && (Year % 4 == 0) &&(Year % 100 != 0 || Year % 400 == 0)) *24 * 3600; //闰月
    Year -= 1970;
    Result += (Year / 4 - Year / 100 + Year / 400) * 24 * 3600; //闰年
  }
  return Result;
}

获得毫秒级时间戳

亚秒转化为毫秒 (PREDIV_S_VALUE - 亚秒) * 999 / PREDIV_S_VALUE,返回 0 ~ 999。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
uint64_t get_timestamp_ms() {
  uint64_t Result;
  if (HAL_OK == HAL_RTC_GetTime(&hRTC, &RTC_Time, RTC_FORMAT_BIN) && HAL_OK == HAL_RTC_GetDate(&hRTC, &RTC_Date, RTC_FORMAT_BIN)) {
    uint16_t Year = RTC_Date.Year + 1900 + 68; //使Year获得值为当前年份
    Result = (Year - 1970) * 365 * 24 * 3600 +(monDays[RTC_Date.Month - 1] + RTC_Date.Date) * 24 * 3600 +(RTC_Time.Hours) * 3600 + RTC_Time.Minutes * 60 + RTC_Time.Seconds;
    Result += (RTC_Date.Month > 2 && (Year % 4 == 0) &&(Year % 100 != 0 || Year % 400 == 0)) *24 * 3600; //闰月
    Year -= 1970;
    Result += (Year / 4 - Year / 100 + Year / 400) * 24 * 3600; //闰年
    Result = Result * 1000 + uint64_t((PREDIV_S_VALUE - RTC_Time.SubSeconds) * 999 / PREDIV_S_VALUE);
  }
  return Result;
}

RTC时间戳扩展

mbed-os\targets\TARGET_STM\TARGET_STM32F4\STM32Cube_FW\STM32F4xx_HAL_Driver目录下stm32f4xx_hal_rtc_ex.h

RTC的时间戳,通过PC13引脚的上升沿或者下降沿可以触发时间戳的中断函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
  * @brief  Sets TimeStamp with Interrupt.
  * @param  hrtc pointer to a RTC_HandleTypeDef structure that contains
  *                the configuration information for RTC.
  * @note   This API must be called before enabling the TimeStamp feature.
  * @param  TimeStampEdge Specifies the pin edge on which the TimeStamp is
  *         activated.
  *          This parameter can be one of the following values:
  *             @arg RTC_TIMESTAMPEDGE_RISING: the Time stamp event occurs on the
  *                                        rising edge of the related pin.
  *             @arg RTC_TIMESTAMPEDGE_FALLING: the Time stamp event occurs on the
  *                                         falling edge of the related pin.
  * @param  RTC_TimeStampPin Specifies the RTC TimeStamp Pin.
  *          This parameter can be one of the following values:
  *             @arg RTC_TIMESTAMPPIN_DEFAULT: PC13 is selected as RTC TimeStamp Pin.
  *             @arg RTC_TIMESTAMPPIN_PI8: PI8 is selected as RTC TimeStamp Pin. (not applicable in the case of STM32F446xx, STM32F412xx, STM32F413xx and STM32F423xx devices)
  *             @arg RTC_TIMESTAMPPIN_PA0: PA0 is selected as RTC TimeStamp Pin only for STM32F446xx devices
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_RTCEx_SetTimeStamp_IT(RTC_HandleTypeDef *hrtc, uint32_t TimeStampEdge, uint32_t RTC_TimeStampPin);
/**
  * @brief  This function handles TimeStamp interrupt request.
  * @param  hrtc pointer to a RTC_HandleTypeDef structure that contains
  *                the configuration information for RTC.
  * @retval None
  */
void HAL_RTCEx_TamperTimeStampIRQHandler(RTC_HandleTypeDef *hrtc);

结束语

因为硬件引脚触发中断获得时间戳的通用性不强,所以通常还是使用计算获得RTC时间戳。