寫在前面:標題“使用內部rc振盪器做時鐘源”其實不太準確,實際應該是“使用內部rc振盪器經PLL倍頻後做時鐘源”,爲了簡單本文統一用“使用內部rc振盪器做時鐘源”。
在做開發時,一些場合對時鐘要求不是非常精確的時候可以省掉外部晶體和兩個電容,好處是可以簡化佈線,節省成本並進一步降低功耗;缺點也很明顯,HSI不夠精準,官方給出的誤差是在1%(25攝氏度)。根據手冊,USB時鐘不能用HSI經PLL後得到,但是實際應用時這樣做是可以的(只是能用,但是非常不推薦)。
在用正點原子例程時,一直找不到相關例程,網上的一些教程也只是談到了原理,代碼部分都需要改動庫函數,不方便移植。後來發現野火的例程裏有,而且可以直接調用,就直接用了,詳細文件是“16-RCC—使用HSE或者HSI配置系統時鐘”的“bsp_clkconfig.c”和“bsp_clkconfig.h”。
文件中有如下函數
void HSI_SetSysClock(uint32_t pllmul)
{
__IO uint32_t HSIStartUpStatus = 0;
// 把RCC外設初始化成復位狀態,這句是必須的
RCC_DeInit();
//使能HSI
RCC_HSICmd(ENABLE);
// 等待 HSI 就緒
HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
// 只有 HSI就緒之後則繼續往下執行
if (HSIStartUpStatus == RCC_CR_HSIRDY)
{
//----------------------------------------------------------------------//
// 使能FLASH 預存取緩衝區
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// SYSCLK週期與閃存訪問時間的比例設置,這裏統一設置成2
// 設置成2的時候,SYSCLK低於48M也可以工作,如果設置成0或者1的時候,
// 如果配置的SYSCLK超出了範圍的話,則會進入硬件錯誤,程序就死了
// 0:0 < SYSCLK <= 24M
// 1:24< SYSCLK <= 48M
// 2:48< SYSCLK <= 72M
FLASH_SetLatency(FLASH_Latency_2);
//----------------------------------------------------------------------//
// AHB預分頻因子設置爲1分頻,HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// APB2預分頻因子設置爲1分頻,PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
// APB1預分頻因子設置爲1分頻,PCLK1 = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
//-----------------設置各種頻率主要就是在這裏設置-------------------//
// 設置PLL時鐘來源爲HSE,設置PLL倍頻因子
// PLLCLK = 4MHz * pllmul
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);
//------------------------------------------------------------------//
// 開啓PLL
RCC_PLLCmd(ENABLE);
// 等待 PLL穩定
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
// 當PLL穩定之後,把PLL時鐘切換爲系統時鐘SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//user_add
//SystemCoreClock=48000000;
// 讀取時鐘切換狀態位,確保PLLCLK被選爲系統時鐘
while (RCC_GetSYSCLKSource() != 0x08)
{
}
}
else
{ // 如果HSI開啓失敗,那麼程序就會來到這裏,用戶可在這裏添加出錯的代碼處理
// 當HSE開啓失敗或者故障的時候,單片機會自動把HSI設置爲系統時鐘,
// HSI是內部的高速時鐘,8MHZ
while (1)
{
}
}
}
本函數可以在主函數中的第一句調用。如果有外部晶體(8M,PLL倍頻9),調用前系統時鐘頻率是72M;如果沒有外部晶體,調用前系統時鐘頻率是HSI的8M。
在調用後,可以將OSC_IN和OSC_OUT引腳重映射(對於100腳以下的芯片,不包括100腳)到PD0,PD1並配置推輓輸出低電平,代碼實現如下:
void OSC_GPIO_Remap(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_PD01, ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD,GPIO_Pin_0|GPIO_Pin_1);
}