1、STM32F103ZET6時鍾說明
STM32F103ZET6的時鍾樹圖如下所示:
STM32F103ZET6有很多個時鍾源,分別有:
HSE:高速外部時鍾信號。
HSI:高速內部部時鍾信號。
LSI:低速內部時鍾信號。
LSE:低速外部時鍾信號。
HSI和LSI是芯片內置的時鍾源,它們的頻率大小是固定的,HSI是8MHZ,LSI是大約40KHZ。
時鍾樹中的序號1是高速外部時鍾信號HSE:
HSE是由有源晶振或無源晶振通過OSC_OUT和OSC_IN腳提供的,從圖片中可以看到,HSE頻率從4MHZ到16MHZ不等。當使用有源晶振時,時鍾從OSC_IN引腳進入,OSC_OUT引腳懸空;當使用無源晶振時,時鍾從OSC_IN和OSC_OUT進入,並且要配諧振電容。HSE最常使用的就是8MHZ的無源晶振。
時鍾樹中的序號D是外部低速時鍾LSE:
LSE是由有源晶振或無源晶振通過OSC32_OUT和OSC32_IN腳提供的。LSE一般使用的是32.768KHZ的無源晶振。
時鍾樹中的序號2是選擇PLL(倍頻后的時鍾)的時鍾源:
從圖中可以看出,PLL時鍾的來源可以是HSE或HSI/2,通過PLLSRC(CFGR寄存器的bit16)來選擇使用哪一個時鍾源。HSI是8MHZ的內部高速時鍾信號,HSI會根據溫度和環境的情況頻率會有漂移,一般不作為PLL的時鍾來源。一般使用HSE作為PLL的時鍾源。
時鍾樹中的序號3是設置PLL的倍頻因子:
可以對PLL的時鍾來源進行倍頻,然后得到PLLCLK時鍾源。倍頻因子可以通過時鍾配置寄存器CFGR的bit21~bit18:PLLMUL[3:0]來配置,分別可配置成2、3、4、5、6、7、8、9、10、11、12、13、14、15、16倍頻。舉個例子來說,如果選擇HSE作為PLL的時鍾源,而且HSE=8MHZ,且將PLL的倍頻因子設置為9倍頻,那么PLLCLK=9*8MHZ = 72MZH。需要注意的是PLLCLK的頻率不要超過72MHZ,想跑超頻也可以,最大可以跑到128MHZ,不過一旦出問題ST是不負責的。
時鍾樹中的序號4是系統時鍾SYSCLK時鍾源的選擇:
從圖中可以看出,系統時鍾SYSCLK的來源可以選擇為HSI、PLLCLK、HSE。可以通過時鍾配置寄存器CFGR的bit1~bit0:SW[1:0]來選擇系統時鍾SYSCLK的來源,一般選用PLLCLK,將系統時鍾SYSCLK設置為72MHZ。
在SMT32剛上電的時候,由於還沒有配置過時鍾,所以用的是系統內部8MHZ的HSI時鍾,也就是說上電的時候系統時鍾SYSCLK的頻率為8MHZ。
時鍾樹中的序號5是AHB總線時鍾HCLK:
從圖中可以看出AHB總線時鍾HCLK是系統時鍾SYSCLK經過AHB預分頻器分頻之后得到的時鍾。時鍾配置寄存器CFGR的bit7~bit4:HPRE[3:0]可以設置AHB總線時鍾HCLK的分頻系數,分頻系數可以設置為:1、2、4、8、16、64、128、256、512等。一般分頻系數設置為1,即HCLK = SYSCLK。
時鍾樹中的序號B是CortexM3的系統滴答時鍾SYSTICK:
從圖中可以看出系統滴答時鍾SYSTICK的來源是AHB總線時鍾HCLK經過8分頻之后的時鍾。AHB總線時鍾HCK一般是不分頻的,所以系統滴答時鍾SYSTICK的頻率就等於SYSCLK/8,如果SYSCLK為72MHZ,那么系統滴答時鍾SYSTICK的頻率就是9MHZ。
時鍾樹中的序號6是APB1總線時鍾PCLK1:
這是APB1總線上外設的時鍾。從圖中可以看出,APB1總線時鍾PCLK1的最大頻率為36MHZ,所以掛載在APB1總線時鍾PCLK1上的外設,最大時鍾都是36MHZ,除了APB1總線時鍾PCLK1上的定時器除外。
從圖中可以看出,如果APB1總線時鍾PCLK1的分頻系數為1(即不分頻),那么掛載在APB1總線上的定時器的頻率就是APB1總線時鍾PCLK1的頻率;如果APB1總線時鍾PCLK1的分頻系數不為1(即分頻),那么掛載在APB1總線上的定時器的頻率就是APB1總線時鍾PCLK1的頻率的2倍。
一般將APB1總線時鍾PCLK1的時鍾配置為36MHZ,如果AHB總線時鍾HCLK設置為72MHZ,那么APB1總線時鍾PCLK1的分頻系數就設置為1。
時鍾樹中的序號7是APB2總線時鍾PCLK2:
這是APB2總線上外設的時鍾。從圖中可以看出,APB2總線時鍾PCLK2的最大頻率為72MHZ,所以掛載在APB2總線時鍾PCLK2上的外設,最大時鍾都是72MHZ,除了APB2總線時鍾PCLK2上的定時器除外。
從圖中可以看出,如果APB2總線時鍾PCLK2的分頻系數為1(即不分頻),那么掛載在APB2總線上的定時器的頻率就是APB2總線時鍾PCLK2的頻率;如果APB2總線時鍾PCLK2的分頻系數不為1(即分頻),那么掛載在APB2總線上的定時器的頻率就是APB2總線時鍾PCLK2的頻率的2倍。
APB2總線時鍾PCLK2可以通過預分頻器設置,一般默認設置為1,即不分頻。
時鍾樹中的序號C是ADC的時鍾:
從圖中可以看出,ADC的時鍾源是由APB2總線時鍾PCLK2經過分頻后得到的。
時鍾樹中的序號E是主時鍾輸出信號:
從圖中可以看出MCO的腳位可以輸出主時鍾的信號,MCO可以輸出PLLCLK/2、HSI、HSE、SYSCLK等信號。
一般將STM32F103ZET6的時鍾配置如下:
HSE = 8MHZ。
PLLCLK = HSE * 9 = 72MHZ。
SYSCLK = PLLCLK = 72MHZ。
AHB總線時鍾HCLK = SYSCLK/1 = 72MHZ。
APB1總線時鍾PCLK1 = AHB總線時鍾HCLK/2 = 36MHZ。
APB2總線時鍾PCLK2 = AHB總線時鍾HCLK/1 = 72MHZ。
2、HAL庫版本配置時鍾
HAL庫是通過HAL_RCC_OscConfig和HAL_RCC_ClockConfig這兩個函數對STM32F103ZET6的時鍾進行配置。函數如下:
void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK) { while(1); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2)!= HAL_OK) { while(1); } }
HAL_RCC_OscConfig函數的作用是選擇時鍾源,設置PLLCLK的時鍾頻率。
HAL_RCC_ClockConfig函數的作用是配置系統時鍾SYSCLK、AHB總線時鍾HCLK、APB1總線時鍾PCLK1、APB2總線時鍾PCLK2的頻率。
HAL_RCC_OscConfig函數參數說明:
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE是選擇外部高速時鍾HSE。
RCC_OscInitStruct.HSEState = RCC_HSE_ON是使能外部高速時鍾HSE。
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1是選擇HSE時鍾還是HSE/2時鍾作為PLL的時鍾輸入。
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON是使能PLL功能。
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE是通過PLLSRC(CFGR寄存器的bit16)選擇HSE作為PLL的輸入時鍾。
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9是設置PLL的倍頻系數。
HAL_RCC_ClockConfig函數參數說明:
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2是選擇時鍾類型。
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK是選擇系統時鍾SYSCLK的時鍾源。
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1是設置AHB總線時鍾HCLK的分頻系數。
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2是設置APB1總線時鍾PCLK1的分頻系數。
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1是設置APB2總線時鍾PCLK2的分頻系數。
從上面的程序還可以才看出,如果HAL_RCC_OscConfig和HAL_RCC_ClockConfig這兩個函數如果配置不成功,那么就會進入死循環,無法運行下去。
3、寄存器版本配置時鍾
這里的配置時鍾的函數是摘錄了正點原子的時鍾配置函數來進行說明,函數如下:
//系統時鍾初始化函數 //pll:選擇的倍頻數,從 2 開始,最大值為 16 void Stm32_Clock_Init(u8 PLL) { unsigned char temp=0; MYRCC_DeInit(); //復位並配置向量表 RCC->CR|=0x00010000; //外部高速時鍾使能 HSEON while(!(RCC->CR>>17));//等待外部時鍾就緒 RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1; PLL-=2;//抵消 2 個單位 RCC->CFGR|=PLL<<18; //設置 PLL 值 2~16 RCC->CFGR|=1<<16; //PLLSRC ON FLASH->ACR|=0x32; //FLASH 2 個延時周期 RCC->CR|=0x01000000; //PLLON while(!(RCC->CR>>25));//等待 PLL 鎖定 RCC->CFGR|=0x00000002;//PLL 作為系統時鍾 while(temp!=0x02) //等待 PLL 作為系統時鍾設置成功 { temp=RCC->CFGR>>2; temp&=0x03; } }
在Stm32_Clock_Init函數中,設置了APB1總線時鍾PCLK1為2分頻,APB2總線時鍾PCLK2為1分頻,AHB總線時鍾HCLK為1分頻,同時選擇PLLCLK作為系統時鍾SYSCLK。Stm32_Clock_Init函數只有一個PLL的參數,是用設置PLL的倍頻系數的。比如當HSE = 8MHZ,PLL的倍頻系數設置為9,那么STM32將運行在72MHZ的速度下。
MYRCC_DeInit函數實現外設的復位,並關斷所有中斷,同時調用向量表配置函數MY_NVIC_SetVectorTable,配置中斷向量表。
//把所有時鍾寄存器復位 void MYRCC_DeInit(void) { RCC->APB1RSTR = 0x00000000;//復位結束 RCC->APB2RSTR = 0x00000000; RCC->AHBENR = 0x00000014; //睡眠模式閃存和 SRAM 時鍾使能.其他關閉. RCC->APB2ENR = 0x00000000; //外設時鍾關閉. RCC->APB1ENR = 0x00000000; RCC->CR |= 0x00000001; //使能內部高速時鍾 HSION RCC->CFGR &= 0xF8FF0000; //復位 SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0] RCC->CR &= 0xFEF6FFFF; //復位 HSEON,CSSON,PLLON RCC->CR &= 0xFFFBFFFF; //復位 HSEBYP RCC->CFGR &= 0xFF80FFFF;//復位 PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE RCC->CIR = 0x00000000; //關閉所有中斷 //配置向量表 #ifdef VECT_TAB_RAM MY_NVIC_SetVectorTable(0x20000000, 0x0); #else MY_NVIC_SetVectorTable(0x08000000,0x0); #endif }
MY_NVIC_SetVectorTable函數是用來配置中斷向量表基址和偏移量的,決定向量表在哪個區域。
//設置向量表偏移地址 //NVIC_VectTab:基址 //Offset:偏移量
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset) { SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);//設置NVIC的向量表偏移寄存器 //用於標識向量表是在CODE區還是在RAM區
}
4、通過MCO口輸出系統時鍾SYSCLK
如果想驗證時鍾是否配置成功,可以通過MCO輸出相應的時鍾,通過示波器檢驗輸出是否正確。
MCO可以輸出PLLCLK/2、HSI、HSE、SYSCLK等信號。