最近在跟着ZF学习无人机,到了软件编程部分,首先是产生PWM的代码,芯片使用的是STM32F373CCT6,Keil创建Project就不说了,关于固件库的移植做了一个图表便于记录。
固件库默认的时钟是8MHz,实际使用的16MHz,需要修改固件库。
问题1:到哪里找这个8MHz呢?猜测在和时钟相关的.h文件种。
在stm32f37x.h文件中有宏定义:
/**
* @brief In the following line adjust the value of External High Speed oscillator (HSE) used in your application
*/
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
右键索引到了文件system_stm32f37x.h中的SystemCoreClockUpdate()中,可以看出HSE_VALUE的值由RCC的CFGR 寄存器的SWS决定。
/* Get SYSCLK source -------------------------------------------------------*/
tmp = RCC->CFGR & RCC_CFGR_SWS;
switch (tmp)
{
case 0x04: /* HSE used as system clock */
SystemCoreClock = HSE_VALUE;
break;
}
到《STM32F373XXX_Reference_Manual》中找下RCC_CFGR_SWS,当值SWS[1:0]为01时,即0x04,系统时钟选用HSE,吻合。
问题2:SystemCoreClockUpdate()这个函数什么时候被调用?被谁调用?
首先函数自身有解释,Each time the core clock (HCLK) changes, this function must be called to update SystemCoreClock variable value. Otherwise, any configuration based on this variable will be incorrect. 就是说只有HCLK被改变时,该函数才会执行。“默认不用调用这个函数,因为SystemCoreClock默认被设置为设定的频率了(72MHz)”。感觉不对。
回到Reference_Manual中找下HSE的介绍,有这一句话:The HSE crystal can be switched on and off using the HSEON bit in the Clock control register (RCC_CR)P103.找到RCC_CR,Bit16决定HSE的ON/OFF。
问题3:在哪里设置RCC_CR呢?
在出现RCC_CFGR_SWS的stm32f37x.h中搜一下RCC_CR,找到RCC_CR_HSEON=0x00010000=0001,0000,0000,0000,0000,即Bit16=1。
/******************************************************************************/
/* Reset and Clock Control */
/******************************************************************************/
/******************** Bit definition for RCC_CR register ********************/
#define RCC_CR_HSION ((uint32_t)0x00000001)
……
#define RCC_CR_HSEON ((uint32_t)0x00010000)
……
#define RCC_CR_PLLON ((uint32_t)0x01000000)
#define RCC_CR_PLLRDY ((uint32_t)0x02000000)
右键索引下谁在调用RCC_CR_HSEON, 跳到了SetSysClock()函数中,看解释是用来配置系统时钟频率,到此HSE使能。
/**
* @brief Configures the System clock frequency, AHB/APBx prescalers and Flash
* settings.
* @note This function should be called only once the RCC clock configuration
* is reset to the default reset state (done in SystemInit() function).
* @param None
* @retval None
*/
static void SetSysClock(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/******************************************************************************/
/* PLL (clocked by HSE) used as System clock source */
/******************************************************************************/
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------*/
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
……
}
问题4:谁调用了SetSysClock()呢?google一下……
注意看下static void SetSysClock(void),它是一个静态函数,只在system_stm32f37x.c中被调用,一搜是函数SystemInit()调用的,用来配置系统时钟。
/**
* @brief Setup the microcontrollers system.
* Initialize the Embedded Flash Interface, the PLL and update the
* SystemCoreClock variable.
*/
void SystemInit (void)
{
……
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Configure the System clock frequency, AHB/APBx prescalers and Flash settings */
SetSysClock();
……
}
问题5:谁调用了SystemInit()呢?google一下……
“STM32系统复位后首先进入SystemInit函数进行时钟的设置,然后进入主函数main()”,第一步操作,找到源头了。到此修改HSE_Value就分析完了。
打算结束了,突然感觉HSE_VALUE好像并没有写到函数中啊,只是宏定义了而已。
*********************************************************************************************************************************************************
从初始函数SystemInit()来逐渐分析下函数执行的顺序,在SystemInit()中先启动HSI,再复位几个寄存器,最后调用SystemInit()来配置the System clock frequency, AHB/APBx prescalers and Flash settings,可以借助STM32CubeMX看下时钟树:HSI RC->HSI->SYSCLK->AHB Prescaler->HCLK
再到 SetSysClock()函数,已知该函数是用来配置系统时钟的,看下是如何配置时钟的:
/******************************************************************************/
/* PLL (clocked by HSE) used as System clock source */
/******************************************************************************/
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------*/
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
到此相当于打开了图4中PLL Soure Mux的HSE开关,即如图5所示,HSE作为时钟输入。等待HSERDY……
HSERDY之后,继续配置,效果如图6所示,继续……
/* HCLK = SYSCLK / 1 */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; /*!< SYSCLK not divided */
/* PCLK2 = HCLK / 1 */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; /*!< HCLK not divided */
/* PCLK1 = HCLK / 2 */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; /*!< HCLK divided by 2 */
配置RCC_CFGR的PLLSRC、PLLXTPRE和PLLMULL位,选择HSE作为PLL输入时钟,并且不分频,但PLLMULL9倍频。效果如图7所示。
/* PLL configuration */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL9);
后面代码使能PLL,选择PLL作为系统时钟。由默认的外部时钟8MHz,PLL倍频,得到系统72MHz时钟的流程,如图8。
到此清楚了,如果修改默认的8MHz外部时钟为16MHz,那么得到需要的72MHz的SYSCLK,需要将PLLMULL9/2,不是整除,可以先将16MHz/2得到8MHz,再给PLL。只需要在程序中将RCC_CFGR_PLLXTPRE_PREDIV1修改为RCC_CFGR_PLLXTPRE_PREDIV1_Div2即可。如果将默认的8MHz外部时钟修改为16MHz,只需要2部就可以:
1、修改#define HSE_VALUE ((uint32_t)16000000);
2、修改RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1_Div2 | RCC_CFGR_PLLMULL9)。