STM32F103時鐘系統學習及C語言實現


1 時鐘系統結構圖


2 STM32時鐘系統(一)

2.1 各個時鐘源

① STM32有5個時鐘源:HSI、HSE、 LSI、 LSE、 PLL

  • HSI是高速內部時鐘,RC振盪器,頻率爲8MHz,精度不高
  • HSE是高速外部時鐘,可接石英/陶瓷諧振器,或者接外部時鐘源,頻率範圍爲4MHz ~ 16MHz
  • LSI是低速內部時鐘,RC振盪器,頻率爲40kHz,提供低功耗時鐘
  • LSE是低速外部時鐘,接頻率爲32.768kHz的石英晶體
  • PLL爲鎖相環倍頻輸出,其時鐘輸入源可選擇爲HSI/2、HSE或者HSE/2,倍頻可選擇爲2~16倍,但是其輸出頻率最大不得超過72MHz

② 系統時鐘SYSCLK可來源於三個時鐘源:

  • HSI振盪器時鐘
  • HSE振盪器時鐘
  • PLL時鐘

④ 舉例: Keil編寫程序是默認的時鐘爲72Mhz:

  • 外部晶振(HSE)提供的8MHz,通過PLLXTPRE分頻器
  • 進入PLLSRC選擇開關,通過PLLMUL鎖相環進行倍頻(x9),爲系統提供72MHz的系統時鐘(SYSCLK)
  • AHB預分頻器對時鐘信號進行分頻,爲低速外設提供時鐘

③ 注意:

  • 獨立時鐘源爲4個,因爲PLL本身不能提供時鐘
  • LSI一般作爲IWDGCLK(獨立看門狗)時鐘源和RTC時鐘源
  • 系統時鐘最大頻率爲72MHz

2.2 時鐘信號輸出到外部

  STM32可以選擇一個時鐘信號輸出到MCO腳(PA8)上,可以選擇爲PLL輸出的2分頻、HSI、HSE、或者系統時鐘。可以把時鐘信號輸出供外部使用

2.3 AHB分頻器

  系統時鐘通過AHB分頻器給外設提供時鐘,系統時鐘 -> AHB分頻器 -> 各個外設分頻倍頻器 -> 外設時鐘的設置

  AHB分頻器可選擇1、2、4、8、16、64、128、256、512分頻,輸出的時鐘輸送給5大模塊使用:

  • 內核總線:輸送給AHB總線、內核、內存和DMA使用的HCLK時鐘
  • Tick定時器:通過8分頻後送給Cortex的系統定時器時鐘
  • I2S總線:直接送給Cortex的空閒運行時鐘FCLK
  • APB1外設:送給APB1分頻器。APB1分頻器可選擇1、2、4、8、16分頻,其輸出一路供APB1外設使用(PCLK1,最大頻率36MHz),另一路送給通用定時器使用。該倍頻器可選擇1或者2倍頻,時鐘輸出供定時器2-7使用
  • APB2外設:送給APB2分頻器。APB2分頻器可選擇1、2、4、8、16分頻,其輸出一路供APB2外設使用(PCLK2,最大頻率72MHz),另一路送給高級定時器。該倍頻器可選擇1或者2倍頻,時鐘輸出供定時器1和定時器8使用

2.4 APB1和APB2的對應外設

  APB1上面連接的是低速外設,包括電源接口、備份接口、CAN、USB、I2C1、I2C2、USART2、USART3、UART4、UART5、SPI2、SP3等

  而APB2上面連接的是高速外設,包括UART1、SPI1、Timer1、ADC1、ADC2、ADC3、所有的普通I/O口(PA-PE)、第二功能I/O(AFIO)口等


3 STM32時鐘系統(二)

3.1 時鐘安全系統(CSS)

  如果HSE時鐘發生故障,HSE振盪器被自動關閉,時鐘失效事件將被送到高級定時器(TIM1和TIM8)的剎車輸入端,併產生時鐘安全中斷CSSI,允許軟件完成營救操作。此CSSI中斷連接到Cortex™-M3的NMI中斷(不可屏蔽中斷)

  一旦CSS被激活,並且HSE時鐘出現故障,CSS中斷就產生,並且NMI也自動產生。NMI將被不斷執行,直到CSS 中斷掛起位被清除。因此,在NMI的處 理程序中必須通過設置時鐘中斷寄存器(RCC_ CIR) 裏的CSSC位來清除CSS中斷

  如果HSE振盪器被直接或間接地作爲系統時鐘,(間接的意思是:它被作爲PLL輸入時鐘或通過PLL2,並且PLL時鐘被作爲系統時鐘),時鐘故障將導致系統時鐘自動切換到HSI振盪器,同時外部HSE振盪器被關閉。在時鐘失效時,如果HSE振盪器時鐘(直接的或通過PLL2)是作爲PLL的輸入時鐘,PLL也將被關閉

3.2 RTC時鐘

  通過設置 備份域控制寄存器(RCC_BDCR)裏的RTCSEL[1:0]位,RTCCLK時鐘源可以由HSE/128、LSE或LSI時鐘提供

3.3 看門狗時鐘

  如果獨立看門狗已經由硬件選項或軟件啓動,LSI振盪器將被強制在打開狀態,並且不能被關閉。在LSI振盪器穩定後,時鐘供應給IWDG

3.4 USB時鐘

  STM32中有一個全速功能的USB模塊,其串行接口引擎需要一個頻率爲48MHz的時鐘源。該時鐘源只能從PLL輸出端獲取(唯一的),可以選擇爲1.5分頻或者1分頻,也就是,當需要使用USB模塊時,PLL必須使能,並且時鐘頻率配置爲48MHz或72MHz


4 RCC寄存器

4.1 時鐘控制寄存器(RCC_CR)

4.2 時鐘配置寄存器(RCC_CFGR)

4.3 時鐘中斷寄存器(RCC_CIR)

4.4 APB2外設復位寄存器(RCC_APB2RSTR)

4.5 APB1外設復位寄存器(RCC_APB1RSTR)

4.6 AHB外設時鐘使能寄存器(RCC_AHBENR)

4.7 APB2外設時鐘使能寄存器(RCC_APB2ENR)

4.8 APB1外設時鐘使能寄存器(RCC_APB1ENR)

4.9 備份域控制寄存器(RCC_BDCR)

4.10 控制/狀態寄存器(RCC_CSR)

4.11 AHB外設時鐘復位寄存器(RCC_AHBRSTR)

4.12 時鐘配置寄存器2(RCC_CFGR2)

4.13 RCC寄存器地址映像


5 時鐘系統初始化C語言代碼實現


  參考正點原子STM32F103教程寄存器版本代碼
  工程下載鏈接:鏈接: https://pan.baidu.com/s/1fBTO_HOrW13mPI97cJkqJg 提取碼: jywj


步驟:

定義一個RCC結構體指針,按照RCC_TypeDef類型指向RCC的基地址;STM32訪問寄存器的方式是通過基地址加偏移地址來實現的

/*
C語言書籍這樣定義volatile關鍵字:volatile提醒編譯器它後面所定義的變量隨時都有可能改變,因此編譯後的程序每次需要存儲或
讀取這個變量的時候,告訴編譯器對該變量不做優化,都會直接從變量內存地址中讀取數據,從而可以提供對特殊地址的穩定訪
問。。如果沒有volatile關鍵字,則編譯器可能優化讀取和存儲,可能暫時使用寄存器中的值,如果這個變量由別的程序更新了的
話,將出現不一致的現象。(簡潔的說就是:volatile關鍵詞影響編譯器編譯的結果,用volatile聲明的變量表示該變量隨時可能發
生變化,與該變量有關的運算,不要進行編譯優化,以免出錯)
*/
#define  __IO  volatile  /*!< defines 'read / write' permissions   */

typedef unsigned           int uint32_t;

//所有外設基地址
#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
//AHB總線基地址
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
//RCC寄存器基地址
#define RCC_BASE              (AHBPERIPH_BASE + 0x1000) 

//使用這個結構體,不用再給每一個寄存器初始化其物理地址
//只需要知道RCC寄存器地址即可,利用了結構體對齊的優點
//即指針只要指過來,就可以訪問到該寄存器實際地址了
//參考本文的 4.13 -> RCC寄存器地址映像 左邊兩列
typedef struct
{
  __IO uint32_t CR; ////HSI,HSE,CSS,PLL等的使能和就緒標誌位,用於等待時鐘開始和穩定
  __IO uint32_t CFGR; ////PLL等的時鐘源選擇,分頻係數設定
  __IO uint32_t CIR; //// 清除/使能 時鐘就緒中斷
  __IO uint32_t APB2RSTR; //APB2線上外設復位寄存器
  __IO uint32_t APB1RSTR;//APB1線上外設復位寄存器
  __IO uint32_t AHBENR;//DMA,SDIO等時鐘使能
  __IO uint32_t APB2ENR; //APB2線上外設時鐘使能
  __IO uint32_t APB1ENR;  //APB1線上外設時鐘使能
  __IO uint32_t BDCR; //備份域控制寄存器
  __IO uint32_t CSR;    //控制狀態寄存器
  
} RCC_TypeDef;

#define RCC                 ((RCC_TypeDef *) RCC_BASE)

定義一個函數來複位初始化所有時鐘寄存器、配置向量表

//不能在這裏執行所有外設復位!否則至少引起串口不工作.		    
//把所有時鐘寄存器復位		  
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
}

定義一個可供調用的系統時鐘初始化函數


//系統時鐘初始化函數
//pll:選擇的倍頻數,從2開始,最大值爲16		 
void Stm32_Clock_Init(u8 PLL)
{
	unsigned char temp=0;   
	MYRCC_DeInit();		  //復位並配置向量表
 	RCC->CR|=0x00010000;  //外部高速時鐘使能HSEON
	while(!(RCC->CR>>17));//等待外部時鐘就緒(位17 值1:外部4-16MHz振盪器就緒)
	RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1;
	PLL-=2;				  //抵消2個單位(因爲是從2開始的,設置0就是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;
	}    
}		  

在main函數調用,設置系統時鐘爲72MHz

#include "sys.h"
#include "usart.h"		
#include "delay.h"	
#include "led.h" 

int main(void)
{		 
	Stm32_Clock_Init(9); 	//系統時鐘設置
	delay_init(72);	     	//延時初始化
	LED_Init();		  	 	//初始化與LED連接的硬件接口    
	while(1)
	{
		LED0=0;
		LED1=1;
		delay_ms(300);
		LED0=1;
		LED1=0;
		delay_ms(300);
	}	 
}


6 結束

如有錯誤,還望指正🤞


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