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