HSE时钟

HSE时钟

目录

简介

做stm32程序移植的时候,发现串口发送一直失败,最终找到了源头–HSE时钟不同,所以来探究一下这个时钟到底是怎么回事。

探索历程

stm32硬件

HSE全称:高速外部时钟信号HSE(High Speed External Clock signal)
stm32F407可以由三种不同的时钟源来驱动系统时钟:HSI(High Speed Internal Clock signal) 振荡器时钟、HSE 振荡器时钟、主 PLL (Phase Locked Loop) 时钟。对于每个时钟源来说,在未使用时都可单独打开或者关闭,以降低功耗。
我的板子是野火的STM32F407ZGT6开发板,系统是用HSE时钟驱动的,所以主要看HSE。
首先看时钟树,这个可以再STM32F4xx中文参考手册中找到:
这里写图片描述
这里用的HSE晶振是25Mhz。HSE时钟信号首先进行一个分频/M(M=25),然后进行一个倍频xN(336),得到了VCO时钟,再经过一个分频/P(P=2),得到了PLLCLK(锁相环时钟),当我们SYSCLK设定时钟源是PLLCLK时,这个时钟就等于SYSCLK(系统时钟)。这个可以在RCC_CFGR 时钟配置寄存器的SW位配置。在手册中找到:
这里写图片描述
S Y S C L K = P L L C L K = H S E / M N / P = 168 M H z
然后SYSCLK在/PRESC分频因子进行分频后,给AHB(advanced high-performance bus)、APB总线使用。其中AHB1最高可以168MHz,APB2可以为84MHz,APB1为42MHz。

HSE硬件

那这个HSE时钟是怎么来的呢?我再去看看晶振的一些东西。HSE的来源可以是有源晶振(1-50M)、无源晶振(4-26M)。如果使用有源晶振来给系统提供时钟,之前时钟树里面的OSC_OUT不用连接,需要配置RCC_CR时钟控制寄存器 (RCC_CR)的位16:HSEON控制。在手册中找到:
这里写图片描述
PLL时钟还可以HSI来输入,具体怎么控制由RCC PLL 配置寄存器 (RCC_PLLCFGR)的PLLSRC位配置。在手册中找到:
这里写图片描述
通常我们使用HSE时钟,因为HSI时钟的精度不如HSE,通常作为HSE的备用时钟。

软件

那时钟的配置通过软件是怎么实现的呢?
在用固件库开发stm32的时候,我们一般不用配置时钟,甚至连它们在哪都不知道。这是因为在启动文件里面就已经把时钟配置好了。启动文件是startup_stm32f40xx.s,其中有这么一段汇编程序

    ; Reset handler
    Reset_Handler    PROC
    EXPORT  Reset_Handler             [WEAK]
    IMPORT  SystemInit
    IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

意思是说复位后先启动SystemInit时钟配置,再进入main函数。然后我们右键进入这个函数,发现这个函数再system_stm32f4xx.c文件中,函数如下:

/**
  * @brief  Setup the microcontroller system
  *         Initialize the Embedded Flash Interface, the PLL and update the 
  *         SystemFrequency variable.
  * @param  None
  * @retval None
  */
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
}

其中最关键的是SetSysClock()函数,部分原型(只是时钟部分)如下:

static void SetSysClock(void)
{
/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
  /* 使能 HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
    /*等待HSE时钟稳定*/
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    StartUpCounter++;
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }
  /* HSE启动成功 */
  if (HSEStatus == (uint32_t)0x01)
  {

    /* HCLK = SYSCLK / 1*/
    /* 这是AHB1 */
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;

    /* PCLK2 = HCLK / 2*/
    /* 这是APB2 */
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;

    /* PCLK1 = HCLK / 4*/
    /* 这是APB1 */
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;

    /* 配置主PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);

    /* 使能主PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* 等待主PLL准备完毕 */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }

    /* 选择主PLL作为时钟源 */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;

    /* 等待PLL配置完毕 */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);

}

探索结果

我用的新板子HSE时钟是16MHz的,而野火的STM32F407ZGT6开发板HSE是25MHz的,我只要修改SetSysClock()函数里面的PLL_M值,从25改成16即可。另外,如果要让HSE作为系统时钟使用,只要把HSE_VALUE从25M改成16M即可。

猜你喜欢

转载自blog.csdn.net/qq2331374921/article/details/82187815
今日推荐