9.1 關于時鐘
時鐘對于一款芯片非常重要,其作用相當于人的心臟,人只有在心率正常穩定的情況下才能健康生活,同樣的,芯片只有作業在合法正常的時鐘頻率下才能保證程式得到正常的運行,
本章就將從時鐘樹開始分析STM32F103的時鐘系統,其中包括內部高速/低速時鐘源、外部高速/低速時鐘源、PLL(鎖相環)和系統滴答定時器,以高屋建瓴的形式讓用戶對STM32F103的時鐘系統有一個整體的認識,并在后續的時鐘配置實驗中讓用戶進一步的了解HAL庫下的時鐘配置流程,
首先讀者要學會如何讀STM32F103系列的時鐘樹,如圖 9.1.1 所示,左側的①HSI(內部高速時鐘)、 ②HSE(外部高速時鐘)、③LSE(外部低速時鐘)、④LSI(內部低速時鐘)為時鐘源,右側的各種片上外設,圖中矩形框內用“/”加數字表示分頻器, ,數字表示幾分頻;矩形框內用“X”加數字表示的為鎖相環, ,數字表示幾倍頻;倒梯形表示選擇器,長邊表示多個輸入,短邊表示選擇其中一個輸出 ,
⑥系統時鐘SYSCLK最高為72MHz,從圖中左側的選擇器SW可以看到來源有三個,分別是:①內部高速時鐘HSI(綠色)、⑤鎖相環時鐘PLLCLK(紫色)和②外部高速時鐘HSE(黃色),而鎖相環時鐘PLLCLK由內部高速時鐘HSI和外部高速時鐘HSE,經過分頻和PLL鎖相環倍頻而來,
內部高速時鐘HSI可直接經過選擇器SW給系統時鐘SYSCLK,此時系統時鐘SYSCLK為8MHz;內部高速時鐘HSI先2分頻,再經過選擇器PLLSRC進入鎖相環PLLMUL,最大倍頻為16倍,得到64MHz的鎖相環時鐘PLLCLK給系統時鐘SYSCLK;當外部高速時鐘HSE(假設外接晶振為8MHz時)直接給選擇器SW,則系統時鐘SYSCLK為8MHz;當外部高速時鐘HSE(假設外接晶振為8MHz時)直接經過選擇器PLLXTPRE給 PLLSRC,再經過PLLMUL 9倍頻,得到72MHz的PLLCLK給系統時鐘SYSCLK,
⑩RTCCLK(實時時鐘)的時鐘源也有三個,分別是②外部高速時鐘HSE的128倍分頻(黃色)、③外 部低速時鐘LSE的32.768kHz(藍色)、④內部低速時鐘LSI的40kHz(橙色),
11 IWDGCLK(獨立看門狗)的時鐘來源于④內部低速時鐘LSI的40kHz(橙色),
理清各個時鐘來源后,再來看各總線的時鐘,⑦高速介面總線AHB由⑥SYSCLK系統時鐘分頻得到,最高是系統時鐘的72MHz,⑧外設總線APB1和⑨外設總線APB2,來源于⑦高速介面總線AHB,APB1的輸出時鐘最高是36MHz,APB2的輸出時鐘最高是72MHz,APB1和APB2下有各種外設,比如GPIO、USART等,

9.2 硬體設計
內部時鐘HSI不涉及硬體,外部時鐘HSE參考前面最小系統的時鐘電路,“5.2.2 時鐘電路”,
9.3 軟體設計
9.3.1軟體設計思路
實驗目的:分別使用內部時鐘HSI和外部時鐘HSE作為系統時鐘,
- 使用內部時鐘HSI配置系統時鐘到最大值64Mhz;
- 呼叫庫函式讀取系統時鐘值以驗證;
- 使用外部時鐘HSE配置系統時鐘到最大值72Mhz;
- 呼叫庫函式讀取系統時鐘值以驗證;
本實驗配套代碼位于“5_程式原始碼\3_基礎重點—時鐘系統”,
9.3.2軟體設計講解
- 使用內部時鐘源HSI作為PLL的時鐘源,然后將PLL作為系統時鐘源
內部時鐘HSI配置如代碼段 9.3.1 所示,HAL庫定義了兩個結構體,只需要設定這兩個結構體成員,就
完成對時鐘的設定,
代碼段 9.3.1 使用 HSI 作為系統時鐘(stm32f1xx_clk.c)
/**
* @brief System Clock Configuration
* The system Clock is configured as follow : * System Clock source = PLL (HSI)
* SYSCLK(Hz) = 64000000
* HCLK(Hz) = 64000000
* AHB Prescaler = 1 * APB1 Prescaler = 2 * APB2 Prescaler = 1 * PLLMUL = 16
* Flash Latency(WS) = 2 * @param None
* @retval None
*/
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSEState = RCC_HSE_OFF;
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_HSI_DIV2;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
while(1);
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK
| 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);
} }
- 17行:定義RCC_OscInitTypeDef結構體變數,該結構體主要是選擇時鐘源、設定時鐘開關狀態、PLL倍頻;
- -20行:設定時鐘來源為內部高速時鐘HSI;
- 21行:不使用外部高速時鐘,則HSE關閉;
- 22行:設定HSE預分頻,這里沒用上HSE,默認為1分頻即可;
- 23行:內部高速時鐘HSI打開;
- 24行:鎖相環PLL打開;
- 25行:選擇PLL時鐘源,為HSI的二分頻;
- 26行:PLL的倍頻為16倍頻;
- 27行:使用“HAL_RCC_ClockConfig()”函式設定RCC_OscInitStruct;
- 18行:定義RCC_ClkInitTypeDef結構體變數,該結構體主要是配置系統時鐘、AHB、APB1、APB2的分頻;
- 34-35行:設定哪些時鐘將被設定;
- 36行:設定系統時鐘SYSCLK的來源為PLLCLK;
- 37行:設定HCLK時鐘(AHB Clock)為1分頻(不能超過最大72MHz);
- 38行:設定PCLK1時鐘(APB1 Clock)為2分頻(不能超過最大36MHz);
- 39行:設定PCLK2時鐘(APB2 Clock)為1分頻(不能超過最大72MHz);
- 41行:用“HAL_RCC_ClockConfig()”函式設定RCC_ClkInitStruct;
將代碼和時鐘樹進行對照,如圖 9.3.1 所示,紅色數字為對應代碼行號,藍色數字為時鐘頻率,紅色線段為時鐘走向,內部高速時鐘HSI經過二分頻為4MHz,再經過PLL 16倍頻為64MHz,作為系統時鐘SYSCLK, 再1分頻給AHB,AHB再2分頻給APB1,1分頻給APB2,

2) 獲取系統時鐘的函式
主函式里呼叫HAL庫提供的“HAL_RCC_GetSysClockFreq()”函式獲取系統時鐘驗證,
代碼段 9.3.2 獲取系統時鐘(main.c)
// 此處定義全域變數以便在 debug 的時候可以看到這個變數的值
uint32_t sys_clk = 0;
int main(void) {
// 初始化 HAL 庫函式必須要呼叫此函式
HAL_Init();
/*
* 系統時鐘即 AHB/APB 時鐘配置
* 當使用內部高速時鐘 HSI(8MHz)配置系統時鐘時,使用 PLL 前會默認先二分頻得到 4MHz 的 PLL 分頻輸入頻率
* 然后經過鎖相環放大,最大放大倍數為 16,即 4*16=64MHz 是能配置的最大系統頻率,F103 的最大系統頻率為 72MHz,64MHz 顯然是合法
的系統頻率
*/
SystemClock_Config();
// 呼叫庫函式來檢驗自己的配置是否成功配置為系統頻率 64MHz
sys_clk = HAL_RCC_GetSysClockFreq();
while(1);
}
- 使用外部時鐘源HSE作為PLL的時鐘源,然后將PLL作為系統時鐘源
與前面類似,這里設定外部高速時鐘HSE作為系統時鐘,仍是設定兩個結構體,
代碼段 9.3.3 使用 HSE 作為系統時鐘(stm32f1xx_clk.c)
/**
* @brief System Clock Configuration
* The system Clock is configured as follow : * System Clock source = PLL (HSE) * SYSCLK(Hz) = 72000000
* HCLK(Hz) = 72000000
* AHB Prescaler = 1 * APB1 Prescaler = 2 * APB2 Prescaler = 1 * PLLMUL = 9 * Flash Latency(WS) = 2 * @param None
* @retval None
*/
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
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);
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK
| 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);
} }
- 17行:定義RCC_OscInitTypeDef結構體變數,該結構體主要是選擇時鐘源、設定時鐘開關狀態、PLL倍頻;
- 20行:設定時鐘來源為外部高速時鐘HSE;
- 21行:內部高速時鐘HSE打開;
- 22行:設定HSE預分頻,設定為1分頻;
- 23行:不使用內部高速時鐘,則HSI關閉;
- 24行:鎖相環PLL打開;
- 25行:選擇PLL時鐘源,為HSE;
- 26行:PLL的倍頻為9倍頻;
- 27行:使用“HAL_RCC_ClockConfig()”函式設定RCC_OscInitStruct;
- 18行:定義RCC_ClkInitTypeDef結構體變數,該結構體主要是配置系統時鐘、AHB、APB1、APB2的分頻;
- 34-35行:設定哪些時鐘將被設定;
- 36行:設定系統時鐘SYSCLK的來源為PLLCLK;
- 37行:設定HCLK時鐘(AHB Clock)為1分頻(不能超過最大72MHz);
- 38行:設定PCLK1時鐘(APB1 Clock)為2分頻(不能超過最大36MHz);
- 39行:設定PCLK2時鐘(APB2 Clock)為1分頻(不能超過最大72MHz);
- 41行:用“HAL_RCC_ClockConfig()”函式設定RCC_ClkInitStruct;
將代碼和時鐘樹進行對照,如圖 9.3.2 所示,紅色數字為對應代碼行號,藍色數字為時鐘頻率,紅色線段為時鐘走向,外部高速時鐘HSE不分頻,通過選擇器PLLXTPRE和選擇器PLLRC,再PLL 9倍頻為72MHz,作為系統時鐘SYSCLK,再1分頻給AHB,AHB再2分頻給APB1,1分頻給APB2,當前狀態,為MCU全速作業狀態,

4) 獲取系統時鐘的函式
與前面類似,主函式里呼叫HAL庫提供的“HAL_RCC_GetSysClockFreq()”函式獲取系統時鐘驗證,
代碼段 9.3.4 獲取系統時鐘(main.c)
uint32_t sys_clk = 0;
int main(void) {
// 初始化 HAL 庫函式必須要呼叫此函式
HAL_Init();
/*
* 系統時鐘即 AHB/APB 時鐘配置
* 當使用外部高速時鐘 HSE(8MHz)配置系統時鐘時,使用 PLL 前可以選擇不分頻或者二分頻,我們要配置到最大 72MHz 的系統頻率此處當
然是不分頻
* 然后經過鎖相環放大,最大放大倍數為 16,我們要想得到 72MHz,此處選擇 9 倍放大系數,即 8*9=72MHz 即可達到目標
*/
SystemClock_Config();
// 呼叫庫函式來檢驗自己的配置是否成功配置為系統頻率 64MHz
sys_clk = HAL_RCC_GetSysClockFreq();
while(1);
}
9.4 實驗效果
本實驗對應配套資料的“5_程式原始碼\3_基礎重點—時鐘系統\”下的“3.1_HSI配置系統時鐘64M”和
“3.2_HSE配置系統時鐘72M”,打開工程后,參考前面“4.4 下載程式和除錯”進入除錯模式,在代碼中找到“sys_clk”選中,右鍵選擇“Add ‘sys_clk’ to …”->“Watch 1”,即可在右下角顯示該變數查看視窗,可以看到當前“sys_clk”為0,如圖 9.4.1 所示,

在“while(1);”處加上斷點,運行程式,可以看到“sys_clk”分別為64000000和72000000,如圖 9.4.3 和 圖 9.4.4 所示,與代碼設計吻合,如果顯示16進制,可選中該數字,右鍵去掉勾選“Hexadecimal Dispaly”,如圖 9.4.2 所示,



百問網技術論壇:
http://bbs.100ask.net/
百問網嵌入式視頻官網:
https://www.100ask.net/index
百問網開發板:
淘寶:https://100ask.taobao.com/
天貓:https://weidongshan.tmall.com/
技術交流群(鴻蒙開發/Linux/嵌入式/驅動/資料下載)
QQ群:869222007(已滿)752871361
單片機-嵌入式Linux交流群:
QQ群:536785813
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/277414.html
標籤:其他
上一篇:51單片機培訓計劃(新訂)
