版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領
文檔簡介
第第頁STM32單片機到底是如何實現(xiàn)軟硬件結(jié)合?本文分析(STM32)(單片機)到底是如何實現(xiàn)軟(硬件)結(jié)合的,接著分析單片機程序如何編譯、運行。
軟硬件結(jié)合
初學者,通常有一個困惑,就是為什么軟件能控制硬件?就像當年的(51單片機),為什么只要寫P1=0X55,就可以在IO口輸出高低電平?要理清這個問題,先要認識一個概念:地址空間。
尋址空間
什么是地址空間呢?所謂的地址空間,就是PC指針的尋址范圍,因此也叫尋址空間。
大家應該都知道,我們的(電腦)有32位系統(tǒng)和64位系統(tǒng)之分,為什么呢?因為32位系統(tǒng),PC指針就是一個32位的二進制數(shù),也就是0xffffffff,范圍只有(4G)尋址空間。現(xiàn)在內(nèi)存越來越大,4G根本不夠,所以需要擴展,為了能訪問超出4G范圍的內(nèi)存,就有了64位系統(tǒng)。STM32是多少位的?是32位的,因此PC指針也是32位,尋址空間也就是4G。
我們來看看STM32的尋址空間是怎么樣的。在數(shù)據(jù)手冊《STM32F407_數(shù)據(jù)手冊.pdf》中有一個圖,這個圖,就是STM32的尋址空間分配。所有的(芯片),都會有這個圖,名字基本上都是叫Memorymap,用一個新芯片,就先看這個圖。
最左邊,8個block,每個block512M,總共就是4G,也就是芯片的尋址空間。
block0里面有一段叫做FLASH,也就是內(nèi)部FLASH,我們的程序就是(下載)到這個地方,起始地址是0X8000000,大家注意,這個只有1M空間?,F(xiàn)在STM32已經(jīng)有2Mflash的芯片了,超出1M的FLASH放在哪里呢?請自行查看對應的芯片手冊。
在block1內(nèi),有兩段S(RAM),總共128K,這個空間,也就是我們前面說的內(nèi)存,存放程序使用的變量。如果需要,也可以把程序放到SRAM中運行。407不是有196K嗎?
其實407有196K內(nèi)存,但是有64k并不是普通的SRAM,而是放在block0內(nèi)的CCM。
這兩段區(qū)域不連續(xù),而且,CCM只能內(nèi)核使用,外設不能使用,例如(DMA)就不能用CCM內(nèi)存,否則就死機。
block2,是Peripherals,也就是外設空間。我們看右邊,主要就是APB1/APB2、AHB1/AHB2,什么東西呢?回頭再說。
block3、block4、block5,是FSMC的空間,F(xiàn)SMC可以外擴SRAM,NANDFALSH,LCD等外設。
好的,我們分析了尋址空間,我們回過頭看看,軟件是如何控制硬件的。對于這個疑惑,也可以看此文:代碼是如何控制硬件的?在IO口輸出的例程中,我們配置IO口是調(diào)用庫函數(shù),我們看看庫函數(shù)是怎么做的。例如:
GPIO_SetBits(GPIOG,GPIO_(Pi)n_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);這個函數(shù)其實就是對一個變量賦值,對GPIOx這個結(jié)構(gòu)體的成員BSRRL賦值。voidGPIO_SetBits(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin){/*Checktheparamete(rs)*/assert_param(IS_GPIO_ALL_PERIPH(GPIOx));assert_param(IS_GPIO_PIN(GPIO_Pin));GPIOx->BSRRL=GPIO_Pin;}
assert_param:這個是斷言,用于判斷輸入?yún)?shù)是否符合要求,GPIOx是一個輸入?yún)?shù),是一個GPIO_TypeDef結(jié)構(gòu)體指針,所以,要用->獲取其成員。
GPIOx是我們傳入的參數(shù)GPIOG,具體是啥?在stm32f4xx.h中有定義。
#defineGPIOG((GPIO_TypeDef*)GPIOG_BASE)GPIOG_BASE同樣在文件中有定義,如下:#defineGPIOG_BASE(AHB1PERIPH_BASE+0x1800)AHB1PERIPH_BASE,AHB1地址,有點眉目了吧?在進一步看看/*!再找找PERIPH_BASE的定義#definePERIPH_BASE((uint32_t)0x40000000)到這里,我們可以看出,操作IO口G,其實就是操作0X40000000+0X1800這個地址上的一個結(jié)構(gòu)體里面的成員。說白了,就是操作了這個地方的(寄存器)。實質(zhì)上跟我們操作普通變量一樣,就像下面的兩句代碼,區(qū)別就是變量i是SRAM空間地址,0X40000000+0X1800是外設空間地址。u32i;i=0x55aa55aa;這個外設空間地址的寄存器是IO口硬件的一部分。如下圖,左邊的輸出數(shù)據(jù)寄存器,就是我們操作的寄存器(內(nèi)存、變量),它的地址就是0X40000000+0X1800+0x14。
控制其他外設也類似,就是將數(shù)據(jù)寫到外設寄存器上,跟操作內(nèi)存一樣,就可控制外設了。
寄存器,其實應該是內(nèi)存的統(tǒng)稱,外設寄存器應該叫做特殊寄存器。慢慢的,所有人都把外設的叫做寄存器,其他的統(tǒng)稱內(nèi)存或RAM。寄存器為什么能控制硬件外設呢?因為,初略的說,一個寄存器的一個BIT,就是一個開關(guān),開就是1,關(guān)就是0。通過這個(電子)開關(guān)去(控制電路),從而控制外設硬件。
純軟件-包羅萬象的小程序
我們已經(jīng)完成了串口和IO口的控制,但是我們僅僅知道了怎么用,對其他一無所知。程序怎么跑的?代碼到底放在那里?內(nèi)存又是怎么保存的?下面,我們通過一個簡單的程序,學習(嵌入式軟件)的基本要素。
分析啟動代碼
函數(shù)從哪里開始運行?
每個芯片都有復位功能,復位后,芯片的PC指針(一個寄存器,指示程序運行位置,對于多級流水線的芯片,PC可能跟真正執(zhí)行的指令位置不一致,這里暫且認為一致)會復位到固定值,一般是0x00000000,在STM32中,復位到0X08000004。因此復位后運行的第一條代碼就是0X08000004。前面我們不是拷貝了一個啟動代碼文件到工程嗎?
startup_stm32f40_41xxx.s,這個(匯編)文件為什么叫啟動代碼?因為里面的匯編程序,就是復位之后執(zhí)行的程序。在文件中,有一段數(shù)據(jù)表,稱為中斷向量,里面保存了各個中斷的執(zhí)行地址。復位,也是一個中斷。
芯片復位時,芯片從中斷表中將Reset_Handler這個值(函數(shù)指針)加載到PC指針,芯片就會執(zhí)行Reset_Handler函數(shù)了。(一個函數(shù)入口就是一個指針)
;VectorTableMappedto(Ad)dress0atResetAREARESET,DATA,READONLYEXPORT__VectorsEXPORT__Vectors_EndEXPORT__Vectors_Size__Vectors
(DC)D
__ini(ti)al_sp
;
Top
of
Stack
DCD
Reset_Handler
;
Reset
Handler
DCD
NMI_Handler
;
NMI
Handler
DCD
HardFault_Handler
;
Hard
Fault
Handler
DCD
MemManage_Handler
;
MPU
Fault
Handler
DCD
BusFault_Handler
;
Bus
Fault
Handler
DCD
UsageFault_Handler
;
Usage
Fault
HandlerReset_Handler函數(shù),先執(zhí)行SystemInit函數(shù),這個函數(shù)在標準庫內(nèi),主要是初始芯片時鐘。然后跳到__main執(zhí)行,__main函數(shù)是什么函數(shù)?
是我們在main.c中定義的main函數(shù)嗎?后面我們再說這個問題。
;ResethandlerReset_HandlerPROCEXPORTReset_Handler[WEAK]IMPORTSystemInitIMPORT__mainLDRR0,=SystemInitBLXR0LDRR0,=__mainBXR0ENDP
芯片是怎么知道開始就執(zhí)行啟動代碼的呢?或者說,我們?nèi)绾伟堰@個啟動代碼放到復位的位置?這就牽涉到一個一般情況下不關(guān)注的文件wujique.sct,這個文件在wujiqueprjObjects目錄下,通常把這個文件叫做分散加載文件,編譯工具在鏈接時,根據(jù)這個文件放置各個代碼段和變量。
在MDK軟件Options菜單Linker下有關(guān)于這個菜單的設置。
把UseMemoryLayoutfromTarget(Dialog)前面的勾去掉,之前不可設置的框都可以設置了。點擊Edit進行編輯。
在代碼編輯框出現(xiàn)了分散加載文件內(nèi)容,當前文件只有基本的內(nèi)容。
其實這個文件功能很強大,通過修改這個文件可以配置程序的很多功能,例如:1指定FLASH跟RAM的大小于起始位置,當我們把程序分成BOOT、CORE、APP,甚至進行驅(qū)動分離的時候,就可以用上了。2指定函數(shù)與變量的位置,例如把函數(shù)加載到RAM中運行。
從這個基本的分散加載文件我們可以看出:
第6行ER_I(ROM)10x080000000x00080000定義了ER_IROM1,也就是我們說的內(nèi)部FLASH,從0x08000000開始,大小0x00080000。
第7行.o(RESET,+First)從0x08000000開始,先放置一個.o文件,并且用(RESET,+First)指定RESET塊優(yōu)先放置,RESET塊是什么?請查看啟動代碼,中斷向量就是一個AREA,名字叫RESET,屬于READONLY。這樣編譯后,RESET塊將放在0x08000000位置,也就是說,中斷向量就放在這個地方。
DCD是分配空間,4字節(jié),第一個就是__initial_sp,第二個就是Reset_Handler函數(shù)指針。也就是說,最后編譯后的程序,將Reset_Handler這個函數(shù)的指針(地址),放在0x800000+4的地方。所以芯片在復位的時候,就能找到復位函數(shù)Reset_Handler。
第8行*(InRoot$$Sections)什么鬼?GOOGLE?。』仡^再說。
第9行.ANY(+RO)意思就是其他的所有RO,順序往后放。就是說,其他代碼,跟著啟動代碼后面。
第11行RW_IRAM10x200000000x00020000定義了RAM大小。
第12行.ANY(+RW+ZI)所有的RWZI,全部放到RAM里面。RW,ZI,也就是變量,這一行指定了變量保存到什么地址。
分析用戶代碼
到此,基本啟動過程已經(jīng)分析完。下一步開始分析用戶代碼,就從main函數(shù)開始。(1)程序跳轉(zhuǎn)到main函數(shù)后,RCC_GetClocksFreq獲取RCC(時鐘)頻率;SysTick_Config配置SysTick,在這里打開了SysTick中斷,10毫秒一次。Delay(5);延時50毫秒。
intmain(void){GPIO_InitTypeDefGPIO_InitStructure;/*!(2)初始化IO就不說了,進入while(1),也就是一個死循環(huán),(嵌入式)程序,都是一個死循環(huán),否則就跑飛了。/*初始化LEDIO口*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;GPIO_Init(GPIOG,/*Infini(te)loop*/(mcu)_(uart)_open(3);while(1){GPIO_ResetBits(GPIOG,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);Delay(100);GPIO_SetBits(GPIOG,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);Delay(100);mcu_uart_test();TestFun(TestTmp2);}(3)在while(1)中調(diào)用TestFun函數(shù),這個函數(shù)使用兩個全局變量,兩個局部變量。/*Privatefunctions*/u32TestTmp1=5;//全局變量,初始化為5u32TestTmp2;//全局變量,未初始化constu32TestTmp3[10]={6,7,8,9,10,11,12,13,12,13};u8TestFun(u32x)//函數(shù),帶一個參數(shù),并返回一個u8值{u8test_tmp1=4;//局部變量,初始化u8test_tmp2;//局部變量,未初始化staticu8test_tmp3=0;//靜態(tài)局部變量test_tmp3++;test_tmp2=x;if(test_tmp2>TestTmp1)test_tmp1=10;elsetest_tmp1=5;TestTmp2+=TestTmp3[test_tmp1];returntest_tmp1;}然后程序就一直在main函數(shù)的while循環(huán)里面執(zhí)行。中斷呢?對,還有中斷。中斷中斷,就是中斷正常的程序執(zhí)行流程。我們查看Delay函數(shù),uwTimingDelay不等于0就死等?誰會將uwTimingDelay改為0?/***@briefInsertsadelaytime.*@paramnTime:specifiesthedelaytimelength,inmilliseconds.*@retvalNone*/voidDelay(__IOuint32_tnTime){uwTimingDelay=nTime;while(uwTimingDelay!=0);}搜索uwTimingDelay變量,函數(shù)TimingDelay_Decrement會將變量一直減到0。/***@briefDecrementstheTimingDelayvariable.*@paramNone*@retvalNone*/voidTimingDelay_Decrement(void){if(uwTimingDelay!=0x00){uwTimingDelay--;}}這個函數(shù)在哪里執(zhí)行?經(jīng)查找,在SysTick_Handler函數(shù)中運行。誰用這個函數(shù)?/***@briefThisfunctionhandlesSysTickHandler.*@paramNone*@retvalNone*/voidSysTick_Handler(void){TimingDelay_Decrement();}經(jīng)查找,在中斷向量表中有這個函數(shù),也即是說這個函數(shù)指針保存在中斷向量表內(nèi)。當發(fā)生中斷時,就會執(zhí)行這個函數(shù)。當然,在進出中斷會有保存和恢復現(xiàn)場的操作。這個主要涉及到匯編,暫時不進行分析了。有興趣自己研究研究。通常,現(xiàn)在我們開發(fā)程序不用關(guān)心上下文切換了。__Vectors
DCD
__initial_sp
;
Top
of
Stack
DCD
Reset_Handler
;
Reset
Handler
DCD
NMI_Handler
;
NMI
Handler
DCD
HardFault_Handler
;
Hard
Fault
Handler
DCD
MemManage_Handler
;
MPU
Fault
Handler
DCD
BusFault_Handler
;
Bus
Fault
Handler
DCD
UsageFault_Handler
;
Usage
Fault
Handler
DCD
0
;
Reserved
DCD
0
;
Reserved
DCD
0
;
Reserved
DCD
0
;
Reserved
DCD
SVC_Handler
;
SVCall
Handler
DCD
DebugMon_Handler
;
Debug
Monitor
Handler
DCD
0
;
Reserved
DCD
PendSV_Handler
;
PendSV
Handler
DCD
SysTick_Handler
;
SysTick
Handler余下問題1__main函數(shù)是什么函數(shù)?是我們在main.c中定義的main函數(shù)嗎?2分散加載文件中*(InRoot$$Sections)是什么?3ZI段,也就是初始化為0的數(shù)據(jù)段,什么時候初始化?誰初始化?為什么這幾個問題前面留著不說?因為這是同一個問題。順藤摸瓜!
通過MAP文件了解代碼構(gòu)成
編譯結(jié)果
程序編譯后,在下方的BuildOutput窗口會輸出信息:
***UsingCompiler'V5.06update5(build528)',folder:'C:(Keil)_v5(ARM)ARMCCBin'Buildtarget'wujique'compilingstm32f4xx_it.cassemblingstartup_stm32f40_41pilingpilingmcu_uart.c...linking...ProgramSize:Code=9038RO-data=990RW-data=40ZI-data=6000FromELF:creatinghexfile...".Objectswujique.axf"-0Error(s),0Warning(s).BuildTimeElapsed:0032
編譯目標是wujique
C文件compiling,匯編文件assembling,這個過程叫編譯
編譯結(jié)束后,就進行l(wèi)ink,鏈接。
最后得到一個編譯結(jié)果,9038字節(jié)code,RO990,RW40,ZI6000。CODE,是代碼,很好理解,那RO、RW、ZI都是什么?
FromELF,創(chuàng)建hex文件,F(xiàn)romELF是一個好工具,需要自己添加到option中才能用
map文件配置
更多編譯具體信息在map文件中,在MDKOptions中我們可以看到,所有信息都放在Listingswujique.map
默認很多編譯信息可能沒鉤,鉤上所有信息會增加編譯時間。
map文件
打開map文件,好亂?習慣就好。我們抓重點就行了。
map總信息
從最后看起,看到?jīng)]?最后的這一段map內(nèi)容,說明了整個程序的基本概況。有多少RO?RO到底是什么?有多少RW?RW又是什么?
ROM為什么不包括ZIData?為什么包含RWData?
Imagecomponentsizes
往上,看看Imagecomponentsizes,這個就比剛剛的總體統(tǒng)計更細了。這部分內(nèi)容,說明了每個源文件的概況
首先,是我們自己的源碼,這個程序我們的代碼不多,只有main.o,wujique_log.o,和其他一些STM32的庫文件。
第2部分是庫里面的文件,看到?jīng)]?里面有一個main.o。main函數(shù)是不是我們寫的main函數(shù)?明顯不是,我們的main函數(shù)是放在main.o文件。這么小的一個工程,用了這么多庫,你以前關(guān)注過嗎?估計沒有,除非你曾經(jīng)將一個原本在1Mflash上的程序壓縮到能在512K上運行。
第3部分也是庫,暫時沒去分析這兩個是什么東西。
庫文件是什么?庫文件就是別人已經(jīng)別寫好的代碼庫。在代碼中,我們經(jīng)常會包含一些頭文件,例如:
#include#include#include
這些就是庫的頭文件。這些頭文件保存在MDK開發(fā)工具的安裝目錄下。我們經(jīng)常用的庫函數(shù)有:memcpy、memcmp、strcmp等。只要代碼中包含了這些函數(shù),就會鏈接庫文件。
文件map
再往上,就是文件MAP了,也就時每個文件中的代碼段(函數(shù))跟變量在ROM跟RAM中的位置。首先是ROM在0x08000000確實放的是startup_stm32f40_41xxx.o中的RESET。
庫文件是什么?
庫文件就是別人已經(jīng)別寫好的代碼庫。
在代碼中,我們經(jīng)常會包含一些頭文件,例如:
#include#include#include這些就是庫的頭文件。這些頭文件保存在MDK開發(fā)工具的安裝目錄下。我們經(jīng)常用的庫函數(shù)有:memcpy、memcmp、strcmp等。只要代碼中包含了這些函數(shù),就會鏈接庫文件。
每個文件有有多行,例如串口,4個函數(shù)。
然后是RAM的,main.o中的變量,放在0x20000000,總共有0x0000000c,類型是Data、RW。串口有兩種變量,data和bss,什么是bss?這兩個名稱,是sectionname,也就是段的意思??辞懊鎡ype和Attr,RWData,放在.data段;RWZero放在.bss段,RWZero,其實就是ZI。到底哪些變量是RW,哪些是ZI?
ImageSymbolTable
再往上就是ImageSymbolTable,就更進一步到每個函數(shù)或者變量的信息了。
例如,全局變量TestTmp1,是Data,4字節(jié),分配的位置是0x20000004。
TestTmp3數(shù)組放在哪里?放在0X080024E0這個地方,這可是代碼區(qū)。因為我們用const修飾了這個全局變量數(shù)組,告訴編譯器,這個數(shù)組是不可以改變的,編譯器就將這個數(shù)組保存到代碼中了。程序中我們經(jīng)常會使用一些大數(shù)組數(shù)據(jù),例如字符點陣,通常有幾K、幾十K大,不可能也沒必要放到RAM區(qū),整個程序運行過程這些數(shù)據(jù)都不改變,因此通過const修飾,將其存放到代碼區(qū)。
const的用處比較多,可以修飾變量,也可以修飾函數(shù)。更多用法自行學習。
那局部變量存放在哪里呢?我們找到了test_tmp3。
沒找到test_tmp1/
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年醫(yī)用衛(wèi)生材料敷料合作協(xié)議書
- 2025年雷達車合作協(xié)議書
- 2025年國土資源普查核儀器合作協(xié)議書
- 人教版 八年級英語下冊 Unit 3 單元綜合測試卷(2025年春)
- 2025年氯磺化聚乙烯合作協(xié)議書
- 2025年九年級第二學期班主任德育工作總結(jié)(二篇)
- 2025年互聯(lián)網(wǎng)科技公司股東合作協(xié)議模板(2篇)
- 2025年產(chǎn)品配送委托合同(三篇)
- 2025年產(chǎn)品總代理合同參考模板(2篇)
- 2025年產(chǎn)品年度區(qū)域銷量合同(三篇)
- 《梅大高速茶陽路段“5·1”塌方災害調(diào)查評估報告》專題警示學習
- 2024年09月北京中信銀行北京分行社會招考(917)筆試歷年參考題庫附帶答案詳解
- 《大健康解讀》課件
- 2025年度交通運輸規(guī)劃外聘專家咨詢協(xié)議3篇
- 專項債券培訓課件
- 《會務的組織和管理》課件
- 2024年公司領導在新年動員會上的講話樣本(3篇)
- 給排水管道工程分項、分部、單位工程劃分
- 《傻子上學》臺詞
- 高中英語新課程標準解讀 (課堂PPT)
- 石灰石石膏濕法脫硫化學分析方案
評論
0/150
提交評論