嵌入式技术论坛
直播中

王银喜

7年用户 2390经验值
私信 关注
[经验]

ENV中硬件RTC如何去选择HSE时钟源呢

前景提要

我的板上LSE没有外接晶振,ENV中无奈只能选择LSI
LSI时钟源精度太差,不去改分频值的前提下,每1s有127ms的误差,难以接受
STM32是支持HSE时钟为RTC的时钟源
主要是用在ulog上,方便后续看日志找对应的时间点
先看结论

使用LSI时钟源

1.jpg

uart帧间隔为333ms,但ulog显示间隔为293ms
tcpsend帧间隔为2s,但ulog显示间隔为1.746s
这误差实在是有点大呀。。。

2.使用HSE时钟源

1.jpg

虽然误差还是有1~2ms的误差,但已经足够使用了
如果还要提高精度,hal库里有相应的校准函数,这里就不深入了

修改过程如下

1 修改board/kconfig

为了跟原来的程序兼容,这里添加一些自己的宏

menuconfig BSP_USING_ONCHIP_RTC
bool "Enable RTC"
select RT_USING_RTC
default n
if BSP_USING_ONCHIP_RTC
choice
prompt "Select clock source"
default BSP_RTC_USING_LSE
config BSP_RTC_USING_LSE
bool "RTC USING LSE"
config BSP_RTC_USING_LSI
bool "RTC USING LSI"
config BSP_RTC_USING_HSE_8M25DIV
bool "RTC USING HSE 8M 25DIV"
endchoice
endif

增加宏选择 BSP_RTC_USING_HSE_8M25DIV
然后ENV进memuconfig,选择新增加的宏,保存退出

1.jpg

2 修改cubemax RTC时钟源
我的板子是F4,晶振8M,看官自行选择

1.jpg

配置完后,右上角生成代码即可

  1. 修改drv_rtc.c
  2. 修改 stm32_rtc_init
    修改点:22行,HSE时钟已经起振,这里主要是屏蔽LSE和LSI

static rt_err_t stm32_rtc_init(void)
{
#if !defined(SOC_SERIES_STM32H7) && !defined(SOC_SERIES_STM32WL) && !defined(SOC_SERIES_STM32WB)
__HAL_RCC_PWR_CLK_ENABLE();
#endif
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
#ifdef BSP_RTC_USING_LSI
#ifdef SOC_SERIES_STM32WB
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
#else
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
#endif
#elif defined(BSP_RTC_USING_HSE_8M25DIV)
#else
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
#endif
if (rt_rtc_config() != RT_EOK)
{
LOG_E("rtc init failed.");
return -RT_ERROR;
}
return RT_EOK;
}
2. 修改 rt_rtc_config()
这里要着重注意分频系数
配置出1HZ的RTC时钟源就可以了
公式: HSE/DIV = AsynchPrediv * SynchPrediv
如:8M/25 = 125 * 2560

修改点:910行,5052行

static rt_err_t rt_rtc_config(void)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
HAL_PWR_EnableBkUpAccess();
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
#ifdef BSP_RTC_USING_LSI
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
#elif defined(BSP_RTC_USING_HSE_8M25DIV)
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV25;
#else
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
#endif
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
#if defined(SOC_SERIES_STM32WL)
__HAL_RCC_RTCAPB_CLK_ENABLE();
#endif
/* Enable RTC Clock /
__HAL_RCC_RTC_ENABLE();
RTC_Handler.Instance = RTC;
#if defined(SOC_SERIES_STM32F1)
RTC_Handler.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
RTC_Handler.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
#elif defined(SOC_SERIES_STM32F0)
/
set the frequency division /
#ifdef BSP_RTC_USING_LSI
RTC_Handler.Init.AsynchPrediv = 0XA0;
RTC_Handler.Init.SynchPrediv = 0xFA;
#else
RTC_Handler.Init.AsynchPrediv = 0X7F;
RTC_Handler.Init.SynchPrediv = 0x0130;
#endif /
BSP_RTC_USING_LSI /
RTC_Handler.Init.HourFormat = RTC_HOURFORMAT_24;
RTC_Handler.Init.OutPut = RTC_OUTPUT_DISABLE;
RTC_Handler.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
RTC_Handler.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
#elif defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32H7) || defined (SOC_SERIES_STM32WB)
/
set the frequency division /
#ifdef BSP_RTC_USING_LSI
RTC_Handler.Init.AsynchPrediv = 0x7D;
RTC_Handler.Init.SynchPrediv = 0xFF;
#elif defined(BSP_RTC_USING_HSE_8M25DIV)
RTC_Handler.Init.AsynchPrediv = 0x7D-1; //125
RTC_Handler.Init.SynchPrediv = 0xA00-1; //2560
#else
RTC_Handler.Init.AsynchPrediv = 0X7F;
RTC_Handler.Init.SynchPrediv = 0xFF;
#endif /
BSP_RTC_USING_LSI /
RTC_Handler.Init.HourFormat = RTC_HOURFORMAT_24;
RTC_Handler.Init.OutPut = RTC_OUTPUT_DISABLE;
RTC_Handler.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
RTC_Handler.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
#endif
if (HAL_RTCEx_BKUPRead(&RTC_Handler, RTC_BKP_DR1) != BKUP_REG_DATA)
{
LOG_I("RTC hasn't been configured, please use command to config.");
if (HAL_RTC_Init(&RTC_Handler) != HAL_OK)
{
return -RT_ERROR;
}
}
#ifdef SOC_SERIES_STM32F1
else
{
/
F1 series need update by bkp reg datas */
rt_rtc_f1_bkp_update();
}
#endif
return RT_EOK;
}

3 修改 get_rtc_timeval()

这里是ulog能否显示ms的关键

STM32的RTC是支持ms显示的,但不明白的是为什么原drv_rtc选择屏蔽

这里只需要将算出的 ms值传入 tv->tv_usec 即可

修改点:19~26行

static void get_rtc_timeval(struct timeval *tv)
{
RTC_TimeTypeDef RTC_TimeStruct = {0};
RTC_DateTypeDef RTC_DateStruct = {0};
struct tm tm_new = {0};
HAL_RTC_GetTime(&RTC_Handler, &RTC_TimeStruct, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&RTC_Handler, &RTC_DateStruct, RTC_FORMAT_BIN);
tm_new.tm_sec = RTC_TimeStruct.Seconds;
tm_new.tm_min = RTC_TimeStruct.Minutes;
tm_new.tm_hour = RTC_TimeStruct.Hours;
tm_new.tm_mday = RTC_DateStruct.Date;
tm_new.tm_mon = RTC_DateStruct.Month - 1;
tm_new.tm_year = RTC_DateStruct.Year + 100;
tv->tv_sec = timegm(&tm_new);
#if defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32F4)
#if defined(BSP_RTC_USING_HSE_8M25DIV)
tv->tv_usec = (RTC_Handler.Init.SynchPrediv - RTC_TimeStruct.SubSeconds * 1.0)
/ RTC_Handler.Init.SynchPrediv * 1000.0 * 1000.0;
#else
tv->tv_usec = (255.0 - RTC_TimeStruct.SubSeconds * 1.0) / 256.0 * 1000.0 * 1000.0;
#endif
#endif
}
无意义的补充
上面的误差,更多的是来自线程消耗的,同一个时间源,按理说应该不会有多少误差
加一个绝对延时,这个“误差” 就被抵消了。

void tcpserv_send(void *parameter)
{
uint32_t LastTime;
extern void proto_auto_poll(void);
while(1)
{
//绝对延时
LastTime = rt_tick_get() - LastTime;
rt_thread_mdelay(LastTime>2000 ? 0 : (2000-LastTime));
LastTime = rt_tick_get();
proto_auto_poll();
}
}

1.jpg

原作者:aingsu

更多回帖

发帖
×
20
完善资料,
赚取积分