open這個(gè)系統(tǒng)調(diào)用會建立一條到文件或者設(shè)備的訪問路徑,如果open調(diào)用成功的話汁讼,那么它將返回一個(gè)可以被read系統(tǒng)調(diào)用吃既,write系統(tǒng)調(diào)用等方法使用的文件描述符。文件描述符是唯一的双霍,他不會與任何其它運(yùn)行中的進(jìn)程共享。如果兩個(gè)進(jìn)程同時(shí)打開一個(gè)文件的話批销,那么它們將會得到兩個(gè)不同的文件描述符洒闸,如果這兩個(gè)進(jìn)程都對文件進(jìn)行寫操作的話,那么它們會各寫各的均芽,這就造成了一個(gè)問題丘逸,誰也搞不清哪部分?jǐn)?shù)據(jù)將會被覆蓋。對此掀宋,我們要利用文件鎖來防止這種問題的出現(xiàn)深纲。
當(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ù)后是趴,最終才會執(zhí)行用戶文件里面所定義的main函數(shù)涛舍。
至于為什么會是這樣澄惊,我們可以看看芯片的啟動文件唆途。對于我們的STM32芯片來說富雅,ST已經(jīng)提供了啟動文件startup_stm32f10x_hd.s啟動文件,我們截取相關(guān)的部分代碼如下:
Reset_Handler PROC
EXPORT Reset_Handler
IMPORT __main ;從外部導(dǎo)入__main方法
IMPORT SystemInit ;同理
LDR R0, = SystemInit ;方法地址存儲在了r0寄存器
BLX R0;跳轉(zhuǎn)執(zhí)行SystemInit
LDR R0, =__main
BX R0
ENDP
從啟動代碼我們可以看出肛搬,先是SystemInit没佑,接著是__main,最后才是main温赔。
回到時(shí)鐘問題上蛤奢。如果我們想要使用某個(gè)外設(shè)的話,那么必須先配置好系統(tǒng)時(shí)鐘SYSCLK陶贼,接著在開啟外設(shè)時(shí)鐘啤贩。為了配置好系統(tǒng)時(shí)鐘SYSCLK,我們需要設(shè)置好一系列的時(shí)鐘來源拜秧,倍頻痹屹,分頻等參數(shù),這些工作都是由system_stm32f10x.c文件定義的SystemInit所完成枉氮。對于SystemInit函數(shù)來說志衍,它的工作流程是這樣的:首先將與配置時(shí)鐘相關(guān)的寄存器都復(fù)位為默認(rèn)值,復(fù)位寄存器后聊替,接著調(diào)用SetSysClock函數(shù)楼肪,而對于SetSysClock來說,它又是根據(jù)條件編譯宏來調(diào)用最底層的設(shè)置系統(tǒng)時(shí)鐘的函數(shù)惹悄,這里指的這些函數(shù)都已經(jīng)是相當(dāng)?shù)讓恿舜航校际侵苯痈拇嫫鞔蚪坏馈O旅嫖覀兛梢钥纯碨etSysClock函數(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ù)這些宏我們可以看出對于我們的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
重要的事情多說一遍央星,對于我們的STM32F10XZE開發(fā)板來說,它的系統(tǒng)時(shí)鐘SYSCLK默認(rèn)是72mhz惫东。
言歸正傳莉给,既然配置系統(tǒng)時(shí)鐘的問題已經(jīng)讓system_stm32f10x.c文件定義的SystemInit函數(shù)給完成了,那么我們接下來要做的工作就是開啟外設(shè)時(shí)鐘廉沮,對于外設(shè)時(shí)鐘來說颓遏,我們知道外設(shè)掛載在兩條總線上,分別為APB1低速外設(shè)總線以及APB2高速外設(shè)總線滞时。歪個(gè)題叁幢,在開發(fā)板中有AHB總線,APB1,APB2。其中高速外設(shè)總線APB2使用的時(shí)鐘是PCLK2時(shí)鐘坪稽,它的默認(rèn)值就是SYSCLK也就是說掛載在高速外設(shè)總線的外設(shè)使用的時(shí)鐘頻率都是72mhz曼玩。對于低速外設(shè)總線APB1來說鳞骤,它所使用的時(shí)鐘就是PCLK1時(shí)鐘。對于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ù)用功能對應(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吊说。對于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í)慣,因此這樣做可以避免重名眉抬。