文章目录

stm32f4xx_hal_rcc.c中 HAL_RCC_GetSysClockFreq()用于获取当前系统时钟,来计算各总线时钟或外设时钟。当HSE_VALUE/pllm结果不是整数时,返回结果有误。
例如:HSE_VALUE = 24000000, pllm=196,plln=14,pllp=2, 原算法结果sysclockfreq=167,999,930,正确结果应该是168,000,000
原因在下面两处计算pllvco时,如果HSE_VALUE/pllm或HSI_VALUE/pllm不能整除时就产生了错误。

stm32f4xx_hal_rcc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__weak uint32_t HAL_RCC_GetSysClockFreq(void)
{
//......
if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_HSI)
{
/* HSE used as PLL clock source */
pllvco = ((HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> POSITION_VAL(RCC_PLLCFGR_PLLN)));
}
else
{
/* HSI used as PLL clock source */
pllvco = ((HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> POSITION_VAL(RCC_PLLCFGR_PLLN)));
}
//......
}

修正:

stm32f4xx_hal_rcc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
__weak uint32_t HAL_RCC_GetSysClockFreq(void)
{
uint32_t plln;
//......
plln = (RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> POSITION_VAL(RCC_PLLCFGR_PLLN);
if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_HSI) /* HSE used as PLL clock source */
{
pllvco = ((uint64_t)HSE_VALUE * plln / pllm);
}else{ /* HSI used as PLL clock source */
pllvco = ((uint64_t)HSI_VALUE * plln / pllm);
}
//......
}

之前在21ic bbs讨论时有网友有疑问:”为什么不在选择M的时候选一个可以整除的呢?” 因为最佳PLL参数组合并不能保证HSE_VALUE/pllm可整除。参见STM32F4时钟PLL参数计算

目前有发现有Bug的版本:STM32Cube_FW_F4 V1.5.0, V1.6.0, V1.7.0,其它库请自行审查。
Bug影响范围:HAL_RCC_GetSysClockFreq()被HAL_RCC_GetHCLKFreq()调用并更新全局变量SystemCoreClock。而HAL_RCC_GetHCLKFreq()又被HAL_RCC_GetPCLK1Freq()、HAL_RCC_GetPCLK2Freq()调用。
HAL函数库里多个模块会调用以下其一来得到系统时钟或总线时钟:HAL_RCC_GetHCLKFreq(), SystemCoreClock, HAL_RCC_GetPCLK1Freq(),HAL_RCC_GetPCLK2Freq()。

注意到HAL_RCC_GetSysClockFreq()函数定义前面有个weak,表明用户可以自行定义一个同名不带weak的函数,链接时会被替换。因此大家可以根据自己的系统写个简化版的放在项目目录里,毕竟实际应用系统的时钟不会变来变去的。不考虑低功耗的系统时钟一般是固定的,直接返回个固定值也未尝不可。考虑低功耗的,一般两种时钟方案也就够了。

文章目录