单片机led模块定义函数的问题

电子说

1.3w人已加入

描述

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

01 问题提出

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

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

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

C51

▲ 图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); }

C51

▲ 图1.2 实验板上后面六个LED就不再点亮了

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

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

02 程序去哪儿了?

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

注:他是一个胆大心细的人,观察还挺仔细的。

2.1 盘古开天辟地

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

关于C51是如何启动的, 在如下面博文中也被测试说明:

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

下面截取了 STARTUP.A51 代码的一段,可以看到盘古在单片机 RESET 之后做了点准备工作(初始化全局变量、堆栈指针)之后,就直接跳转至:?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单片机程序执行流程(STARTUP.A51)[3] 中进行逐步调试跟踪验证过:

C51

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

2.2 世界尽头

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

在博文 单片机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个地址清零,第5条,是定义堆栈,第6条,是将程序重新跳转到main函数的首行进行执行。

2.2.2 MAPLAB编译器

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

※ 总  结 ※

对于嵌入式系统,如果没有运行RTOS,那么程序开发中的 主函数(main())需要通过某种机制使其永远愉快的运行下去,它没有终点。

如果想从main函数中退出,具体干什么是由所使用的C语言编译器决定的。

      审核编辑 :李倩

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分