了解一下SystemInit()函數(shù)
這個(gè)函數(shù)是我們進(jìn)行系統(tǒng)初始化時(shí)會(huì)使用到的一個(gè)函數(shù)。它出現(xiàn)在main函數(shù)的第一行??梢?jiàn)其重要性。
庫(kù)函數(shù)的工程初始化過(guò)程中調(diào)用的函數(shù)及其順序?yàn)椋簊tartup_stm32f10x_hd.s->SystemInit()->SetSysClock()->SetSysClockTo72()
那么SystemInit()函數(shù)進(jìn)行了什么操作呢?——答案是初始化內(nèi)部Flash以及系統(tǒng)時(shí)鐘。在我們默認(rèn)使用該函數(shù)時(shí)系統(tǒng)時(shí)鐘被定為72MHz。系統(tǒng)時(shí)鐘是整個(gè)單片機(jī)時(shí)鐘的核心,各種外設(shè)的時(shí)鐘都依托這個(gè)系統(tǒng)時(shí)鐘,我們可以通過(guò)一張圖看到系統(tǒng)時(shí)鐘的來(lái)源和去向:

系統(tǒng)時(shí)鐘的來(lái)源為外部晶振,一個(gè)是頻率為8MHz的橢圓形晶振,還有一個(gè)頻率為12MHz的小圓柱形晶振,還有內(nèi)部的兩個(gè)震蕩源。我們可以看到?jīng)Q定系統(tǒng)時(shí)鐘的幾個(gè)時(shí)鐘都連接到PLL,左半邊是各個(gè)時(shí)鐘源經(jīng)過(guò)各種分頻之后連接到PLL,右邊是各種外設(shè)時(shí)鐘使用不同的分頻比例。其中AHB有1-512等多種分頻方式(2的倍數(shù)),APB1頻率是系統(tǒng)時(shí)鐘的一半,APB2頻率是系統(tǒng)時(shí)鐘。
平時(shí)使用時(shí)只需要了解這個(gè)時(shí)鐘的頻率,我們通常情況下會(huì)直接調(diào)用該函數(shù),將系統(tǒng)時(shí)鐘頻率設(shè)置成72MHz,APB1的頻率為36MHz,需要修改工作頻率的時(shí)候,可以通過(guò)使用HSE和HSI來(lái)配置系統(tǒng)時(shí)鐘,相關(guān)代碼可見(jiàn)野火的書(shū)P143。
32的位帶操作
32有一個(gè)很特別的東西——位帶操作。我們知道在51的使用中,關(guān)于某個(gè)特定端口的輸出輸入我們有時(shí)會(huì)這么寫(xiě):用P0.7專門(mén)指定一個(gè)引腳,輸出高就setb,輸出低就clr。這種使用方法在32里就不通用了,我們?cè)贗O操作那一節(jié)就可以看到,我們想要指定一個(gè)引腳,必須設(shè)置好一個(gè)完整的GPIO的結(jié)構(gòu)體指明引腳號(hào)啊輸入還是輸出啊之類的相關(guān)信息,非常繁瑣。想要更方便操作,可以使用32的位帶操作。
位帶操作可以簡(jiǎn)單理解成擴(kuò)展位或者膨脹
我們知道,單片機(jī)的一切外設(shè)都由在它片內(nèi)的某個(gè)特殊寄存器設(shè)置及控制,想要控制某個(gè)外設(shè)只需要將相關(guān)的寄存器配置好就可以了。32提供了大小為1M的外設(shè)位帶區(qū),包含了片上外設(shè)的全部寄存器,全部寄存器都可以通過(guò)訪問(wèn)位帶別名區(qū)的方式來(lái)達(dá)到訪問(wèn)原始寄存器的效果(膨脹后映射等效直接連接,大大降低代碼的繁瑣程度)。
通過(guò)這一原理我們可以將“位帶地址+位序號(hào)”轉(zhuǎn)換成別名地址定義成一個(gè)宏。
// 把一個(gè)地址轉(zhuǎn)換成一個(gè)指針 #define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
// 把位帶別名區(qū)地址轉(zhuǎn)換成指針 #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
GPIO位帶操作,實(shí)現(xiàn)兩個(gè)寄存器的地址映射后,
1 // GPIO ODR 和 IDR 寄存器地址映射 2 #define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C 3 #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C 4 #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C 5 #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C 6 #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C 7 #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C 8 #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C 9 10 #define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 11 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 12 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 13 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 14 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 15 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 16 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
就可以通過(guò)位操作的方法來(lái)控制GPIO的輸入和輸出了,宏參數(shù)n表示某個(gè)IO端口。
在通過(guò)宏定義置位函數(shù),可以將輸入輸出變成更加簡(jiǎn)單易懂的寫(xiě)法。
1 // 單獨(dú)操作 GPIO 的某一個(gè)IO 口,n(0,1,2...16),n 表示具體是哪一個(gè)IO 口 2 #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //輸出 3 #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //輸入 4 5 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //輸出 6 #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //輸入 7 8 #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //輸出 9 #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //輸入 10 11 #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //輸出 12 #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //輸入 13 14 #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //輸出 15 #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //輸入 16 17 #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //輸出 18 #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //輸入 19 20 #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //輸出 21 #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //輸入
位帶操作是一個(gè)很有意義的操作,它大大降低了代碼編寫(xiě)的難度,使得原先冗長(zhǎng)的代碼塊可以用更加簡(jiǎn)單易懂的方式替代,使用方法也很簡(jiǎn)單,在寫(xiě)好頭文件后添加即可,頭文件有現(xiàn)成的代碼集我們這里可以直接用,之后的代碼可能都會(huì)用位帶操作來(lái)減少工作量。