问题提出
    
   
   
 
 在进行基本的C51编程实验,编写了一个简单的C51程序如下:
#include <REGX51.H>void test(num) {switch(num) {case 1: P2_0=0; P2_1=0;break;}}void main(void) {test(1);}
程序执行完之后,可以看到实验板上的有两个LED被点亮,另外六个居然微微发亮。
 
 如果在主程序中,增加一个无限循环:while(1); ,则电路板上的就不再会出现“微微点亮”的现象了。
   
 
 
  上面两种情况的区别,在于第二个程序中主循环main()函数始终没有退出,而第一个程序,main() 函数退出了。
似乎前面LED微微点亮应该与主函数退出之后,单片机都干了些啥有关系。
那么就剩下一个问题:对于普通的嵌入式系统,C语言编程中main()函数退出之后,去哪儿了?
  
   
    
     程序去哪儿了?
    
   
   
 
 上面使用的是C51的编译器,在一款C51开发板上进行的实验。他一开始没有按照嵌入式程序开发的惯例:在主程序void main(void) 中利用死循环将程序控制在主程序函数中,就出现了前面实验结果中令人迷惑的情况。
1 开天辟地
对于C语言编程来说,所有的用户程序世界是从主程序 main() 开始的。给用户程序开天辟地的任务是由一小段盘古代码STARTUP.A51。
关于C51是如何启动的, 在如下面博文中也被测试说明:
- 
   51单片机程序执行流程(STARTUP.A51管理Main函数的执行)[2] 
下面截取了 STARTUP.A51 代码的一段,可以看到盘古在单片机 RESET 之后做了点准备工作(初始化全局变量、堆栈指针)之后,就直接跳转至:?C_START
$NOMOD51;------------------------------------------------------------------------------; This file is part of the C51 Compiler package; Copyright (c) 1988-2005 Keil Elektronik GmbH and Keil Software, Inc.; Version 8.01;; *** <<< Use Configuration Wizard in Context Menu >>> ***;------------------------------------------------------------------------------; STARTUP.A51: This code is executed after processor reset.;; To translate this file use A51 with the following invocation:;; A51 STARTUP.A51;; To link the modified STARTUP.OBJ file to your application use the following; Lx51 invocation:;; Lx51 your object file list, STARTUP.OBJ controls;;------------------------------------------------------------------------------; Standard SFR SymbolsACC DATA 0E0HB DATA 0F0HSP DATA 81HDPL DATA 82HDPH DATA 83HNAME ?C_STARTUP?C_C51STARTUP SEGMENT CODE?STACK SEGMENT IDATARSEG ?STACKDS 1EXTRN CODE (?C_START)PUBLIC ?C_STARTUPCSEG AT 0?C_STARTUP: LJMP STARTUP1RSEG ?C_C51STARTUPSTARTUP1:IF IDATALEN <> 0MOV R0,#IDATALEN - 1CLR AIDATALOOP: MOV @R0,ADJNZ R0,IDATALOOPENDIFIF XDATALEN <> 0MOV DPTR,#XDATASTARTMOV R7,#LOW (XDATALEN)IF (LOW (XDATALEN)) <> 0MOV R6,#(HIGH (XDATALEN)) +1ELSEMOV R6,#HIGH (XDATALEN)ENDIFCLR AXDATALOOP: MOVX @DPTR,AINC DPTRDJNZ R7,XDATALOOPDJNZ R6,XDATALOOPENDIFIF PPAGEENABLE <> 0MOV PPAGE_SFR,#PPAGEENDIFIF PDATALEN <> 0MOV R0,#LOW (PDATASTART)MOV R7,#LOW (PDATALEN)CLR APDATALOOP: MOVX @R0,AINC R0DJNZ R7,PDATALOOPENDIFIF IBPSTACK <> 0EXTRN DATA (?C_IBP)MOV ?C_IBP,#LOW IBPSTACKTOPENDIFIF XBPSTACK <> 0EXTRN DATA (?C_XBP)MOV ?C_XBP,#HIGH XBPSTACKTOPMOV ?C_XBP+1,#LOW XBPSTACKTOPENDIFIF PBPSTACK <> 0EXTRN DATA (?C_PBP)MOV ?C_PBP,#LOW PBPSTACKTOPENDIFMOV SP,#?STACK-1; This code is required if you use L51_BANK.A51 with Banking Mode 4;<h> Code Banking; <q> Select Bank 0 for L51_BANK.A51 Mode 4#if 0; <i> Initialize bank mechanism to code bank 0 when using L51_BANK.A51 with Banking Mode 4.EXTRN CODE (?B_SWITCH0)CALL ?B_SWITCH0 ; init bank mechanism to code bank 0#endif;</h>LJMP ?C_STARTEND
上面的代码也被博文 51单片机程序执行流程(STARTUP.A51)[3] 中进行逐步调试跟踪验证过:
 
  2 世界尽头
由于进入main() 函数是长跳转。
所以main函数是不会正常返回到启动程序 STARTUP.A51的。
那么程序去哪了?
在博文 单片机C语言while(1)的问题 中作者对于 KEIL编译器和PIC的 MAPLAB编译器对于main函数的最后时光进行了反汇编查看。
3 Keil编译器
在main函数的最后,程序增加了一下几行代码:
MOV R0, #0x7FCLR AMOV @R0, ADJNZ R0, (3)MOV SP, #0x0CLJMP main
这几条语句,前4条,是将我们单片机的内存的前128个地址清零,第5条,是定义堆栈,第6条,是将程序重新跳转到main函数的首行进行执行。
4 MAPLAB编译器
PIC 单片机语言程序进行跟踪,发现main() 函数最后一条语句为 reset,也就是单片机直接复位,这是 MAPLAB编译器根据 PIC 单片机特点增加的复位语句。
总结
如果单片机程序 从main函数中退出,具体干什么是由所使用的C语言编译器决定的。
对于嵌入式系统,如果没有运行RTOS,那么程序开发中的主函数(main())需要通过某种机制使其永远运行下去,它没有终点。
参考资料
单片机led模块定义函数的问题 : https://ask.csdn.net/questions/7640604?utm_medium=distribute.pc_feed_v2.none-task-ask-ask_personrec_tag-3.pc_personrecdepth_1-utm_source=distribute.pc_feed_v2.none-task-ask-ask_personrec_tag-3.pc_personrec
[2]51单片机程序执行流程(STARTUP.A51管理Main函数的执行) : https://blog.csdn.net/ChenGuiGan/article/details/88769619
[3]51单片机程序执行流程(STARTUP.A51) : https://blog.csdn.net/tangsun999/article/details/45604507
来源
https://blog.csdn.net/zhuoqingjoking97298/article/details/122836855 
 
                         
                     
                     
                     
                     
                     
                     
                     
                     
                    