之前刚在stm32f407ZGT核心板移植完RT-Thread后进行简单的测试的时候,发现rt_thread_delay()阻塞延时函数不能按照正常的时钟进行一次,具体是情况是1s的延时粗略算起来大概延时了3s左右,在网上找了很多资料都没有完全解决的,结合网友的各种建议再结合认真看了一遍f407的时钟树配置以后总算是解决了,记录问题解决过程。
F407系统时钟树
截图比较模糊,建议直接看手册的时钟树。
为了解决rt-thread的系统时钟配置,这里我们只需要注意SYSCLK的配置过程。SYSCLK来源有三个:
- HSE:高速外部时钟(一般是4-24M左右的频率),高速外部时钟信号 (HSE) 有 2 个时钟源:(1) HSE 外部晶振/陶瓷谐振器(核心板或开发板上的一般为8M)、(2)HSE 外部用户时钟
- HSI:高速内部时钟(芯片内部的16M),HSI 时钟信号由内部 16 MHz RC 振荡器生成,可直接用作系统时钟,或者用作 PLL 输入。
- PLL:倍频输出时钟。
我们使用f407这样的芯片,肯定还是想要它能跑的更快的,所以单靠HSE,HSI本身的时钟频率无法满足要求,所以绝大多数情况下SYSCLK都是来自PLL倍频输出时钟。
SYSCLK的配置
- PLL简介
- 主 PLL(PLL)由 HSE 或者 HSI 提供时钟信号(通过选择器),并具有两个不同的输出时钟。主PLL时钟计算方式:PLL=8MHz * N/ (MP)=8MHz 336 /(8*2) = 168MHz (就是分频和倍频:外部晶振选择 8MHz PLL_M=8,倍频器倍频系数 PLL_N=336,分频器分频系数 PLL_P=2 )第一个输出 PLLP 用于生成高速的系统时钟(最高 168MHz)第二个输出 PLLQ 用于生成 USB OTG FS 的时钟(48MHz),随机数发生器的时钟和 SDIO时钟
- 专用 PLL(PLLI2S)用于生成精确时钟,从而在 I2S 接口实现高品质音频性能
由上述的主PLL时钟(也就是SYSCLK)计算方式可知,配置好SYSCLK由HSE为源后,需要将几个分频和倍频因子配置好,这样SYSCLK才算是配置完成。
再由源代码来看这个过程:
- 首先上电后板子进入汇编的这段复位程序后加载系统时钟配置 SystemInit
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
- 在 system_stm32f4xx.c 中找到系统时钟配置函数原型,这里主要是进行了RCC相关寄存器的复位,然后进入 SetSysClock(); 函数。
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset CFGR register */
RCC->CFGR = 0x00000000;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Disable all interrupts */
RCC->CIR = 0x00000000;
#if defined(DATA_IN_ExtSRAM) || defined(DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/* Configure the System clock source, PLL Multiplier and Divider factors,
AHB/APBx prescalers and Flash settings ----------------------------------*/
SetSysClock();
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
- 同样在 system_stm32f4xx.c 中找到 SetSysClock(); 函数原型。由于诸多的宏条件编译,代码很长,但是这里最主要的任务就是选择HSE为源(注意:如果HSE时钟源没有就绪或者没有HSE,那么就会使用HSI为系统时钟),配置好系统时钟和AHB,APB2,APB1的总线时钟。
static void SetSysClock(void)
{
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F412xG) || defined(STM32F413_423xx) || defined(STM32F446xx)|| defined(STM32F469_479xx)
/******************************************************************************/
/* PLL (clocked by HSE) used as System clock source */
/******************************************************************************/
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* Enable HSE */
RCC->CR |