【單片機筆記】STM32F4那些事之-時鐘踩過的坑

之前有用過STM32的F4系列單片機,該單片機的強大之處可以度娘一下,因爲打算系統的學習下,所以自己做了一個板子用作學習,把資料都準備好,主要是官方的標準庫及例程,學32第一件事情就是要搞清楚時鐘,相對傳統的51,32的時鐘相對複雜很多,M3內核如此,M4內核更甚。下面貼出M4的時鐘樹:

可以看到時鐘樹很複雜,我用的芯片是STM32F401系列的,官方給的數據是最高時鐘能達到84M,我也有朋友直接超頻到120M用的,不過在產品設計上還是要以穩定爲主。上圖的註釋是我後來加的,這裏主要記錄一下我如何配置系統時鐘及踩到坑。在最開始的時候使用官方的例程,發現時鐘不對,查底層的時候發現官方測試的目標板使用的是25M的晶體,而我們常用的是8M,我這裏也使用的8M晶體,所以我進行了如下修改:

首先常用的系統源是PLL,而在M4內核上PLL時鐘是經過內部16M或者外部晶振經過M除頻、N倍頻和P除頻來的。這裏我自己寫了一個時鐘函數,可以選擇外部時鐘和內部時鐘。代碼如下:

#define HSE RCC_PLLSource_HSE
#define HSI RCC_PLLSource_HSI

//RCC_PLLSource:PLL時鐘源 RCC_PLLSource_HSE、RCC_PLLSource_HSI
//PLLM:主 PLL和音頻PLL輸入時鐘的分頻係數 範圍2-63
//PLLN:倍頻係數 範圍2-510
//	小心: 軟件必須正確設置這些位,確保 VCO 輸出頻率介於 192 和 432 MHz 之間。
//	VCO 輸出頻率 = VCO 輸入頻率 × PLLN 並且 192 <= PLLN <= 432
//PLLP:主系統時鐘的主 PLL (PLL) 分頻係數 範圍2、4、6、8
//PLLQ:主 PLL (PLL) 分頻係數,適用於 USB OTG FS 範圍2-15
//使用時確保晶振頻率PLLM分頻爲1MHz即可
void SysClock_Configuration(uint32_t RCC_PLLSource, uint32_t PLLM)
{
	__IO uint32_t HSEStatus = 0;
	
    RCC_DeInit();  

	if(RCC_PLLSource_HSE == RCC_PLLSource){	//選擇外部時鐘
		RCC_HSEConfig(RCC_HSE_ON);                                   	//打開外部時鐘
		if(RCC_WaitForHSEStartUp() == SUCCESS){
			HSEStatus = 1;
		}
		else{
			RCC_HSEConfig(RCC_HSE_OFF);	//關閉外部時鐘
			RCC_HSICmd(ENABLE);	
		}			
	}
	
	RCC_HCLKConfig(RCC_SYSCLK_Div1);                          	//HCLK(AHB)時鐘爲系統時鐘1分頻			
	RCC_PCLK1Config(RCC_HCLK_Div2);                            	//PCLK(APB1)時鐘爲HCLK時鐘2分頻
	RCC_PCLK2Config(RCC_HCLK_Div1);                            	//PCLK(APB2)時鐘爲HCLK時鐘1分頻	

	if(HSEStatus == 1) {                 							
		//PLL時鐘配置,外部晶振爲8MHz,系統配置爲8/PLLM*PLLN/PLLP
		RCC_PLLConfig(RCC_PLLSource_HSE, PLLM, 336, 4, 7);   
	}
	else{
		//PLL時鐘配置,內部晶振爲16MHz,系統配置爲16/16*336/4 =84MHz usb=336/7=48
		RCC_PLLConfig(RCC_PLLSource_HSI, 16, 336, 4, 7);   
	}
	
	RCC_PLLCmd(ENABLE);                                         //開啓PLL時鐘,並等待PLL時鐘準備好
	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);      			//選擇PLL時鐘爲系統時鐘
   
	while(RCC_GetSYSCLKSource() != 0x08);                      	//Wait till PLL is used as system clock source
	RCC_ClockSecuritySystemCmd(ENABLE);							//打開時鐘安全系統
}

這個函數在主函數的入口處執行,註釋的也很詳細。傳入參數有兩個,第一個是選擇內部時鐘或者外部時鐘,代碼中有定義,第二個是M除頻,在使用外部時鐘的時候需要根據目標版的晶體頻率選擇,以保證經過晶體頻率經過M除頻後是1MHz的頻率。如果是選用的內部時鐘,則第二個參數無效,可以看到代碼裏面直接是16的除頻而沒有用到輸入的參數作爲M除頻。這是因爲內部時鐘是固定的16MHz,所以就固定死了。親測這個函數是有效的。但是我在使用的時候開始就出現了問題。在一些外設的配置中時鐘就不對,這裏拿串口作爲例子。通過串口的配置底層可以看到一個函數:

可以看到串口的波特率是系統根據時鐘去自動計算的,所以在計算之前需要得到時鐘頻率,此時會調用上圖的獲取時鐘函數。這個地方也是我採坑的地方。花了點時間終於找到問題,跳進去時鐘獲取函數去看發現了一個宏定義:

就是這個,一個是內部時鐘一個是外部時鐘,繼續跟進會看到:

它上面標的是25Mhz,這就是外設時鐘不對的地方。其實時鐘那條線都是完全ok的,只不過在配置這個串口的時候調用了這個錯誤的宏定義,然後就導致外設輸出結果不對。找到問題就好辦了,把25M改成自己的8M,問題解決,世界安靜了。

Urien

2019年11月21日 12:46:45

 

 

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