• 注册 / 登录
  • 切换到窄版
  • 查看: 1749|回复: 0

    单片机main()函数退出之后,程序会跑去哪里?

    [复制链接]

    676

    主题

    690

    帖子

    6808

    积分

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    6808
    发表于 2023-4-4 10:07:41 | 显示全部楼层 |阅读模式

    路线栈欢迎您!

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    前言

    对于嵌入式系统,如果没有运行RTOS,那么程序开发中的主函数main()需要通过某种机制使其永远愉快的运行下去,它没有终点。如果想从main函数中退出,具体干什么是由所使用的C语言编译器决定的。

    一、问题提出

    在单片机led模块定义函数中看到一个有趣的问题。提问者在进行基本的C51编程实验,编写了一个简单的C51程序如下:

    1. #include <REGX51.H>

    2. void test(num) {
    3.     switch(num) {
    4.         case 1: P2_0=0; P2_1=0;
    5.             break;
    6.     }
    7. }

    8. void main(void) {
    9.     test(1);
    10. }
    复制代码

    程序执行完之后,可以看到实验板上的有两个LED被点亮,另外六个居然微微发亮。

    1.jpg

    如果在主程序中,增加一个无限循环:while(1); ,则电路板上的就不再会出现“微微点亮”的现象了。

    1. #include <REGX51.H>

    2. void test(num) {
    3.     switch(num) {
    4.         case 1: P2_0=0; P2_1=0;
    5.             break;
    6.     }
    7. }

    8. void main(void) {
    9.     test(1);
    10.     while(1);
    11. }
    复制代码

    2.jpg

    上面两种情况的区别,在于第二个程序中主循环 main()函数始终没有退出,而第一个程序,main()函数退出了。似乎前面LED微微点亮 应该与主函数退出之后,单片机都干了些啥有关系。

    那么就剩下一个问题:对于普通的嵌入式系统,C语言编程中main()函数退出之后,程序去哪儿了?

    二、程序去哪儿了?

    从上面提问者书写的代码来看,应该是一位C51的爱好者,使用的是C51的编译器,在一款C51开发板上愉快的进行实验。他一开始没有安装嵌入式程序开发的惯例 在主程序void main(void)中利用无限循环将程序控制在主程序函数中,就出现了前面实验结果中令人迷惑的情况。

    1. “注:他是一个胆大心细的人,观察还挺仔细的。”
    复制代码

    2.1 盘古开天辟地

    对于C语言编程来说,所有的用户程序世界是从主程序main()开始的。给用户程序开天辟地的任务是由一小段盘古代码STARTUP.A51。

    51单片机程序执行流程(STARTUP.A51管理Main函数的执行)

    下面截取了STARTUP.A51 代码的一段,可以看到盘古在单片机RESET之后做了点准备工作(初始化全局变量、堆栈指针)之后,就直接跳转至:?C_START

    1. NAME    ?C_STARTUP

    2. ?C_C51STARTUP   SEGMENT   CODE
    3. ?STACK          SEGMENT   IDATA

    4.                 RSEG    ?STACK
    5.                 DS      1

    6.                 EXTRN CODE (?C_START)
    7.                 PUBLIC  ?C_STARTUP

    8.                 CSEG    AT      0
    9. ?C_STARTUP:     LJMP    STARTUP1

    10.                 RSEG    ?C_C51STARTUP

    11. STARTUP1:

    12. IF IDATALEN <> 0
    13.                 MOV     R0,#IDATALEN - 1
    14.                 CLR     A
    15. IDATALOOP:      MOV     @R0,A
    16.                 DJNZ    R0,IDATALOOP
    17. ENDIF

    18. IF XDATALEN <> 0
    19.                 MOV     DPTR,#XDATASTART
    20.                 MOV     R7,#LOW (XDATALEN)
    21.   IF (LOW (XDATALEN)) <> 0
    22.                 MOV     R6,#(HIGH (XDATALEN)) +1
    23.   ELSE
    24.                 MOV     R6,#HIGH (XDATALEN)
    25.   ENDIF
    26.                 CLR     A
    27. XDATALOOP:      MOVX    @DPTR,A
    28.                 INC     DPTR
    29.                 DJNZ    R7,XDATALOOP
    30.                 DJNZ    R6,XDATALOOP
    31. ENDIF

    32. IF PPAGEENABLE <> 0
    33.                 MOV     PPAGE_SFR,#PPAGE
    34. ENDIF

    35. IF PDATALEN <> 0
    36.                 MOV     R0,#LOW (PDATASTART)
    37.                 MOV     R7,#LOW (PDATALEN)
    38.                 CLR     A
    39. PDATALOOP:      MOVX    @R0,A
    40.                 INC     R0
    41.                 DJNZ    R7,PDATALOOP
    42. ENDIF

    43. IF IBPSTACK <> 0
    44. EXTRN DATA (?C_IBP)

    45.                 MOV     ?C_IBP,#LOW IBPSTACKTOP
    46. ENDIF

    47. IF XBPSTACK <> 0
    48. EXTRN DATA (?C_XBP)

    49.                 MOV     ?C_XBP,#HIGH XBPSTACKTOP
    50.                 MOV     ?C_XBP+1,#LOW XBPSTACKTOP
    51. ENDIF

    52. IF PBPSTACK <> 0
    53. EXTRN DATA (?C_PBP)
    54.                 MOV     ?C_PBP,#LOW PBPSTACKTOP
    55. ENDIF

    56.                 MOV     SP,#?STACK-1
    57.                 LJMP    ?C_START

    58.                 END
    复制代码

    上面的代码也被博文51单片机程序执行流程(STARTUP.A51)中进行逐步调试跟踪验证过:

    3.png

    2.2 世界尽头

    由于进入main()函数是长跳转,所以main函数是不会正常返回到启动程序STARTUP.A51,那么程序去哪了?

    在博文单片机C语言while(1)的问题中作者对于KEIL编译器和PIC的MAPLAB编译器对于main函数的最后时光进行了反汇编查看。

    Keil编译器

    在main函数的最后,程序增加了一下几行代码:

    1. MOV R0, #0x7F
    2. CLR A
    3. MOV @R0, A
    4. DJNZ R0, (3)
    5. MOV SP, #0x0C
    6. LJMP main
    复制代码

    这几条语句,前4条,是将我们单片机的内存的前128个地址清零,第5条,是定义堆栈,第6条,是将程序重新跳转到main函数的首行进行执行。

    MAPLAB编译器

    PIC 单片机语言程序进行跟踪,发现main() 函数最后一条语句为 reset,也就是单片机直接复位,这是 MAPLAB编译器根据 PIC 单片机特点增加的复位语句。

    总结

    对于嵌入式系统,如果没有运行RTOS,那么程序开发中的主函数(main())需要通过某种机制使其永远愉快的运行下去,它没有终点。如果想从main函数中退出,具体干什么是由所使用的C语言编译器决定的。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    小黑屋|路丝栈 ( 粤ICP备2021053448号 )

    GMT+8, 2024-12-22 14:06 , Processed in 0.045477 second(s), 21 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表