[简介]单片机led模块定义函数的问题(一)

简介:对于嵌入式系统来说,如果没有RTOS运行c语言程序调试方法视频,程序开发中的main函数(main())需要通过某种机制永远快乐地运行,没有尽头。如果你想退出主函数,你做什么是由所使用的C语言编译器决定的。

关键词:C51,主程序,程序退出

01 要问的问题

今天在CSDN的单片机led模组定义函数问题[1]中看到一个有趣的问题。

一个简单的C51程序如下:

#include 
void test(num) {
    switch(num) {
        case 1: P2_0=0; P2_1=0; 
            break;
    }
}
void main(void) {
    test(1);
}

程序执行完毕后,可以看到实验板上的两个LED灯亮了,另外六个实际上是微亮的。

▲ 图1.1 实验板上不亮的 LED 居然微微亮起

如果在主程序中加入无限循环:while(1); ,电路板上将不再出现“微亮”现象。

#include 
void test(num) {
    switch(num) {
        case 1: P2_0=0; P2_1=0; 
            break;
    }
}
void main(void) {
    test(1);
    while(1);
}

▲ 图1.2 实验板背面的六个LED不再亮

上述两种情况的区别在于,在第二个程序中,主循环 main() 函数永远不会退出,而在第一个程序中,main() 函数会退出。看来前面LED的微亮应该和主函数退出后单片机做了什么有关。

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

02 节目去哪儿了?

从上面提问者写的代码来看,他应该是个C51爱好者,使用C51编译器,开心地在一块C51开发板上做实验。一开始他没有安装嵌入式程序开发的实践,在主程序中使用了一个无限循环void main(void)来控制主程序函数中的程序,出现了之前实验结果中的混乱情况。

注:我很佩服他是一个大胆而细心的人,他观察得非常仔细。

2.1 盘古创造世界

对于 C 语言编程,所有的用户程序世界都是从主程序 main() 开始的。为用户程序打开世界的任务是一小段盘古代码STARTUP.A51。

C51 的启动方式也在以下两篇博文中进行了测试和解释:

下面截取一段 STARTUP.A51 代码。可以看到盘古在MCU RESET后做了一些准备(初始化全局变量和堆栈指针)后,直接跳转到:?C_START

图片[1]-[简介]单片机led模块定义函数的问题(一)-老王博客

$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 Symbols 
ACC     DATA    0E0H
B       DATA    0F0H
SP      DATA    81H
DPL     DATA    82H
DPH     DATA    83H
                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
; This code is required if you use L51_BANK.A51 with Banking Mode 4
; Code Banking
;  Select Bank 0 for L51_BANK.A51 Mode 4
#if 0   
;      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
;
                LJMP    ?C_START
                END

上面的代码也已经在博文51单片机程序执行流程(STARTUP.A51)[3])中一步步调试和跟踪验证:

▲图2.1.1表示LJMP C_START是进入main()程序

2.2 世界末日

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

在博文MCU C语言while(1)中,作者对KEIL编译器和PIC MAPLAB编译器的main函数进行了最后一次反汇编。

2.2.1 Keil 编译器

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

MOV R0, #0x7F
CLR A
MOV @R0, A
DJNZ R0, (3)
MOV SP, #0x0C
LJMP main

这几条语句,前4条是清空我们单片机内存的前128个地址,第五条是定义栈,第六条是把程序重新跳转到main函数的第一行执行。

2.2.2 MAPLAB 编译器

跟踪PIC单片机语言程序c语言程序调试方法视频,发现main()函数的最后一条语句被reset,即单片机直接reset。这是 MAPLAB 编译器根据 PIC 单片机的特性添加的复位语句。

※ 总结※

对于嵌入式系统来说,如果没有RTOS运行,程序开发中的main函数(main())需要通过某种机制永远快乐地运行,没有尽头。

如果你想退出主函数,你做什么是由所使用的C语言编译器决定的。

参考

[1]

单片机led模组功能定义问题:

[2]

51 MCU程序执行流程(STARTUP.A51管理Main函数的执行):

[3]

51 MCU程序执行流程(STARTUP.A51):

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论