楊 宇
(昆明冶金高等??茖W(xué)校電氣與機(jī)械學(xué)院,云南 昆明 650033)
隨著社會生產(chǎn)力的發(fā)展,在嵌入式控制領(lǐng)域,32位高性能單片機(jī)的應(yīng)用越來越廣泛。很多單片機(jī)開發(fā)者對于單片機(jī)的學(xué)習(xí)都是從8位低性能單片機(jī)開始的,8位低性能單片機(jī)和32位高性能單片機(jī)在很多方面存在差異,這使得許多想繼續(xù)學(xué)習(xí)32位高性能單片機(jī)的單片機(jī)開發(fā)者遇到了障礙。
8位低性能單片機(jī)的寄存器映射原理相對簡單,很多單片機(jī)開發(fā)者都能理解,但是他們對32位高性能單片機(jī)的寄存器映射原理往往理解不足,該文以這2種單片機(jī)的2種典型型號為例,即8位低性能單片機(jī)中的8051單片機(jī),32位高性能單片機(jī)中的STM32F103單片機(jī),將這2種單片機(jī)的寄存器映射原理進(jìn)行對比分析,筆者認(rèn)為這種比較式的闡述能幫助單片機(jī)開發(fā)者更好地理解32位高性能單片機(jī)的寄存器映射原理。正確理解寄存器映射的原理對于開發(fā)者進(jìn)行STM32單片機(jī)后續(xù)內(nèi)容的學(xué)習(xí)有極大的幫助。
單片機(jī)寄存器是具有特定功能的單片機(jī)內(nèi)部存儲器單元,所謂寄存器映射是指將每個(gè)寄存器單元的名稱和絕對地址對應(yīng)起來,或者說給每個(gè)寄存器單元取1個(gè)名字,以便在編程時(shí)直接使用寄存器名稱來訪問寄存器單元,這樣可以讓編程更加方便,程序更加易讀。我們首先了解2種單片機(jī)寄存器映射完成后的結(jié)構(gòu)圖(主要以編程時(shí)最常用的通用數(shù)據(jù)輸入輸出端口寄存器為例),再分析寄存器映射實(shí)現(xiàn)的原理。
8051單片機(jī)內(nèi)部存儲器結(jié)構(gòu)相對簡單,每個(gè)字節(jié)對應(yīng)1存儲單元,每個(gè)存儲單元有唯一的地址,總共有256個(gè)字節(jié),對應(yīng)地址00H到FFH,寄存器位于高128字節(jié),每個(gè)寄存器通常占1個(gè)字節(jié),并對應(yīng)不同的地址和名稱,見圖1,比如常用的4個(gè)通用數(shù)據(jù)輸入輸出端口P0到P3,每個(gè)端口對應(yīng)1個(gè)寄存器:名稱P0對應(yīng)地址為80H的寄存器,名稱P1對應(yīng)地址為90H的寄存器,名稱P2對應(yīng)地址為A0H的寄存器,名稱P3對應(yīng)地址為B0H的寄存器,編程時(shí)只需操作名稱P0、P1、P2、P3就可以訪問相關(guān)寄存器單元。STM32F103單片機(jī)內(nèi)部存儲器結(jié)構(gòu)比較復(fù)雜,存儲單元個(gè)數(shù)也比較多,總共包含4 294 967 296個(gè)字節(jié),每個(gè)字節(jié)對應(yīng)1個(gè)存儲單元,每個(gè)存儲單元有唯一的地址,對應(yīng)地址00000000H到FFFFFFFFH,見圖2,常用寄存器位于block2,地址范圍是40000000H到5FFFFFFFH,每個(gè)寄存器通常占4個(gè)字節(jié)(8 051單片機(jī)每個(gè)寄存器通常只占1個(gè)字節(jié)),block2中包含7個(gè)常用的通用數(shù)據(jù)輸入輸出口:PortA、PortB、PortC、PortD、PortE、PortF、PortG這7個(gè)名稱并不是每個(gè)端口的寄存器名稱,因?yàn)镾TM32F103單片機(jī)的每個(gè)通用數(shù)據(jù)輸入輸出口都包含7個(gè)寄存器(8 051單片機(jī)每個(gè)通用數(shù)據(jù)輸入輸出口只包含1個(gè)寄存器):端口配置低寄存器CRL、端口配置高寄存器CRH、數(shù)據(jù)輸入寄存器IDR、數(shù)據(jù)輸出寄存器ODR、位設(shè)置/清除寄存器BSRR、端口位清除寄存器BRR、端口配置鎖定寄存器LCKR,并且每個(gè)寄存器占4個(gè)字節(jié)的存儲空間,所以以B口(PortB)為例,它所包含的寄存器名稱為:GPIOB->CRL、GPIOB->CRH、GPIOB->IDR、GPIOB->ODR、GPIOB->BSRR、GPIOB->BRR、GPIOB->LCKR,以上寄存器名稱分別對應(yīng)的寄存器地址為:0x40010C00、0x40010C04、0x40010C08、0x40010C0C、0x40010C10、0x40010C14、0x40010C18,編程時(shí)通過操作寄存器名稱訪問相關(guān)寄存器單元。
圖1 8 051單片機(jī)寄存器映射Fig.1 Register mapping of MCU8 051
圖2 STM32F103單片機(jī)寄存器映射Fig.2 Register mapping of STM32F103
8 051單片機(jī)和STM32F103單片機(jī)都是通過頭文件中的程序來實(shí)現(xiàn)寄存器映射的,8 051單片機(jī)使用的頭文件是reg51.h,STM32F103單片機(jī)使用的頭文件是STM32f10x.h,reg51.h文件是單片機(jī)開發(fā)軟件自帶的,而STM32f10x.h文件需要用戶創(chuàng)建。正確理解頭文件中的相關(guān)程序就能正確理解單片機(jī)實(shí)現(xiàn)寄存器映射的原理,上述2種單片機(jī)的寄存器映射程序差別很大,下面將主要分析和比較2種單片機(jī)寄存器映射的實(shí)現(xiàn)代碼。
8 051單片機(jī)寄存器映射實(shí)現(xiàn)原理相對簡單,主要是通過C51編程語言中的關(guān)鍵字“SFR”和運(yùn)算符“=”將寄存器的名稱和寄存器的絕對地址聯(lián)系起來,如圖3所示,編程時(shí)給寄存器名稱賦值就可以操作寄存器。以操作通用數(shù)據(jù)輸入輸出端口P1為例:如圖3中的語句sfr P1=0x90,P1是8051單片機(jī)通用數(shù)據(jù)輸入輸出端口1的名稱,0x90是該寄存器在內(nèi)存中的絕對地址,可在圖1中找到。這樣在編寫應(yīng)用程序時(shí)就可以直接使用P1來操作該端口,比如:想讓8 051單片機(jī)P1端口的8個(gè)引腳都輸出高電平,使用語句P1=0xFF就可以實(shí)現(xiàn)。操作其他的寄存器也是同樣的道理。
圖3 8 051單片機(jī)寄存器映射實(shí)現(xiàn)原理Fig.3 Register mapping principle of MCU8 051
相較于8 051單片機(jī),STM32F103單片機(jī)由于存儲器結(jié)構(gòu)更復(fù)雜,存儲單元數(shù)量更多,并且程序中不能使用關(guān)鍵字SFR,所以STM32F103單片機(jī)寄存器映射實(shí)現(xiàn)原理比較復(fù)雜。還是以操作通用數(shù)據(jù)輸入輸出端口(GPIO)為例來闡述,由于STM32F103單片機(jī)的片內(nèi)外設(shè)結(jié)構(gòu)層級較多并且1個(gè)通用數(shù)據(jù)輸入輸出端口包含多個(gè)寄存器,所以映射實(shí)現(xiàn)代碼的思路是首先確定各個(gè)GPIO的基地址,然后將各個(gè)基地址轉(zhuǎn)換成一種合適的數(shù)據(jù)類型的指針。
(1)確定各個(gè)通用數(shù)據(jù)輸入輸出端口的基地址
STM32F103單片機(jī)的CPU是通過3種總線(APB1、APB2、AHB)來連接各種外圍設(shè)備的,通用數(shù)據(jù)輸入輸出端口連接在APB2總線上,1個(gè)通用數(shù)據(jù)輸入輸出端口包含多個(gè)寄存器。根據(jù)這種層級關(guān)系,某個(gè)通用數(shù)據(jù)輸入輸出端口的基地址是根據(jù)CPU的外設(shè)基地址(圖4中的PERIPH_BASE)、總線基地址(圖4中的APB2PERIPH_BASE)和偏移量(圖4中“+”號后的常量)來確定的。如圖4所示,用C語言關(guān)鍵字#define進(jìn)行宏定義,將宏GPIOA_BASE、GPIOB_BASE、GPIOC_BASE、GPIOD_BASE、GPIOE_BASE、GPIOF_BASE、GPIOG_BASE定義成7個(gè)通用數(shù)據(jù)輸入輸出端口的絕對基地址,這7個(gè)絕對基地址分別對應(yīng)圖2中PortA、PortB、PortC、PortD、PortE、PortF、PortG的首地址。
圖4 STM32F103單片機(jī)寄存器映射實(shí)現(xiàn)原理1Fig.4 Register mapping principle 1 of STM32F103
(2)把各個(gè)通用數(shù)據(jù)輸入輸出端口的基地址轉(zhuǎn)換成結(jié)構(gòu)體型指針
STM32F103單片機(jī)包含7個(gè)通用數(shù)據(jù)輸入輸出端口,每個(gè)通用數(shù)據(jù)輸入輸出端口包含多個(gè)寄存器,如果確定7個(gè)通用數(shù)據(jù)輸入輸出端口的基地址之后,再根據(jù)每個(gè)通用數(shù)據(jù)輸入輸出端口中的各個(gè)寄存器(端口配置低寄存器CRL、端口配置高寄存器CRH、數(shù)據(jù)輸入寄存器IDR、數(shù)據(jù)輸出寄存器ODR、位設(shè)置/清除寄存器BSRR、端口位清除寄存器BRR、端口配置鎖定寄存器LCKR)的偏移地址得到各個(gè)寄存器的絕對地址,再將這些絕對地址使用#define進(jìn)行宏定義,這種方法也可以實(shí)現(xiàn)各個(gè)寄存器的映射,但是重復(fù)性代碼量
過多,技術(shù)含量低,不是一種好方法。STM32F103單片1個(gè)通用數(shù)據(jù)輸入輸出端口包含多個(gè)寄存器這一特征可以使用C語言中的1種構(gòu)造數(shù)據(jù)類型—結(jié)構(gòu)體來表示,用結(jié)構(gòu)體的名稱代表某個(gè)通用數(shù)據(jù)輸入輸出端口,用結(jié)構(gòu)體的各個(gè)成員代表該通用數(shù)據(jù)輸入輸出端口的各個(gè)寄存器。圖5中的GPIO_TypeDef就是創(chuàng)建的1個(gè)結(jié)構(gòu)體,該結(jié)構(gòu)體的成員CRL代表端口配置低寄存器,成員CRH代表端口配置高寄存器,成員IDR代表數(shù)據(jù)輸入寄存器,成員ODR代表數(shù)據(jù)輸出寄存器,成員BSRR代表位設(shè)置/清除寄存器,成員BRR代表端口位清除寄存器,成員LCKR代表端口配置鎖定寄存器。將之前定義的各個(gè)通用數(shù)據(jù)輸入輸出端口基地址的宏強(qiáng)制轉(zhuǎn)換成GPIO_TypeDef型指針,最后用#define將宏GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF、GPIOG分別定義成7個(gè)通用數(shù)據(jù)輸入輸出端口的指向每個(gè)端口基地址的GPIO_TypeDef型指針,這樣就可以通過使用這些宏名稱加結(jié)構(gòu)體成員名稱來操作各個(gè)通用數(shù)據(jù)輸入輸出端口的寄存器,比如使用語句GPIOB->ODR=0xFFFF就可以讓通用數(shù)據(jù)輸入輸出端口B對應(yīng)的引腳輸出高電平。相比第一種方法,這種引入結(jié)構(gòu)體的方法更加高效合理。操作其他的寄存器也是同樣的道理,這里就不贅述。
圖5 STM32F103單片機(jī)寄存器映射實(shí)現(xiàn)原理2Fig.5 Register mapping principle 2 of STM32F103
8 051單片機(jī)和STM32F103單片機(jī)的寄存器映射原理既有相同點(diǎn)又有差異性,對于單片機(jī)開發(fā)者來說,將兩者結(jié)合起來對比學(xué)習(xí)比單純學(xué)習(xí)STM32F103單片機(jī)的寄存器映射原理更容易理解,采用基地址結(jié)合結(jié)構(gòu)體的方法能夠很好的實(shí)現(xiàn)STM32F103單片機(jī)的寄存器映射,一旦掌握了STM32單片機(jī)的寄存器映射原理,對于STM32單片機(jī)的寄存器編程和固件庫編程會有極大的幫助,能夠讓單片機(jī)開發(fā)者在編程時(shí)知其然并知其所以然。