0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

單片機(jī)中為什么軟件能控制硬件?

FPGA之家 ? 來源:嵌入式IOT技術(shù)圈 ? 作者:嵌入式IOT技術(shù)圈 ? 2021-04-04 12:26 ? 次閱讀

我們通過IO和串口的軟件開發(fā),已經(jīng)體驗(yàn)了嵌入式軟件開發(fā)。不知道大家有沒有疑惑,為什么軟件能控制硬件?反正當(dāng)年我學(xué)習(xí)51的時(shí)候,有這個(gè)疑惑。今天我們就暫停軟件開發(fā),分析單片機(jī)到底是如何軟硬件結(jié)合的。并通過一個(gè)基本的程序,分析單片機(jī)程序的編譯,運(yùn)行。

軟硬件結(jié)合

初學(xué)者,通常有一個(gè)困惑,就是為什么軟件能控制硬件?就像當(dāng)年的51,為什么只要寫P1=0X55,就可以在IO口輸出高低電平?要理清這個(gè)問題,先要認(rèn)識(shí)一個(gè)概念:地址空間。

尋址空間

什么是地址空間呢?所謂的地址空間,就是PC指針的尋址范圍,因此也叫尋址空間。

大家應(yīng)該都知道,我們的電腦有32位系統(tǒng)和64位系統(tǒng)之分,為什么呢?因?yàn)?2位系統(tǒng),PC指針就是一個(gè)32位的二進(jìn)制數(shù),也就是0xffffffff,范圍只有4G尋址空間?,F(xiàn)在內(nèi)存越來越大,4G根本不夠,所以需要擴(kuò)展,為了能訪問超出4G范圍的內(nèi)存,就有了64位系統(tǒng)。STM32是多少位的?是32位的,因此PC指針也是32位,尋址空間也就是4G。

我們來看看STM32的尋址空間是怎么樣的。在數(shù)據(jù)手冊(cè)《STM32F407_數(shù)據(jù)手冊(cè).pdf》中有一個(gè)圖,這個(gè)圖,就是STM32的尋址空間分配。所有的芯片,都會(huì)有這個(gè)圖,名字基本上都是叫Memory map,用一個(gè)新芯片,就先看這個(gè)圖。

7b900f48-8ecb-11eb-8b86-12bb97331649.png

最左邊,8個(gè)block,每個(gè)block 512M,總共就是4G,也就是芯片的尋址空間。

block 0 里面有一段叫做FLASH,也就是內(nèi)部FLASH,我們的程序就是下載到這個(gè)地方,起始地址是0X800 0000,大家注意,這個(gè)只有1M空間。現(xiàn)在STM32已經(jīng)有2M flash的芯片了,超出1M的FLASH放在哪里呢?請(qǐng)自行查看對(duì)應(yīng)的芯片手冊(cè)。

3 在block 1 內(nèi),有兩段SRAM,總共128K,這個(gè)空間,也就是我們前面說的內(nèi)存,存放程序使用的變量。如果需要,也可以把程序放到SRAM中運(yùn)行。407不是有196K嗎?

其實(shí)407有196K內(nèi)存,但是有64k并不是普通的SRAM,而是放在block 0 內(nèi)的CCM。這兩段區(qū)域不連續(xù),而且,CCM只能內(nèi)核使用,外設(shè)不能使用,例如DMA就不能用CCM內(nèi)存,否則就死機(jī)。

block 2,是Peripherals,也就是外設(shè)空間。我們看右邊,主要就是APB1/APB2、AHB1/AHB2,什么東西呢?回頭再說。

block 3、block4、block5,是FSMC的空間,F(xiàn)SMC可以外擴(kuò)SRAM,NAND FALSH,LCD等外設(shè)。好的,我們分析了尋址空間,我們回過頭看看,軟件是如何控制硬件的。在IO口輸出的例程中,我們配置IO口是調(diào)用庫函數(shù),我們看看庫函數(shù)是怎么做的。

例如:

GPIO_SetBits(GPIOG, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2| GPIO_Pin_3);

這個(gè)函數(shù)其實(shí)就是對(duì)一個(gè)變量賦值,對(duì)GPIOx這個(gè)結(jié)構(gòu)體的成員BSRRL賦值。

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

{

/* Check the parameters */

assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

assert_param(IS_GPIO_PIN(GPIO_Pin));

GPIOx-》BSRRL = GPIO_Pin;

}

assert_param:這個(gè)是斷言,用于判斷輸入參數(shù)是否符合要求GPIOx是一個(gè)輸入?yún)?shù),是一個(gè)GPIO_TypeDef結(jié)構(gòu)體指針,所以,要用-》獲取其成員

GPIOx是我們傳入的參數(shù)GPIOG,具體是啥?在stm32f4xx.h中有定義。

#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)

GPIOG_BASE同樣在文件中有定義,如下:

#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800)

AHB1PERIPH_BASE,AHB1地址,有點(diǎn)眉目了吧?在進(jìn)一步看看

/*!《 Peripheral memory map */

#define APB1PERIPH_BASE PERIPH_BASE

#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)

#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000)

#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000)

再找找PERIPH_BASE的定義

#define PERIPH_BASE ((uint32_t)0x40000000)

到這里,我們可以看出,操作IO口G,其實(shí)就是操作0X40000000+0X1800這個(gè)地址上的一個(gè)結(jié)構(gòu)體里面的成員。說白了,就是操作了這個(gè)地方的寄存器。實(shí)質(zhì)跟我們操作普通變量一樣,就像下面的兩句代碼,區(qū)別就是變量i是SRAM空間地址,0X40000000+0X1800是外設(shè)空間地址。

u32 i;

i = 0x55aa55aa;

這個(gè)外設(shè)空間地址的寄存器是IO口硬件的一部分。如下圖,左邊的輸出數(shù)據(jù)寄存器,就是我們操作的寄存器(內(nèi)存、變量),它的地址就是0X40000000+0X1800+0x14.

7bd36cd4-8ecb-11eb-8b86-12bb97331649.png

控制其他外設(shè)也類似,就是將數(shù)據(jù)寫到外設(shè)寄存器上,跟操作內(nèi)存一樣,就可控制外設(shè)了。

寄存器,其實(shí)應(yīng)該是內(nèi)存的統(tǒng)稱,外設(shè)寄存器應(yīng)該叫做特殊寄存器。慢慢的,所有人都把外設(shè)的叫做寄存器,其他的統(tǒng)稱內(nèi)存或RAM。寄存器為什么能控制硬件外設(shè)呢?因?yàn)椋致缘恼f,一個(gè)寄存器的一個(gè)BIT,就是一個(gè)開關(guān),開就是1,關(guān)就是0。通過這個(gè)電子開關(guān)去控制電路,從而控制外設(shè)硬件。

純軟件-包羅萬象的小程序

我們已經(jīng)完成了串口和IO口的控制,但是我們僅僅知道了怎么用,對(duì)其他一無所知。程序怎么跑的?代碼到底放在哪里??jī)?nèi)存又是怎么保存的?下面,我們通過一個(gè)簡(jiǎn)單的程序,學(xué)習(xí)嵌入式軟件的基本要素。

分析啟動(dòng)代碼

函數(shù)從哪里開始運(yùn)行?每個(gè)芯片都有復(fù)位功能,復(fù)位后,芯片的PC指針(一個(gè)寄存器,指示程序運(yùn)行位置,對(duì)于多級(jí)流水線的芯片,PC可能跟真正執(zhí)行的指令位置不一致,這里暫且認(rèn)為一致)會(huì)復(fù)位到固定值,一般是0x00000000,在STM32中,復(fù)位到0X08000004。因此復(fù)位后運(yùn)行的第一條代碼就是0X08000004。前面我們不是拷貝了一個(gè)啟動(dòng)代碼文件到工程嗎?startup_stm32f40_41xxx.s,這個(gè)匯編文件為什么叫啟動(dòng)代碼?因?yàn)槔锩娴膮R編程序,就是復(fù)位之后執(zhí)行的程序。在文件中,有一段數(shù)據(jù)表,稱為中斷向量,里面保存了各個(gè)中斷的執(zhí)行地址。復(fù)位,也是一個(gè)中斷。

芯片復(fù)位時(shí),芯片從中斷表中將Reset_Handler這個(gè)值(函數(shù)指針)加載到PC指針,芯片就會(huì)執(zhí)行Reset_Handler函數(shù)了。(一個(gè)函數(shù)入口就是一個(gè)指針)

; Vector Table Mapped to Address 0 at Reset

AREA RESET, DATA, READONLY

EXPORT __Vectors

EXPORT __Vectors_End

EXPORT __Vectors_Size

__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

Reset_Handler函數(shù),先執(zhí)行SystemInit函數(shù),這個(gè)函數(shù)在標(biāo)準(zhǔn)庫內(nèi),主要是初始芯片時(shí)鐘。然后跳到__main執(zhí)行,__main函數(shù)是什么函數(shù)?

是我們?cè)趍ain.c中定義的main函數(shù)嗎?后面我們?cè)僬f這個(gè)問題。

; Reset handler

Reset_Handler PROC

EXPORT Reset_Handler [WEAK]

IMPORT SystemInit

IMPORT __main

LDR R0, =SystemInit

BLX R0

LDR R0, =__main

BX R0

ENDP

芯片是怎么知道開始就執(zhí)行啟動(dòng)代碼的呢?或者說,我們?nèi)绾伟堰@個(gè)啟動(dòng)代碼放到復(fù)位的位置?這就牽涉到一個(gè)一般情況下不關(guān)注的文件wujique.sct,這個(gè)文件在wujiqueprjObjects目錄下,通常把這個(gè)文件叫做分散加載文件,編譯工具在鏈接時(shí),根據(jù)這個(gè)文件放置各個(gè)代碼段和變量。

在MDK軟件Options菜單Linker下有關(guān)于這個(gè)菜單的設(shè)置。

7ca8ec9c-8ecb-11eb-8b86-12bb97331649.png

把Use Memory Layout from Target Dialog前面的勾去掉,之前不可設(shè)置的框都可以設(shè)置了。點(diǎn)擊Edit進(jìn)行編輯。

7d0cff98-8ecb-11eb-8b86-12bb97331649.png

在代碼編輯框出現(xiàn)了分散加載文件內(nèi)容,當(dāng)前文件只有基本的內(nèi)容。

其實(shí)這個(gè)文件功能很強(qiáng)大,通過修改這個(gè)文件可以配置程序的很多功能,例如:1 指定FLASH跟RAM的大小于起始位置,當(dāng)我們把程序分成BOOT、CORE、APP,甚至進(jìn)行驅(qū)動(dòng)分離的時(shí)候,就可以用上了。2 指定函數(shù)與變量的位置,例如把函數(shù)加載到RAM中運(yùn)行。

7d81fc26-8ecb-11eb-8b86-12bb97331649.png

從這個(gè)基本的分散加載文件我們可以看出:

第6行 ER_IROM1 0x08000000 0x00080000定義了ER_IROM1,也就是我們說的內(nèi)部FLASH,從0x08000000開始,大小0x00080000。

第7行 .o (RESET, +First)從0x08000000開始,先放置一個(gè).o文件, 并且用(RESET, +First)指定RESET塊優(yōu)先放置,RESET塊是什么?請(qǐng)查看啟動(dòng)代碼,中斷向量就是一個(gè)AREA,名字叫RESET,屬于READONLY。這樣編譯后,RESET塊將放在0x08000000位置,也就是說,中斷向量就放在這個(gè)地方。DCD是分配空間,4字節(jié),第一個(gè)就是__initial_sp,第二個(gè)就是Reset_Handler函數(shù)指針。也就是說,最后編譯后的程序,將Reset_Handler這個(gè)函數(shù)的指針(地址),放在0x800000+4的地方。所以芯片在復(fù)位的時(shí)候,就能找到復(fù)位函數(shù)Reset_Handler。

第8行 *(InRoot$$Sections)什么鬼?GOOGLE??!回頭再說。

第9行 .ANY (+RO)意思就是其他的所有RO,順序往后放。就是說,其他代碼,跟著啟動(dòng)代碼后面。

第11行 RW_IRAM1 0x20000000 0x00020000定義了RAM大小。

第12行 .ANY (+RW +ZI)所有的RW ZI,全部放到RAM里面。RW,ZI,也就是變量,這一行指定了變量保存到什么地址。分析用戶代碼

到此,基本啟動(dòng)過程已經(jīng)分析完。下一步開始分析用戶代碼,就從main函數(shù)開始。1 程序跳轉(zhuǎn)到main函數(shù)后:RCC_GetClocksFreq獲取RCC時(shí)鐘頻率;SysTick_Config配置SysTick,在這里打開了SysTick中斷,10毫秒一次。

Delay(5);延時(shí)50毫秒。

int main(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

/*!《 At this stage the microcontroller clock setting is already configured,

this is done through SystemInit() function which is called from startup

files before to branch to application main.

To reconfigure the default setting of SystemInit() function,

refer to system_stm32f4xx.c file */

/* SysTick end of count event each 10ms */

RCC_GetClocksFreq(&RCC_Clocks);

SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);

/* Add your application code here */

/* Insert 50 ms delay */

Delay(5);

2 初始化IO就不說了,進(jìn)入while(1),也就是一個(gè)死循環(huán),嵌入式程序,都是一個(gè)死循環(huán),否則就跑飛了。

/*初始化LED IO口*/

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, &GPIO_InitStructure);

/* Infinite 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ù),這個(gè)函數(shù)使用兩個(gè)全局變量,兩個(gè)局部變量。

/* Private functions ---------------------------------------------------------*/

u32 TestTmp1 = 5;//全局變量,初始化為5

u32 TestTmp2;//全局變量,未初始化

const u32 TestTmp3[10] = {6,7,8,9,10,11,12,13,12,13};

u8 TestFun(u32 x)//函數(shù),帶一個(gè)參數(shù),并返回一個(gè)u8值

{

u8 test_tmp1 = 4;//局部變量,初始化

u8 test_tmp2;//局部變量,未初始化

static u8 test_tmp3 = 0;//靜態(tài)局部變量

test_tmp3++;

test_tmp2 = x;

if(test_tmp2》 TestTmp1)

test_tmp1 = 10;

else

test_tmp1 = 5;

TestTmp2 +=TestTmp3[test_tmp1];

return test_tmp1;

}

然后程序就一直在main函數(shù)的while循環(huán)里面執(zhí)行。中斷呢?對(duì),還有中斷。中斷中斷,就是中斷正常的程序執(zhí)行流程。我們查看Delay函數(shù),uwTimingDelay不等于0就死等?誰會(huì)將uwTimingDelay改為0?

/**

* @brief Inserts a delay time.

* @param nTime: specifies the delay time length, in milliseconds.

* @retval None

*/

void Delay(__IO uint32_t nTime)

{

uwTimingDelay = nTime;

while(uwTimingDelay != 0);

}

搜索uwTimingDelay變量,函數(shù)TimingDelay_Decrement會(huì)將變量一直減到0。

/**

* @brief Decrements the TimingDelay variable.

* @param None

* @retval None

*/

void TimingDelay_Decrement(void)

{

if (uwTimingDelay != 0x00)

{

uwTimingDelay--;

}

}

這個(gè)函數(shù)在哪里執(zhí)行?經(jīng)查找,在SysTick_Handler函數(shù)中運(yùn)行。誰用這個(gè)函數(shù)?

/**

* @brief This function handles SysTick Handler.

* @param None

* @retval None

*/

void SysTick_Handler(void)

{

TimingDelay_Decrement();

}

經(jīng)查找,在中斷向量表中有這個(gè)函數(shù),也即是說這個(gè)函數(shù)指針保存在中斷向量表內(nèi)。當(dāng)發(fā)生中斷時(shí),就會(huì)執(zhí)行這個(gè)函數(shù)。當(dāng)然,在進(jìn)出中斷會(huì)有保存和恢復(fù)現(xiàn)場(chǎng)的操作。這個(gè)主要涉及到匯編,暫時(shí)不進(jì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ù)?是我們?cè)趍ain.c中定義的main函數(shù)嗎?2 分散加載文件中*(InRoot$$Sections)是什么?3 ZI段,也就是初始化為0的數(shù)據(jù)段,什么時(shí)候初始化?誰初始化?

為什么這幾個(gè)問題前面留著不說?因?yàn)檫@是同一個(gè)問題。順藤摸瓜!

通過MAP文件了解代碼構(gòu)成

編譯結(jié)果

程序編譯后,在下方的Build Output窗口會(huì)輸出信息

*** Using Compiler ‘V5.06 update 5 (build 528)’, folder: ‘C:Keil_v5ARMARMCCBin’

Build target ‘wujique’

compiling stm32f4xx_it.c.。.

。..

assembling startup_stm32f40_41xxx.s.。.

compiling misc.c.。.

。..

compiling mcu_uart.c.。.

linking.。.

Program Size: Code=9038 RO-data=990 RW-data=40 ZI-data=6000

FromELF: creating hex file.。.

“.Objectswujique.axf” - 0 Error(s), 0 Warning(s)。

Build Time Elapsed: 00:00:32

編譯目標(biāo)是wujique

C文件compiling,匯編文件assembling,這個(gè)過程叫編譯

編譯結(jié)束后,就進(jìn)行l(wèi)ink,鏈接。

最后得到一個(gè)編譯結(jié)果,9038字節(jié)code,RO 990,RW 40,ZI 6000。CODE,是代碼,很好理解,那RO、RW、ZI都是什么?

FromELF,創(chuàng)建hex文件,F(xiàn)romELF是一個(gè)好工具,需要自己添加到option中才能用

map文件配置

更多編譯具體信息在map文件中,在MDK Options中我們可以看到,所有信息都放在Listingswujique.map

默認(rèn)很多編譯信息可能沒鉤,鉤上所有信息會(huì)增加編譯時(shí)間。

7dd450de-8ecb-11eb-8b86-12bb97331649.png

map文件

打開map文件,好亂?習(xí)慣就好。我們抓重點(diǎn)就行了。

7e3d69f2-8ecb-11eb-8b86-12bb97331649.png

map 總信息從最后看起,看到?jīng)]?最后的這一段map內(nèi)容,說明了整個(gè)程序的基本概況。

有多少RO?RO到底是什么?

有多少RW?RW又是什么?

ROM為什么不包括ZI Data?為什么包含RW Data?

7e78a2d8-8ecb-11eb-8b86-12bb97331649.png

Image component sizes往上,看看Image component sizes,這個(gè)就比剛剛的總體統(tǒng)計(jì)更細(xì)了。

這部分內(nèi)容,說明了每個(gè)源文件的概況

首先,是我們自己的源碼,這個(gè)程序我們的代碼不多,只有main.o,wujique_log.o,和其他一些STM32的庫文件。

7eb09efe-8ecb-11eb-8b86-12bb97331649.png

第2部分是庫里面的文件,看到?jīng)]?里面有一個(gè)main.o。main函數(shù)是不是我們寫的main函數(shù)?明顯不是,我們的main函數(shù)是放在main.o文件。這么小的一個(gè)工程,用了這么多庫,你以前關(guān)注過嗎?估計(jì)沒有,除非你曾經(jīng)將一個(gè)原本在1M flash上的程序壓縮到能在512K上運(yùn)行。

7ee513aa-8ecb-11eb-8b86-12bb97331649.png

第3部分也是庫,暫時(shí)沒去分析這兩個(gè)是什么東西。

7f1e0d0e-8ecb-11eb-8b86-12bb97331649.png

庫文件是什么?庫文件就是別人已經(jīng)別寫好的代碼庫。在代碼中,我們經(jīng)常會(huì)包含一些頭文件,例如:

#include 《stdarg.h》 #include 《stdlib.h》 #include 《string.h》

這些就是庫的頭文件。這些頭文件保存在MDK開發(fā)工具的安裝目錄下。我們經(jīng)常用的庫函數(shù)有:memcpy、memcmp、strcmp等。只要代碼中包含了這些函數(shù),就會(huì)鏈接庫文件。

文件map再往上,就是文件MAP了,也就是每個(gè)文件中的代碼段(函數(shù))跟變量在ROM跟RAM中的位置。首先是ROM在0x08000000確實(shí)放的是startup_stm32f40_41xxx.o中的RESET

庫文件是什么?

庫文件就是別人已經(jīng)別寫好的代碼庫。

在代碼中,我們經(jīng)常會(huì)包含一些頭文件,例如:

#include 《stdarg.h》

#include 《stdlib.h》

#include 《string.h》

這些就是庫的頭文件。這些頭文件保存在MDK開發(fā)工具的安裝目錄下。

我們經(jīng)常用的庫函數(shù)有:

memcpy、memcmp、strcmp等。

只要代碼中包含了這些函數(shù),就會(huì)鏈接庫文件。

文件map

再往上,就是文件MAP了,也就是每個(gè)文件中的代碼段(函數(shù))跟變量在ROM跟RAM中的位置。首先是ROM在0x08000000確實(shí)放的是startup_stm32f40_41xxx.o中的RESET

7f59b71e-8ecb-11eb-8b86-12bb97331649.png

每個(gè)文件有有多行,例如串口,4個(gè)函數(shù)。

7f96a46c-8ecb-11eb-8b86-12bb97331649.png

然后是RAM的,main.o中的變量,放在0x20000000,總共有0x0000000c,類型是Data、RW。串口有兩種變量,data和bss,什么是bss?這兩個(gè)名稱,是section name,也就是段的意思??辞懊鎡ype和Attr,

RW Data,放在.data段;RW Zero放在.bss段,RW Zero,其實(shí)就是ZI。到底哪些變量是RW,哪些是ZI?

7fc7ea86-8ecb-11eb-8b86-12bb97331649.png

Image Symbol Table再往上就是Image Symbol Table,就更進(jìn)一步到每個(gè)函數(shù)或者變量的信息了

80139b48-8ecb-11eb-8b86-12bb97331649.png

例如,全局變量TestTmp1,是Data,4字節(jié),分配的位置是0x20000004。

804f96e8-8ecb-11eb-8b86-12bb97331649.png

TestTmp3數(shù)組放在哪里?放在0X080024E0這個(gè)地方,這可是代碼區(qū)額。因?yàn)槲覀冇胏onst修飾了這個(gè)全局變量數(shù)組,告訴編譯器,這個(gè)數(shù)組是不可以改變的,編譯器就將這個(gè)數(shù)組保存到代碼中了。程序中我們經(jīng)常會(huì)使用一些大數(shù)組數(shù)據(jù),例如字符點(diǎn)陣,通常有幾K幾十K大,不可能也沒必要放到RAM區(qū),整個(gè)程序運(yùn)行過程這些數(shù)據(jù)都不改變,因此通過const修飾,將其存放到代碼區(qū)。

const的用處比較多,可以修飾變量,也可以修飾函數(shù)。更多用法自行學(xué)習(xí)

808b9792-8ecb-11eb-8b86-12bb97331649.png

那局部變量存放在哪里呢?我們找到了test_tmp3,

80bf172a-8ecb-11eb-8b86-12bb97331649.png

沒找到test_tmp1/test_tmp2,為什么呢?在定義時(shí),test_tmp3增加了static定義,意思就是靜態(tài)局部變量,功能上,相當(dāng)于全局變量,定義在函數(shù)內(nèi),限制了這個(gè)全局變量只能在這個(gè)函數(shù)內(nèi)使用。那test_tmp1、test_tmp2放在哪里呢? 局部變量,在編譯鏈接時(shí),并沒有分配空間,只有在運(yùn)行時(shí),才從棧分配空間。

u8 TestFun(u32 x)//函數(shù),帶一個(gè)參數(shù),并返回一個(gè)u8值

{

u8 test_tmp1 = 4;//局部變量,初始化

u8 test_tmp2;//局部變量,未初始化

static u8 test_tmp3 = 0;//靜態(tài)局部變量

上一部分,我們留了一個(gè)問題,哪些變量是RW,哪些是ZI?我們看看串口變量的情況,UartBuf3放在bss段,其他變量放在.data段。為什么數(shù)組就放在bss?bss是英文Block Started by Symbol的簡(jiǎn)稱。

80f139e4-8ecb-11eb-8b86-12bb97331649.png

到這里,我們可解釋下面幾個(gè)概念了:

Code就是代碼,函數(shù)。

RO Data,就是只讀變量,例如用const修飾的數(shù)組。

RW Data,就是讀寫變量,例如全局變量跟static修飾的局部變量。

ZI Data,就是系統(tǒng)自動(dòng)初始化為0的讀寫變量,大部分是數(shù)組,放在bss段。

RO Size等于代碼加只讀變量。

RW Size等于讀寫變量(包括自動(dòng)初始化為0的),這個(gè)也就是RAM的大小。

ROM Size,也就是我們編譯之后的目標(biāo)文件大小,也就是FLASH的大小。但是?為什么會(huì)包含RW Data呢?因?yàn)樗腥肿兞慷夹枰粋€(gè)初始化的值(就算沒有真正初始化,系統(tǒng)也會(huì)分配一個(gè)初始化空間),例如我們定義一個(gè)變量u8 i = 8;這樣的全局變量,8,這個(gè)值,就需要保存在FALSH區(qū)。

812c165e-8ecb-11eb-8b86-12bb97331649.png

我們看看函數(shù)的情況,前面我們不是有一個(gè)問題嗎?__main和main是一個(gè)函數(shù)嗎?查找main后發(fā)現(xiàn),main是main,放在0x08000579

81555294-8ecb-11eb-8b86-12bb97331649.png

main是main,放在0x08000189

8199e972-8ecb-11eb-8b86-12bb97331649.png

__main到main之間發(fā)生了什么?還記得分散加載文件中的這句嗎?

*(InRoot$$Sections)

__main就在這個(gè)段內(nèi)。下圖是__main的地址,在0x08000189。__Vectors就是中斷向量,放在最開始。

82209c2e-8ecb-11eb-8b86-12bb97331649.png

在分散加載文件中,緊跟RESET的就是*(InRoot$$Sections)。

827f1dc6-8ecb-11eb-8b86-12bb97331649.png

而且,RESET段正好大小0x00000188。

82b90e28-8ecb-11eb-8b86-12bb97331649.png

這一段代碼都完成什么功能呢?主要完成ZI代碼的初始化,也就是將一部分RAM初始化為0。其他環(huán)境初始化。。。。通常,我們不用管這一部分。

其他再往上,就是其他信息了,例如優(yōu)化了哪些東西,移除了哪些函數(shù)。最后

到這里,一個(gè)程序,是怎么組成的,程序是如何運(yùn)行的,基本有一個(gè)總體印象了。不過,對(duì)于中斷,后面還會(huì)進(jìn)行詳細(xì)說明。

原文標(biāo)題:深度:?jiǎn)纹瑱C(jī)到底是如何軟硬件結(jié)合的?

文章出處:【微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 單片機(jī)
    +關(guān)注

    關(guān)注

    6023

    文章

    44376

    瀏覽量

    628364
  • 嵌入式
    +關(guān)注

    關(guān)注

    5046

    文章

    18817

    瀏覽量

    298520
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3521

    瀏覽量

    93268

原文標(biāo)題:深度:?jiǎn)纹瑱C(jī)到底是如何軟硬件結(jié)合的?

文章出處:【微信號(hào):zhuyandz,微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    stm32單片機(jī)用什么軟件編程

    STM32單片機(jī)是一種廣泛應(yīng)用于嵌入式系統(tǒng)領(lǐng)域的微控制器,具有高性能、低功耗、豐富的外設(shè)接口等特點(diǎn)。要對(duì)STM32單片機(jī)進(jìn)行編程,需要選擇合適的軟件工具。 概述 STM32
    的頭像 發(fā)表于 09-02 10:16 ?330次閱讀

    單片機(jī)燒錄程序用什么軟件

    單片機(jī)燒錄程序是單片機(jī)開發(fā)過程的一個(gè)重要環(huán)節(jié),涉及到將編寫好的程序代碼通過燒錄器寫入單片機(jī)的ROM,以實(shí)現(xiàn)對(duì)
    的頭像 發(fā)表于 09-02 10:05 ?261次閱讀

    51單片機(jī)燒錄軟件怎么用

    準(zhǔn)備工作 確保您已經(jīng)安裝了51單片機(jī)燒錄軟件,如Keil、IAR等。 準(zhǔn)備好51單片機(jī)開發(fā)板和燒錄器,如STC-ISP等。 確保您的計(jì)算機(jī)已經(jīng)安裝了相應(yīng)的驅(qū)動(dòng)程序。 安裝燒錄軟件 從官
    的頭像 發(fā)表于 08-22 09:26 ?299次閱讀

    基于51單片機(jī)無線溫度傳輸控制設(shè)計(jì)

    本資源內(nèi)容概要:? ? ? ?這是基于51單片機(jī)無線溫度傳輸控制設(shè)計(jì)包含了電路圖源文件(Altiumdesigner軟件打開)、C語言程序源代碼(keil軟件打開)、元件清單(exce
    發(fā)表于 06-26 10:50 ?0次下載

    數(shù)字電路仿真軟件單片機(jī)怎么用

    數(shù)字電路仿真軟件是一種用于模擬和測(cè)試數(shù)字電路設(shè)計(jì)的工具。其中,單片機(jī)仿真軟件是一種專門針對(duì)單片機(jī)進(jìn)行仿真的工具。這種軟件能夠提供一個(gè)類似真實(shí)
    的頭像 發(fā)表于 04-21 10:28 ?862次閱讀

    單片機(jī)如何通過代碼控制硬件:一名工程師的分享

    今天跟大家聊聊單片機(jī)是怎樣通過代碼來操控硬件的。作為一名單片機(jī)工程師,我們平時(shí)的工作就像是給單片機(jī)編寫“指令集”,讓它按照我們的意圖去驅(qū)動(dòng)各種硬件
    的頭像 發(fā)表于 03-06 14:46 ?1088次閱讀
    <b class='flag-5'>單片機(jī)</b>如何通過代碼<b class='flag-5'>控制</b><b class='flag-5'>硬件</b>:一名工程師的分享

    什么是單片機(jī)及其特性與應(yīng)用領(lǐng)域

    單片機(jī)除具有計(jì)算機(jī)基本邏輯與數(shù)據(jù)處理功能之外,還自帶RAM、ROM、I/O口以及定時(shí)器等外設(shè),并由芯片制造商提供完備的軟件硬件開發(fā)工具及文檔支持,極大地方便了工程師們的開發(fā)工作。此外,單片機(jī)
    發(fā)表于 02-15 15:04 ?412次閱讀

    單片機(jī)控制和plc控制的優(yōu)缺點(diǎn)

    單片機(jī)控制和PLC控制是工業(yè)自動(dòng)化領(lǐng)域中常用的兩種控制方式。它們各自具有一定的優(yōu)缺點(diǎn),下面將對(duì)它們的優(yōu)缺點(diǎn)進(jìn)行詳細(xì)的分析和比較。 一、單片機(jī)
    的頭像 發(fā)表于 12-30 11:14 ?4487次閱讀

    單片機(jī)是如何控制數(shù)碼管顯示各種字符的?

    單片機(jī)控制數(shù)碼管顯示各種字符的過程是一個(gè)相對(duì)復(fù)雜的過程,涉及到硬件電路設(shè)計(jì)和軟件編程兩個(gè)方面。下面我將詳細(xì)介紹單片機(jī)如何
    的頭像 發(fā)表于 12-13 10:47 ?1400次閱讀

    51單片機(jī)控制步進(jìn)電機(jī)硬件連接部分

    本案例講解的內(nèi)容是51單片機(jī)控制步進(jìn)電機(jī)硬件連接部分。后續(xù)會(huì)分別講解單片機(jī)程序,S曲線加減速方法,上位機(jī)等相關(guān)內(nèi)容
    的頭像 發(fā)表于 10-26 11:11 ?2095次閱讀
    51<b class='flag-5'>單片機(jī)</b><b class='flag-5'>控制</b>步進(jìn)電機(jī)<b class='flag-5'>硬件</b>連接部分

    單片機(jī)硬件設(shè)計(jì)和PCB Layout參考

    單片機(jī)硬件設(shè)計(jì)和PCB Layout參考
    的頭像 發(fā)表于 10-25 15:57 ?919次閱讀
    <b class='flag-5'>單片機(jī)</b><b class='flag-5'>硬件</b>設(shè)計(jì)和PCB Layout參考

    基于ATmegn128單片機(jī)控制電路和軟件解決方案設(shè)計(jì)

    電子發(fā)燒友網(wǎng)站提供《基于ATmegn128單片機(jī)控制電路和軟件解決方案設(shè)計(jì).pdf》資料免費(fèi)下載
    發(fā)表于 10-19 10:46 ?0次下載
    基于ATmegn128<b class='flag-5'>單片機(jī)</b>的<b class='flag-5'>控制</b>電路和<b class='flag-5'>軟件</b>解決方案設(shè)計(jì)

    如何學(xué)習(xí)單片機(jī)步驟的知識(shí)梳理

    單片機(jī)的學(xué)習(xí)實(shí)踐 單片機(jī)提高重在實(shí)踐,想要學(xué)好單片機(jī),軟件編程必不可少。但是熟悉硬件對(duì)于學(xué)好單片機(jī)
    發(fā)表于 10-07 09:55 ?588次閱讀

    單片機(jī)原理圖的硬件設(shè)計(jì)、軟件設(shè)計(jì)和信號(hào)處理

    單片機(jī)原理圖是設(shè)計(jì)和開發(fā)電子產(chǎn)品不可或缺的一部分。它通過連接各種元器件和電路,實(shí)現(xiàn)了對(duì)單片機(jī)控制和操作。本文將從四個(gè)方面對(duì)單片機(jī)原理圖進(jìn)
    的頭像 發(fā)表于 10-05 15:26 ?2834次閱讀

    單片機(jī)CRC原理及應(yīng)用

    單片機(jī)CRC原理及應(yīng)用
    的頭像 發(fā)表于 09-27 16:02 ?1628次閱讀
    <b class='flag-5'>單片機(jī)</b><b class='flag-5'>中</b>CRC原理及應(yīng)用