1. 程式人生 > 實用技巧 >stm32時鐘設定

stm32時鐘設定

在做專案時,碰到一個問題被困擾很久,採集交流電均值時會出現結果為零的情況,但是我設計的是採集一個週期數據的均值,因此是不可能為零的。
隨著不斷深入尋找錯誤,發現是因為我採用的模板晶振為8MHZ,而我使用的板子外部晶振為25MHZ。 問題解決了,但是也證明自己對時鐘這邊瞭解不夠深入。
遂趁週末好好學習了一下,做個記錄。

// 暫存器巨集定義
// RCC暫存器基地址為0x40021000
#define RCC_BASE	0x40021000			// RCC部分暫存器的基地址
#define RCC_CR		(RCC_BASE + 0x00)	// RCC_CR的地址
#define RCC_CFGR	(RCC_BASE + 0x04)

#define FLASH_ACR	0x40022000

// 用C語言來訪問暫存器的巨集定義
#define rRCC_CR		(*((volatile unsigned int *)RCC_CR))
#define rRCC_CFGR	(*((volatile unsigned int *)RCC_CFGR))
#define rFLASH_ACR	(*((volatile unsigned int *)FLASH_ACR))

void Set_SysClockTo72M(void)
{
	unsigned int rccCrHserdy = 0;
	unsigned int rccCrPllrdy = 0;
	unsigned int rccCfrSwsPll = 0;
	unsigned int faultTime = 0;
    
	rRCC_CR = 0x00000083;
	rRCC_CR &= ~(1<<16);	  	// bit16為0,關閉HSEON
	rRCC_CR |= (1<<16);			// 開啟HSEON,讓HSE工作,這裡的開啟關閉主要是指內部振盪電路

	do                      //開始振盪到穩定需要一段時間,所以需要判斷
	{
		rccCrHserdy = rRCC_CR & (1<<17);	//檢測第17位是否為1,第17位為HSERDY
		faultTime++;//檢測時間
	}
	while ((faultTime<0x0FFFFFFF) && (rccCrHserdy==0));  //不超時或者HSERDY為0,也就是沒準備好,繼續迴圈

	if ((rRCC_CR & (1<<17)) != 0)   //不為零說明是因為HSERDY為1,也就是HSE準備好了
	{
		rFLASH_ACR |= 0x10;         //與FLASH相關,暫不深究
		rFLASH_ACR &= (~0x03);
		rFLASH_ACR |= (0x02);

		// 到這裡HSE就ready了,下面再去配PLL並且等待他ready
		//HPRE為bit4-7,是AHB預分頻器;PPRE1為8-10,APB1;PPRE2為11-13,APB2;
		rRCC_CFGR &= (~((0x0f<<4) | (0x07<<8) | (0x07<<11))); //全部置0
		//rRCC_CFGR &= (~(0x3ff<<4));
		// AHB和APB2未分頻,APB1被2分頻,所以最終:AHB和APB2都是72M,APB1是36M 
		rRCC_CFGR |= ((0x0<<4) | (0x04<<8) | (0x0<<11));

		// PLLSRC為bit16,輸入時鐘源;PLLXTPRE為bit17,HSE分頻後作為PLL輸入;
		//選擇HSE作為PLL輸入並且HSE不分頻,所以PLL輸入為8M
		rRCC_CFGR &= (~((1<<16) | (1<<17)));   	// 清零bit17和bit16
		rRCC_CFGR |= ((1<<16) | (0<<17));		// 置1 bit16

		// 設定PLL倍頻係數為9
		rRCC_CFGR &= (~(0x0f<<18));   			// PLLMUL倍頻引數bit18-21 清零
		rRCC_CFGR |= (0x07<<18);				// 9倍頻

		// 開啟PLL開關
		rRCC_CR |= (1<<24);

		// do while 迴圈等待PLL時鐘穩定
		faultTime = 0;
		do
		{
			rccCrPllrdy = rRCC_CR & (1<<25);	//檢測第25位是否為1
			faultTime++;//檢測時間
		}
		while ((faultTime<0x0FFFFFFF) && (rccCrPllrdy==0));
		//while (rccCrPllrdy==0);

		if ((rRCC_CR & (1<<25)) == (1<<25))
		{
		  	// 到這裡說明PLL已經穩定了,可以用了,下面就可以切了
			
			// 切換PLL輸出為SYSCLK
			//SW系統時鐘切換bit1:0,10表示PLL作為系統時鐘
			rRCC_CFGR &= (~(0x03<<0));   //低兩位置零
			rRCC_CFGR |= (0x02<<0);	     //bit2置為1

			faultTime = 0;
			do
				{                                        //SWS系統時鐘轉換狀態位bit3:2
				rccCfrSwsPll = rRCC_CFGR & (0x03<<2);	//檢測第2、3位
				faultTime++;//檢測時間
			}
			while ((faultTime<0x0FFFFFFF) && (rccCfrSwsPll!=(0x02<<2)));
			
		   	if ((rRCC_CFGR & (0x03<<2))== (0x02<<2))
			{
				// 到這裡我們的時鐘整個就設定好了,可以結束了

			}
			else
			{
				// 到這裡就說明PLL輸出作為SYSCLK不成功
				while (1);
			}

		}
		else
		{
			// 到這裡就說明PLL啟動時出錯了,PLL不能穩定工作
			while (1);
		}

	}
	else        //HSE沒有開啟,死迴圈
	{
		// HSE配置超時,說明HSE不可用,一般硬體就有問題要去查
		while (1);   
	}
}