時鐘設置
時鐘樹介紹
有以下三種時鐘可以作爲系統時鐘(SYSCLK):
- HSI 振盪器時鐘
- HSE 振盪器時鐘
- PLL 時鐘
時鐘樹如下:
-
OSC_OUT 和 OSC_IN 爲外部晶振的接口,爲 HSE(高速) 提供振盪源,接 4 -16 MHz 的晶振;
-
OSC32_OUT 和 OSC32_IN 爲外部晶振的接口,爲 LSE(低速) 提供振盪源,接 32.768 kHz 的晶振。其爲看門狗時鐘 (IWDGCLK) 提供動力;
-
LSI RC 爲內部低速振盪器,頻率爲 40kHz;
-
RTCCLK (Real Time Clock) 由 HSE/128、LSE 和 LSI 提供動力;
-
PLLXTPRE 爲預分頻選擇器,主要選擇 不分頻 或 /2,它和 HSI RC 8MHz(內部高速振盪器)的 /2 共同接入到了 PLLSRC ,由其選擇使用外部時鐘 HSE 還是內部時鐘 HSI,再將時鐘信號輸入到 PLLMUL;
-
HSI RC 8MHz 不但有上述作用,flash 編程接口也是由其驅動的;
-
PLLMUL 爲倍頻器,最高倍頻到 16,主要倍頻 HSI 或 HSE,得到 PLLCLK,其可用作 USBCLK 的驅動源,一般預分頻到 48 MHz;
-
CSS
-
SYSCLK 主要由 HSE、HSI 或 PLLCLK 提供,最大頻率爲 72 MHz;
-
I2S3、I2S2 都是由 SYSCLK 驅動的;
-
SYSCLK 主要爲 AHB 總線提供驅動:
上圖可以看到 AHB 系統總線主要與 SDIO、RCC、APB1 及 APB2 交互,且爲它們提供時鐘驅動。APB2 和 APB1 兩個總線連接上圖的片上外設;
- SYSCLK 會經過 AHB 預分頻器(/1,…,/512),從而爲其他總線提供時鐘:
- 爲 HCLK 提供時鐘,頻率最高爲 72 MHz。HCLK 至 AHB BUS、core、memory 及 DMA 等;
- AHB/8 至 Cortex 系統定時器;
- AHB/1 也可直接爲 FCLK Cortex 自由運行定時器提供驅動;
- APB1 總線的最大頻率爲 36 MHz,由此需要經由 APB1 預分頻器分頻(/1,2,4,8,16)。再由外設時鐘使能得到 PCLK1 至 APB1 總線上的外設;
- APB2 總線的最大頻率爲 72 MHz,同樣需要經由 APB2 預分頻器分頻(/1,2,4,8,16)。再由外設時鐘使能得到 PCLK2 至 APB2 總線上的外設;
- APB2 總線的時鐘頻率在給 ADC 驅動時,需要先分頻(/2,4,6,8),ADCCLK 最大頻率爲 14 MHz;
- AHB/2 經由外設時鐘使能(HCLK/2),至 SDIO AHB 接口;
- MCO(主時鐘輸出)由以下 4 個時鐘源以供選擇:
- PLLCLK
- HSI
- HSE
- SYSCLK
#include <stm32f10x.h>
// 設置向量表偏移地址
//NVIC_VectTab: 基址
//Offset: 偏移量
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset){
//設置NVIC的向量表偏移寄存器,用於標識向量表在CODE區還是RAM區
SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);
}
// 系統初始的配置:主要用來配置向量表
void MYRCC_DeInit(void) {
RCC->APB1RSTR = 0x0000 0000; // No effect
RCC->APB2RSTR = 0x0000 0000;
// 初始是睡眠狀態,打開閃存接口電路時鐘使能(FLITFEN)和SRAM時鐘使能(SRAMEN)
RCC->AHBENR = 0x0000 0014;
RCC->APB2ENR = 0x0000 0000;
RCC->APB1ENR = 0x0000 0000;
// 使能內部高速時鐘HSI開始工作
RCC->CR |= RCC_CR_HSION;
// datasheet's note:先配置,再控制
// 配置RCC(復位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]):SYSClock<--HSI(時鐘樹的SW處), SYSClock不分頻,APB1和APB2也都不分頻,ADC默認/2,PLL時鐘源選擇HSE(現在用不到), MCO:No Clock¸
RCC->CFGR &= 0xF8FF 0000;
// 關閉 HSE、PLL、CSS 使能
RCC->CR &= 0xFEF6FFFF;
// 在HSE未使能時,設置HSE晶振沒有旁路
RCC->CR &= 0xFFFBFFFF;
// 復位 PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE
RCC->CFGR &= 0xFF80FFFF;
// 清除所有中斷標誌
RCC->CIR = 0x00000000;
// 配置向量表
#ifdef VECT_TAB_RAM
MY_NVIC_SetVectorTable(0x20000000, 0x0);
#else
MY_NVIC_SetVectorTable(0x08000000,0x0);
#endif
}
// stm32時鐘的初始化
void Stm32_Clock_Init(u8 pll){
// 復位並配置向量表
MYRCC_DeInit();
// 使能HSEON
RCC->CR |= RCC_CR_HSEON;
// 等待 HSE 使能就緒:HSERDY == 1
while(!(RCC->CR>>17));
// APB1 = DIV2; APB2 = DIV1; AHB = DIV1
RCC->CFGR = (RCC_CFGR_PPRE1_DIV2 + RCC_CFGR_PPRE2_DIV1 + RCC_CFGR_HPRE_DIV1);
// PLLMUL[21:18] ====> 0000: PLL Clock x 2,所以要 -2.
PLL -= 2;
// 設置倍頻,PLL = 9 即倍頻到72MHz
RCC->CFGR |= PLL<<18;
// PLL Clock <--- HSE
RCC->CFGR |= RCC_CFGR_PLLSRC;
// ?? FLASH 兩個延時週期
FLASH->ACR|=0x32;
// 使能PLL時鐘
RCC->CR |= RCC_CR_PLLON;
// 等待PLL時鐘就緒:PLLRDY == 1
while(!(RCC->CR>>25));
// 設置 PLL 爲 SYSCLK
RCC->CFGR |= RCC_CFGR_SW_PLL;
// 等待SYSCLK設置就緒:SWS == 10 -- PLL
while(temp!=0x02)
{
temp = RCC->CFGR>>2;
temp &= 0x03;
}
}