正常的程序,都不會跳出main,但是,如果跳出了 main 函數(shù),程序到底去哪兒了,你有相關(guān)這個問題嗎?
一、問題提出
今天在單片機led模塊定義函數(shù)中看到一個有趣的問題。提問者在進(jìn)行基本的C51編程實驗,編寫了一個簡單的C51程序如下:
#include?void?test(num)?{ ????switch(num)?{ ????????case?1:?P2_0=0;?P2_1=0;? ????????????break; ????} } void?main(void)?{ ????test(1); }
程序執(zhí)行完之后,可以看到實驗板上的有兩個LED被點亮,另外六個居然微微發(fā)亮。
如果在主程序中,增加一個無限循環(huán):while(1); ,則電路板上的就不再會出現(xiàn)“微微點亮”的現(xiàn)象了。
#include?void?test(num)?{ ????switch(num)?{ ????????case?1:?P2_0=0;?P2_1=0;? ????????????break; ????} } void?main(void)?{ ????test(1); ????while(1); }
上面兩種情況的區(qū)別,在于第二個程序中主循環(huán) main()函數(shù)始終沒有退出,而第一個程序,main()函數(shù)退出了。似乎前面LED微微點亮 應(yīng)該與主函數(shù)退出之后,單片機都干了些啥有關(guān)系。
那么就剩下一個問題:對于普通的嵌入式系統(tǒng),C語言編程中main()函數(shù)退出之后,程序去哪兒了?
二、程序去哪兒了?
從上面提問者書寫的代碼來看,應(yīng)該是一位C51的愛好者,使用的是C51的編譯器,在一款C51開發(fā)板上愉快的進(jìn)行實驗。他一開始沒有安裝嵌入式程序開發(fā)的慣例 在主程序void main(void)中利用無限循環(huán)將程序控制在主程序函數(shù)中,就出現(xiàn)了前面實驗結(jié)果中令人迷惑的情況。
“
注:他是一個膽大心細(xì)的人,觀察還挺仔細(xì)的。
”
2.1 盤古開天辟地
對于C語言編程來說,所有的用戶程序世界是從主程序main()開始的。給用戶程序開天辟地的任務(wù)是由一小段盤古代碼STARTUP.A51。
51單片機程序執(zhí)行流程(STARTUP.A51管理Main函數(shù)的執(zhí)行)
下面截取了STARTUP.A51 代碼的一段,可以看到盤古在單片機RESET之后做了點準(zhǔn)備工作(初始化全局變量、堆棧指針)之后,就直接跳轉(zhuǎn)至:?C_START
?NAME?????C_STARTUP ?C_C51STARTUP???SEGMENT???CODE ?STACK??????????SEGMENT???IDATA ????????????????RSEG?????STACK ????????????????DS??????1 ????????????????EXTRN?CODE?(?C_START) ????????????????PUBLIC???C_STARTUP ????????????????CSEG????AT??????0 ?C_STARTUP:?????LJMP????STARTUP1 ????????????????RSEG?????C_C51STARTUP STARTUP1: IF?IDATALEN?<>?0 ????????????????MOV?????R0,#IDATALEN?-?1 ????????????????CLR?????A IDATALOOP:??????MOV?????@R0,A ????????????????DJNZ????R0,IDATALOOP ENDIF IF?XDATALEN?<>?0 ????????????????MOV?????DPTR,#XDATASTART ????????????????MOV?????R7,#LOW?(XDATALEN) ??IF?(LOW?(XDATALEN))?<>?0 ????????????????MOV?????R6,#(HIGH?(XDATALEN))?+1 ??ELSE ????????????????MOV?????R6,#HIGH?(XDATALEN) ??ENDIF ????????????????CLR?????A XDATALOOP:??????MOVX????@DPTR,A ????????????????INC?????DPTR ????????????????DJNZ????R7,XDATALOOP ????????????????DJNZ????R6,XDATALOOP ENDIF IF?PPAGEENABLE?<>?0 ????????????????MOV?????PPAGE_SFR,#PPAGE ENDIF IF?PDATALEN?<>?0 ????????????????MOV?????R0,#LOW?(PDATASTART) ????????????????MOV?????R7,#LOW?(PDATALEN) ????????????????CLR?????A PDATALOOP:??????MOVX????@R0,A ????????????????INC?????R0 ????????????????DJNZ????R7,PDATALOOP ENDIF IF?IBPSTACK?<>?0 EXTRN?DATA?(?C_IBP) ????????????????MOV??????C_IBP,#LOW?IBPSTACKTOP ENDIF IF?XBPSTACK?<>?0 EXTRN?DATA?(?C_XBP) ????????????????MOV??????C_XBP,#HIGH?XBPSTACKTOP ????????????????MOV??????C_XBP+1,#LOW?XBPSTACKTOP ENDIF IF?PBPSTACK?<>?0 EXTRN?DATA?(?C_PBP) ????????????????MOV??????C_PBP,#LOW?PBPSTACKTOP ENDIF ????????????????MOV?????SP,#?STACK-1 ????????????????LJMP?????C_START ????????????????END
上面的代碼也被博文51單片機程序執(zhí)行流程(STARTUP.A51)中進(jìn)行逐步調(diào)試跟蹤驗證過:
2.2 世界盡頭
由于進(jìn)入main()函數(shù)是長跳轉(zhuǎn),所以main函數(shù)是不會正常返回到啟動程序STARTUP.A51,那么程序去哪了?
在博文單片機C語言while(1)的問題中作者對于KEIL編譯器和PIC的MAPLAB編譯器對于main函數(shù)的最后時光進(jìn)行了反匯編查看。
Keil編譯器
在main函數(shù)的最后,程序增加了一下幾行代碼:
MOV?R0,?#0x7F CLR?A MOV?@R0,?A DJNZ?R0,?(3) MOV?SP,?#0x0C LJMP?main
這幾條語句,前4條,是將我們單片機的內(nèi)存的前128個地址清零,第5條,是定義堆棧,第6條,是將程序重新跳轉(zhuǎn)到main函數(shù)的首行進(jìn)行執(zhí)行。
MAPLAB編譯器
PIC 單片機語言程序進(jìn)行跟蹤,發(fā)現(xiàn)main() 函數(shù)最后一條語句為 reset,也就是單片機直接復(fù)位,這是 MAPLAB編譯器根據(jù) PIC 單片機特點增加的復(fù)位語句。
總結(jié)
對于嵌入式系統(tǒng),如果沒有運行RTOS,那么程序開發(fā)中的主函數(shù)(main())需要通過某種機制使其永遠(yuǎn)愉快的運行下去,它沒有終點。如果想從main函數(shù)中退出,具體干什么是由所使用的C語言編譯器決定的。
審核編輯:黃飛
?
評論
查看更多