關於STM32F107RCT6使用8M晶振串口波特率錯誤的問題

發現問題

這段時間在學習FreeRTOS,今天移植成功後,寫了2個任務。一個是LED每隔1s閃爍任務,另一個是使用UART4每隔100ms發送字符串任務。下載成功後,發現LED閃爍間隔不對,串口打印間隔時間也不對。當時就很納悶了:RCC是按標準庫的例子來配置的,FreeRTOS的系統節拍也是配置正確的,不可能會出現這種問題。後來,仔細排查,用示波器測試串口的波形發現一個問題:程序代碼配置的是115200波特率,但實際輸出波形算出來是38400,相差3倍。到這就開始懷疑RCC配置了,這時,我把MCO引腳打開,讓其出示SYSCLK時鐘,示波器上面反應的是72MH。接着又讓它輸出APB1CLK時鐘,是正常的36MHz。調試到這裏就感覺莫名其妙了,後來仔細查看RCC配置代碼,發現官方例子是使用的25MHz的晶振,於是我換上25Mhz,重算了下分頻係數等。燒寫後,就變正常了。這裏說下官網的例子模板

問題解決

STM32F107的時鐘樹

這裏寫圖片描述
從圖中可以看出,STM32F107的SYSCLK時鐘有2條配置路線。第一條可以使用8M外部晶振來配置系統72MHz。第二條可以使用25M外部晶振來配置系統72MHz時鐘。起出使用的是第一條路線,系統時鐘也是正確配置成72MHz了,但是串口時鐘是不正確的(其實是HSE_VALUE值沒修改)。換成25MHz晶振,按第二條線路配置後,串口就正常了

官方RCC配置例子代碼

void RCC_Configuration(void)
{
 RCC_ClocksTypeDef RCC_ClockFreq;

   /* RCC system reset(for debug purpose) */
  RCC_DeInit();

  /* Enable HSE */
  RCC_HSEConfig(RCC_HSE_ON);

  /* Wait till HSE is ready */
  HSEStartUpStatus = RCC_WaitForHSEStartUp();

  if(HSEStartUpStatus != ERROR)
  {
    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

 /****************************************************************/
 /*      HSE=25MHz, HCLK=72MHz, PCLK2=72MHz, PCLK1=36MHz         */
 /****************************************************************/
    /* Flash 2 wait state */
    FLASH_SetLatency(FLASH_Latency_2);
    /* HCLK = SYSCLK */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);
    /* PCLK2 = HCLK */
    RCC_PCLK2Config(RCC_HCLK_Div1);
    /* PCLK1 = HCLK/2 */
    RCC_PCLK1Config(RCC_HCLK_Div2);
    /*  ADCCLK = PCLK2/4 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    /* Configure PLLs *********************************************************/
    /* PPL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
    RCC_PREDIV2Config(RCC_PREDIV2_Div5);
    RCC_PLL2Config(RCC_PLL2Mul_8);

    /* Enable PLL2 */
    RCC_PLL2Cmd(ENABLE);

    /* Wait till PLL2 is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET)
    {}

    /* PPL1 configuration: PLLCLK = (PLL2 / 5) * 9 = 72 MHz */
    RCC_PREDIV1Config(RCC_PREDIV1_Source_PLL2, RCC_PREDIV1_Div5);
    RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_9);

    /* Enable PLL */
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {}

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while (RCC_GetSYSCLKSource() != 0x08)
    {}
  }

  RCC_GetClocksFreq(&RCC_ClockFreq);

  /* Enable USART2 clock */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);    


  /* Enable ETHERNET clock  */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC | RCC_AHBPeriph_ETH_MAC_Tx |
                        RCC_AHBPeriph_ETH_MAC_Rx, ENABLE);

  /* Enable GPIOs clocks */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC |
                         RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE| RCC_APB2Periph_AFIO, ENABLE);
  /* Enable ADC1 clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
}

可以發現官方的例子代碼是使用25MHz外部晶振來配置的系統時鐘,我在官方的代碼基礎上修改我的8M晶振配置就不能正常運行

我修改的代碼

void bsp_RCC_Init(void)
{
 RCC_ClocksTypeDef RCC_ClockFreq;

   /* RCC system reset(for debug purpose) */
  RCC_DeInit();


  /* Enable HSE */
  RCC_HSEConfig(RCC_HSE_ON);

  /* Wait till HSE is ready */
  if(RCC_WaitForHSEStartUp() != ERROR)
  {
    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

 /****************************************************************/
 /*      HSE=8MHz, HCLK=72MHz, PCLK2=72MHz, PCLK1=36MHz         */
 /****************************************************************/
    /* Flash 2 wait state */
    FLASH_SetLatency(FLASH_Latency_2);
    /* HCLK = SYSCLK */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);
    /* PCLK2 = HCLK */
    RCC_PCLK2Config(RCC_HCLK_Div1);
    /* PCLK1 = HCLK/2 */
    RCC_PCLK1Config(RCC_HCLK_Div2);
    /*  ADCCLK = PCLK2/4 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    /* Configure PLLs *********************************************************/
    /* PPL2 configuration: PLL2CLK = (HSE / 2) * 10 = 40 MHz */
    RCC_PREDIV2Config(RCC_PREDIV2_Div2);
    RCC_PLL2Config(RCC_PLL2Mul_10);

    /* Enable PLL2 */
    RCC_PLL2Cmd(ENABLE);

    /* Wait till PLL2 is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET)
    {}

    /* PPL1 configuration: PLLCLK = (HSE / 1) * 9 = 72 MHz */
    RCC_PREDIV1Config(RCC_PREDIV1_Source_HSE, RCC_PREDIV1_Div1);
    RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_9);

    /* Enable PLL */
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {}

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while (RCC_GetSYSCLKSource() != 0x08)
    {}
  }
 }

上面我修改的代碼使用後就出現了串口波特率錯誤的問題。在keil調試查看寄存器發現UART->BRR的數值與理論計算的不一樣。追溯USART_Init()函數配置過程,發現獲取的PCLK1時鐘頻率不是36MHz。這裏寫圖片描述
仔細查詢源頭代碼,發現HSE_VALUE定義的是25000000,這就導致圖中RCC_GetClockFreq()函數獲取的PCLK1_Frequency的值計算出錯。所以需將HSE_VALUE改成8000000。
這裏寫圖片描述
另一種辦法,更換外部8M晶振爲25M,修改RCC配置爲官方代碼,就可以正常運行

總結

STM32F107具有以太網、USB OTG 等網絡設備,以太網使用MII接口時需要提供25MHz時鐘,STM32F107使用外部25MHz晶振來作爲時鐘源是最好不過了,官方也推薦使用25MHz。使用8M晶振需要修改HSE_VALUE值爲8000000,然後配置相應的PLL時鐘了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章