時(shí)鐘問題

open這個(gè)系統(tǒng)調(diào)用會(huì)建立一條到文件或者設(shè)備的訪問路徑,如果open調(diào)用成功的話,那么它將返回一個(gè)可以被read系統(tǒng)調(diào)用,write系統(tǒng)調(diào)用等方法使用的文件描述符。文件描述符是唯一的,他不會(huì)與任何其它運(yùn)行中的進(jìn)程共享。如果兩個(gè)進(jìn)程同時(shí)打開一個(gè)文件的話,那么它們將會(huì)得到兩個(gè)不同的文件描述符,如果這兩個(gè)進(jìn)程都對(duì)文件進(jìn)行寫操作的話,那么它們會(huì)各寫各的,這就造成了一個(gè)問題,誰也搞不清哪部分?jǐn)?shù)據(jù)將會(huì)被覆蓋。對(duì)此,我們要利用文件鎖來防止這種問題的出現(xiàn)。


STM32時(shí)鐘樹

當(dāng)芯片開始復(fù)位的時(shí)候,代碼的執(zhí)行順序是先調(diào)用SystemInit函數(shù),接著在進(jìn)入_main函數(shù)(注意這個(gè)函數(shù)與main函數(shù)的區(qū)別,_main是一個(gè)c標(biāo)準(zhǔn)庫的初始化函數(shù)),執(zhí)行完_main函數(shù)后,最終才會(huì)執(zhí)行用戶文件里面所定義的main函數(shù)。

至于為什么會(huì)是這樣,我們可以看看芯片的啟動(dòng)文件。對(duì)于我們的STM32芯片來說,ST已經(jīng)提供了啟動(dòng)文件startup_stm32f10x_hd.s啟動(dòng)文件,我們截取相關(guān)的部分代碼如下:

Reset_Handler PROC
  EXPORT Reset_Handler
  IMPORT __main ;從外部導(dǎo)入__main方法
  IMPORT SystemInit ;同理
  LDR R0, = SystemInit ;方法地址存儲(chǔ)在了r0寄存器
  BLX R0;跳轉(zhuǎn)執(zhí)行SystemInit
  LDR R0, =__main
  BX R0
 ENDP

從啟動(dòng)代碼我們可以看出,先是SystemInit,接著是__main,最后才是main。

回到時(shí)鐘問題上。如果我們想要使用某個(gè)外設(shè)的話,那么必須先配置好系統(tǒng)時(shí)鐘SYSCLK,接著在開啟外設(shè)時(shí)鐘。為了配置好系統(tǒng)時(shí)鐘SYSCLK,我們需要設(shè)置好一系列的時(shí)鐘來源,倍頻,分頻等參數(shù),這些工作都是由system_stm32f10x.c文件定義的SystemInit所完成。對(duì)于SystemInit函數(shù)來說,它的工作流程是這樣的:首先將與配置時(shí)鐘相關(guān)的寄存器都復(fù)位為默認(rèn)值,復(fù)位寄存器后,接著調(diào)用SetSysClock函數(shù),而對(duì)于SetSysClock來說,它又是根據(jù)條件編譯宏來調(diào)用最底層的設(shè)置系統(tǒng)時(shí)鐘的函數(shù),這里指的這些函數(shù)都已經(jīng)是相當(dāng)?shù)讓恿?,都是直接更寄存器打交道。下面我們可以看看SetSysClock函數(shù)。

static void SetSysClock(void){
  #ifdef SYSCLK_FREQ_HSE
    SetSysClockToHSE();
  #elif defined SYSCLK_FREQ_24MHz
    SetSysClockTo24();
  #elif defined SYSCLK_FREQ_36MHz
    SetSysClockTo36();
  #elif defined SYSCLK_FREQ_48MHz
    SetSysClockTo48();
  #elif defined SYSCLK_FREQ_56MHz
    SetSysClockTo56();  
  #elif defined SYSCLK_FREQ_72MHz
    SetSysClockTo72();
  #endif
}

接著看看ST庫定義的條件編譯宏,根據(jù)這些宏我們可以看出對(duì)于我們的stm32f103ze開發(fā)板來說,它的系統(tǒng)時(shí)鐘SYSCLK默認(rèn)是72mhz的。

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
 #define SYSCLK_FREQ_24MHz  24000000
#else
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz  24000000 */ 
/* #define SYSCLK_FREQ_36MHz  36000000 */
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
#define SYSCLK_FREQ_72MHz  72000000
#endif

重要的事情多說一遍,對(duì)于我們的STM32F10XZE開發(fā)板來說,它的系統(tǒng)時(shí)鐘SYSCLK默認(rèn)是72mhz。

言歸正傳,既然配置系統(tǒng)時(shí)鐘的問題已經(jīng)讓system_stm32f10x.c文件定義的SystemInit函數(shù)給完成了,那么我們接下來要做的工作就是開啟外設(shè)時(shí)鐘,對(duì)于外設(shè)時(shí)鐘來說,我們知道外設(shè)掛載在兩條總線上,分別為APB1低速外設(shè)總線以及APB2高速外設(shè)總線。歪個(gè)題,在開發(fā)板中有AHB總線,APB1,APB2。其中高速外設(shè)總線APB2使用的時(shí)鐘是PCLK2時(shí)鐘,它的默認(rèn)值就是SYSCLK也就是說掛載在高速外設(shè)總線的外設(shè)使用的時(shí)鐘頻率都是72mhz。對(duì)于低速外設(shè)總線APB1來說,它所使用的時(shí)鐘就是PCLK1時(shí)鐘。對(duì)于APB1外設(shè)以及APB2外設(shè)來說,開啟外設(shè)時(shí)鐘所使用的庫函數(shù)是不同的,分別為RCC_APB1PeriphClockCmd()以及RCC_APB2PeriphClockCmd()。

一個(gè)問題為什么在配置好了系統(tǒng)時(shí)鐘SYSCLK之后還得費(fèi)力開啟外設(shè)時(shí)鐘?答案就是為了減少功耗。

關(guān)于開啟時(shí)鐘需要注意的問題,如果我們使用了IO引腳的復(fù)用功能的話,那么我們還得開啟復(fù)用功能對(duì)應(yīng)外設(shè)的時(shí)鐘。比如我們?nèi)绻肜肎PIOX的某個(gè)引腳為ADC采集引腳的話,那么就得像下面這樣做:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOX, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADCX, ENABLE);

關(guān)于控制I/O引腳輸出高低電平的問題,很簡單就是通過控制GPIOX_BSRR寄存器上相應(yīng)的值,前16位是set,后16位是reset。對(duì)于ST庫來說,它提供了GPIO_SetBits(GPIOX,GPIO_Pin_x)來set以及GPIO_ResetBits(GPIOX,GPIO_Pin_x)來reset。


c語言編寫頭文件小技巧,形如下面這樣:

#ifndef __LED_H
#define __LED_H
...
#endif

為什么需要這樣做呢?答案是為了防止頭文件重復(fù)包含。為什么還要兩條下劃線?也沒什么,因?yàn)楹苌儆羞@樣命名變量的習(xí)慣,因此這樣做可以避免重名。


END

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容