【STM32】晶振,主時鍾,外設頻率介紹


首先,我用的是STM32F407,下方所有圖片都是出自這芯片的文檔,如果型號和我不同,需要找到對應的芯片說明文檔,也許會有出入

 

先看一張時鍾圖

這里會着重說明高速的部分,低速(不管內部還是外部)只給RTC時鍾使用

題外話,MCO1、MCO2,你可以往外面輸出時鍾

 

以下開始正題

圖片紅圈處是主時鍾,供給許多東西使用,例如外設(UART、SPI...),簡直就像大型音樂演奏的指揮者

所有你想用到的外設,初始化第一步,就是使能時鍾(向主時鍾請求)

例如下方的SPI初始化代碼

void SPI3_Init(void)
{	 
  GPIO_InitTypeDef  GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;
	
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);//使能GPIOC時鍾
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);//使能SPI3時鍾
 
  ...
  ...  	 
}  

是不是能看到clock(時鍾)的字樣?又看到ENABLE(使能)了吧?

 

再回到時鍾圖,紅圈處的主時鍾,總共有三種來源

【1】HSI內部高速晶振

【2】HSE外部高速晶振

【3】PLL鎖相環

其中PLL的來源,還是要由『內部高速晶振』或是『外部高速晶振』提供

主時鍾的SW,可以選擇來源是三種的哪一種

 

內部晶振固定是16M,外部的話,要取決你接的晶振是多少,當然,有個范圍,圖上寫了,4M~26M

選擇了『內部高速晶振』或是『外部高速晶振』,這沒什么好說的,主時鍾的頻率就和晶振一樣

下面說明,如果選擇了PLL,應該要設定哪些東西

首先很重要的一點,提供給PLL的時鍾一定要是1M,不可以是2M、4M...之類的

所以呢,就有了圖上的綠圈,除M(代碼為PLL_M)

如果是內部晶振(16M)提供,這個除M的值,就必須設16,16M / 16 = 1M

如果是外部晶振提供,假設接了8M的晶振,這個除M的值,就要設8,8M / 8 = 1M

這是規定

最終,PLL就一定會得到1M的時鍾,然后才開始做倍頻的工作

倍頻,會經過一個乘N(代碼為PLL_N)

之后,再經過一個除P(代碼為PLL_P),分頻后,就會把結果提供給主時鍾了

分頻Q(除Q)(代碼為PLL_Q),會給另一個東西提供時鍾,其他的忘了,但是SDIO就是其中一個

下面來看代碼

『代碼圖片』和『代碼塊』,都是出自sysytem_stm32f4xx.c

代碼塊的代碼有點多,但是只要認真看,其實會發現,ST把所有STM32F4系列的都加進來了

只要看和自己有關的就好了,我一開始也說了,我用的是STM32F407,我只關注宏定義『STM32F40_41xxx』即可

static void SetSysClock(void)
{
#if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx) || defined (STM32F401xx)
/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* Enable HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  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;
  }

  if (HSEStatus == (uint32_t)0x01)
  {
    /* Select regulator voltage output Scale 1 mode */
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;

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

#if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx)      
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
#endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx */

#if defined (STM32F401xx)
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
#endif /* STM32F401xx */
   
    /* Configure the main PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);

    /* Enable the main PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till the main PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
   
#if defined (STM32F427_437xx) || defined (STM32F429_439xx)
    /* Enable the Over-drive to extend the clock frequency to 180 Mhz */
    PWR->CR |= PWR_CR_ODEN;
    while((PWR->CSR & PWR_CSR_ODRDY) == 0)
    {
    }
    PWR->CR |= PWR_CR_ODSWEN;
    while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
    {
    }      
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif /* STM32F427_437x || STM32F429_439xx  */

#if defined (STM32F40_41xxx)     
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif /* STM32F40_41xxx  */

#if defined (STM32F401xx)
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;
#endif /* STM32F401xx */

    /* Select the main PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;

    /* Wait till the main PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
    {
    }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock
         configuration. User can add here some code to deal with this error */
  }
#elif defined (STM32F411xE)
#if defined (USE_HSE_BYPASS) 
/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* Enable HSE and HSE BYPASS */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON | RCC_CR_HSEBYP);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  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;
  }

  if (HSEStatus == (uint32_t)0x01)
  {
    /* Select regulator voltage output Scale 1 mode */
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;

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

    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;

    /* Configure the main PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
    
    /* Enable the main PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till the main PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }

    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;

    /* Select the main PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;

    /* Wait till the main PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
    {
    }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock
         configuration. User can add here some code to deal with this error */
  }
#else /* HSI will be used as PLL clock source */
  /* Select regulator voltage output Scale 1 mode */
  RCC->APB1ENR |= RCC_APB1ENR_PWREN;
  PWR->CR |= PWR_CR_VOS;
  
  /* HCLK = SYSCLK / 1*/
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* PCLK2 = HCLK / 2*/
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* PCLK1 = HCLK / 4*/
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
  
  /* Configure the main PLL */
  RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | (PLL_Q << 24); 
  
  /* Enable the main PLL */
  RCC->CR |= RCC_CR_PLLON;
  
  /* Wait till the main PLL is ready */
  while((RCC->CR & RCC_CR_PLLRDY) == 0)
  {
  }
  
  /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
  FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;
  
  /* Select the main PLL as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_PLL;
  
  /* Wait till the main PLL is used as system clock source */
  while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
  {
  }
#endif /* USE_HSE_BYPASS */  
#endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx */  
}

 

在函數setSysClock(設置系統時鍾)里面,一開始就會發現一行代碼

/* Enable HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);

這是選擇使用HSE(外部晶振),但,這還不是主時鍾的三個選擇之一,只是選擇制造時鍾的來源,下面的代碼才是真正決定主時鍾是使用HSE、HSI、PLL哪一個

/* Select the main PLL as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_PLL;

上面寫着RCC_CFGR_SW_PLL,配合文章一開始的圖片,紅圈處,不是有個SW嗎?這就是選擇主時鍾來源

再結合這三行代碼『RCC->CR』『RCC->CFGR』這些代碼(注釋 『/* Enable HSE */ 』這些不算代碼啊)

意思是:我使用外部晶振,然后外部晶振提供給PLL去倍頻,最后主時鍾采用PLL提供的頻率

 

在函數setSysClock里面,還可以找到關於PLL的設置,也就是

PLL_M、PLL_N、PLL_P

這些功用之前也說了,對照一開始的圖片,找到PLL里面的『除M』『乘N』『除P』

也就理解了

在STM32F407里面,沒有關於/R的東西,看看其他F4系列的,應該能找到

 

接下來,我會把重點放在外設上,也就是紅圈處主時鍾的右邊,APBx分頻這部分(有些外設需要的時鍾不同,但主時鍾只能提供一種頻率,這時要靠各自的分頻,實現不同的頻率)

但是在APBx之前,還有一個AHB分頻

整個思路是這樣的,假設我要用SPI:『主時鍾提供時鍾』->『經過AHB分頻』->『經過APBx分頻,APBx總線擁有自己的一個時鍾頻率』->『SPI自己還可以再一次分頻』

相關代碼如下(此代碼還是上面那個很長的代碼,里面的一小部分,注釋是我自己加上的)

/* HCLK = SYSCLK / 1*/
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB分頻

#if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx)      
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2分頻
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1分頻
		
#endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx */

這里就看出了兩個分頻了,一個是AHB,另一個是APBx(APB1和APB2暫且算同一個)

上面以SPI為例,那么『SPI自己還可以再一次分頻』,這代碼在哪呢?

這就要自己寫SPI初始化函數了,設定相關的參數,其中一條,就是分頻,如下圖綠色底那行所示

 

 

在STM32F4系列,有兩個APB,分別是APB1、APB2

APB是總線,至於哪些外設,分別是掛在哪個總線上呢?

我截取一張頭文件stm32f4xx_rcc.h的圖片來看,就知道了

 

這就是外設的分布,例如串口2(USART2),在410行,也就是掛在APB1上

串口1(USART1),在433行,它就是掛在APB2上了

文章上面一點,我不是貼了一個SPI 1的初始化代碼的圖片嗎(圖片顯示行號4~39)?

SPI 1是掛在APB2上的,時鍾使能寫的函數名稱和參數,都寫着APB2的

另外,APB2的上限速度,是比APB1塊的

一開始的時鍾圖,也寫了APB2的上限是84M,比APB1整整多了一倍

 

總算結束了,但是請注意,本篇是以STM32F407為例,不同型號的芯片,最好還是參考各自的說明文檔,以免出錯

謝謝你的觀看,如果有錯,也希望能提出來,一起交流進步

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM