**
這部分感覺有點亂可以先看後邊的總結部分,代碼的上邊那裏
**
stm32的時鐘就是這個圖。時鐘她是一級一級整上去的。通過pll鎖相環,把外部晶振的頻率給他頂上去。再需要多少就分頻多少,得到需要的頻率
我們要通過外部晶振HSE來得到apb2處的點亮gpiob處的流水燈。需要的是72mhz的頻率
所以看圖
是這麼個路線
但是這個是要配置的。所以
這個是內部時鐘,上電以後內部時鐘先運行,讓cpu能夠切換到外部時鐘那裏去。
所以我們看圖,
先是這個,決定輸入的時鐘是否二分頻。
然後是src,選擇內部還是外部時鐘源
mul來倍頻
下一步這裏,sw選擇用哪個做系統時鐘源
下一步ahb分頻。
下一步apb2分頻,也就是pclk2
這樣就可以得到想要的時鐘頻率
然後我們來查手冊
第一個PLLXTPRE,
這個寄存器
第十七位,就是我們的第一道門檻,因爲復位值是0x00000000
所以這個不用管,自動不分頻。
然後PLLSRC
用來選擇哪個時鐘源來pll倍頻
我們選hse,置一。
然後還是這個寄存器,PLLMUL,第18-21位。我們的晶振是8mhz,所以要9倍頻,就是0111。
然後sw,0-1位,我們選10,倍頻過後的時鐘給他。
往下走,是ahb預分頻器,我們不讓他分頻,0000
繼續往下,apb2分頻器,不分頻,000
輸出就是72mhz
這裏注意了,pll還有個專門配置的寄存器,就是RCC_CR
偏移量0,復位值0x00000083
24位置一,然後等PLL準備好後,25位由硬件置一,代表可用了。
16位,我們要啓用外部時鐘源,得置一。
準備好後,這個17位就會被硬件置一,代表準備完畢
0位是1,因爲一開始cpu肯定要有一個時鐘來提供時鐘源,就是這個高速內部時鐘源。他先運行起來,然後才能切換到外部
1位也是1,能用肯定是就緒的。下一個是內部高速時鐘調整,這個是人家系統默認的。10000
所以0x83是這麼來的。1000 0011
————————————————————————————_________________------------------------------------------------------
總結一下。
- RCC_CR 地址:0x40021000 + 0 = 0x40021000 復位值:0x00000083
16位HSEON置一,然後判斷17位HSERDY,外部時鐘就緒後進性下一步 - RCC_CFGR 地址:0x40021000 + 0x04 = 0x40021004 復位值:0x00000000
就是我們上邊說的哪個順序,16位PLLSRC置1,17位PLLXTPRE置0 ,然後18-21位的PLLMUL 置0111,4-7位AHB預分頻置0000不分頻,8-10位APB1預分頻置100,2分頻(因爲ahb是72mhz,apb1最高可以36mhz,所以必須分頻)。11-13位APB2預分頻置000,不分頻。 - RCC_CR
24位PLLON置一,判斷25位PLLRDY,爲1說明就緒,繼續下一步。 - RCC_CFGR
0-1位SW置10,PLL輸出作爲系統時鐘,然後判斷2-3位,是否爲10(PLL輸出作爲系統時鐘)
配置完畢
思路很簡單,先配置外部時鐘穩定,然後設置裏邊的時鐘路線,配置完畢就啓動PLL開始倍頻。倍頻完畢後把倍頻出來的結果送給系統。切換完成
所以是三層的嵌套。第一層判斷HSE是否準備完畢,第二層判斷PLL是否倍頻完畢,第三層判斷系統時鐘是否已經切換爲PLL的輸出
下面是代碼部分
設置時鐘函數。我都給他分頻了賊低。
#include "setClock.h"
void SetClock72MHZ(void )
{
uint GPIO_LED_HSERDY = 0;
uint GPIO_LED_PLLRDY = 0;
uint GPIO_LED_SWS = 0;
uint WAIT_TIME = 0;
rRCC_CR = 0x00000083;
rRCC_CR &= ~(1 << 16);
rRCC_CR |= (1 << 16);
do
{
GPIO_LED_HSERDY = rRCC_CR & (1 << 17);
WAIT_TIME ++;
}
while((WAIT_TIME < 0x0fffffff) && (GPIO_LED_HSERDY == 0));//confirm it's 1 or no respond
if((rRCC_CR & (1 << 17)) != 0) //confirm it's 1 again
{
//rFLASH_ACR |= 0x10;
//rFLASH_ACR &= (~0x03);
//rFLASH_ACR |= (0x02);
//now, HSE is ok, you can set references
rRCC_CFGR &= ~((0x0f << 4) | (0x07 << 8) | (0x07 << 11));
rRCC_CFGR |= ((0x09 << 4) | (0x04 << 8) | (0x07 << 11));
rRCC_CFGR &= ~((1 << 16) | (1 << 17));
rRCC_CFGR |= ((1 << 16) | (0 << 17));
rRCC_CFGR &= ~(0x0f << 18);
rRCC_CFGR |= (0x07 << 18);
rRCC_CR &= ~(1 << 24);
rRCC_CR |= (1 << 24);
WAIT_TIME = 0;
do
{
//ledFlash();
delay(50);
GPIO_LED_PLLRDY = rRCC_CR & (1 << 25);
WAIT_TIME ++;
}
while(GPIO_LED_PLLRDY == 0);
if((rRCC_CR & (1 << 25)) != 0)
{
//PLL is ready
rRCC_CFGR &= ~(0x03 << 0);
rRCC_CFGR |= (0x02 << 0);
WAIT_TIME = 0;
ledFlash();
do
{
GPIO_LED_SWS = rRCC_CR & (0x03 << 2);
WAIT_TIME ++;
}
while((WAIT_TIME < 0x0fffffff) && (GPIO_LED_SWS != (0x02 << 2)));
if((rRCC_CR & (0x03 << 2)) == (0x02 << 2))
{
//all ready
}
else
{
while(1); //pll set is wrong
}
}
else
{
while (1);// PLL is wrong
}
}
else
{
while (1);//HSE is wrong
}
}
咱們是用位操作乾的,不是用庫函數。用位操作就很容易理解這玩意的原理。
爲什麼要先與取反呢
是因爲先要給他清零,再給值
//2020.5.5, the last day of haoliday. The first day for study every day...
#include "GPIO_LED72MHZ.h"
#include "setClock.h"
void main(void)
{
ledInit();
ledFlash();
ledInit();
SetClock72MHZ();
ledFlash();
while(1);
//return 0;
}
void ledInit(void)
{
rRCC_APB2ENR = 0x00000008;
rGPIOB_CRH = 0x33333333;
rGPIOB_ODR = 0x0000ff00;
}
void ledFlash(void)
{
unsigned char i,j;
for(i = 0; i < 4; i ++)
{
rGPIOB_ODR = 0x0000ff00;
for(j = 0; j < 100; j++)
delay(50000);
rGPIOB_ODR = 0x0000f000;
for(j = 0; j < 100; j++)
delay(50000);
}
}
void delay(unsigned int i)
{
while(i --);
}
這個是主函數,明顯看到改變以後閃的慢了