产品概述

keil单片机程序 51单片机程序执行流程详细分析

小编 2024-10-08 产品概述 23 0

51单片机程序执行流程详细分析

单片机是没有上操作系统的东西,在keil中编写的代码都是裸机代码,深入编写裸机代码有助于了解硬件的特性。

若不是硬件特性已定的情况之下的其它流程都是代码作祟。忽然想到来探探51单片机的执行流程。这个念头起源于最初见到每个51程序里面的主函数里面最终都挂一个while(1);语句。为何要加一句while死循环让程序停留在main函数中呢。将while(1);语句去掉有什么影响么?

写一个很简单的程序试一下。

执行以上程序,由P1端口控制的流水灯闪了一下。程序最终进入while(1);里纠缠去了,这个到好解释。

现将while(1);语句屏蔽掉。我还以为程序不能被正确执行了呢,因为退出了main主函数,就像Render需要循环来实现一样(尽管刚刚闪灯的程序不在循环之内,但我还是不由产生了这一错觉)。程序执行的结果是:流水灯不停的闪烁!

看到这个现象后的猜想及动作^-^:

(1) 这块板坏了吧!(在带操作系统如linux字符界面下运行一个不带死循环的C语言文件完毕后就会返回到linux shell程序中)。赶紧换个板再测试一下,显然还是一样的结果。

(2) 单片机中将一直执行main函数中的最后一个(些)语句?(基于带OS平台下运行标准C语言文件的经验,可从来没有想过是main函数被多次调用或多次进入)

(3) 单片机内将C语言指令取出来加载到单片机内,单片机内自动生成一个主程序循环执行C语言中main函数的内容?(虽然很荒唐,还是想了)

(4) 赶快谷歌百度一下单片机的执行流程(虽然在谷歌百度时以“51单片机程序执行流程”搜索,没有搜到相关内容)。换朴实的搜索词:“51单片机 main”。然后就出现跟我一样带有疑问的问题:为什么main函数中不加while(1);语句之后程序会反复执行呢?回答的关键词包括“程序跑飞、看门狗、复位”。

(5) 趁上嵌入式的机会将“51单片机程序执行流程”搬出来并向老师讲述了我所写程序的得到的现象,包括我怎么验证呀等等。

老师的回答:Keil C51程序自动加载了一个名为”STARTUP.A51”的文件,在这个文件里面进行了一系列的初始化操作后进入用户编写的C语言程序入口main函数中,main函数执行完毕后,STARTUP.A51文件后有一句跳转到程序入口main函数的语句,所以会再次进入C语言主程序main函数中执行相关内容。

然后我用keil软件模拟了运行一下以上那一段代码:

程序开始运行就在程序入口main函数的第一条语句之处,Disassembly窗口是c语言代码与汇编代码相对应的窗口,前面是地址,后面的是C语言对应的汇编语句。下面的窗口是相应文件的运行代码的位置,由黄色箭头指向当前正要执行的代码。然后点击单步运行工具条,指导跳出main函数为止,程序跳转到STARTUP.A51中的以下代码位置:

这里是一个循环,根据DJNZ指令的功能:每执行一次DJNZ RO, IDATALOOP就将R0的值减1,若R0的值不为0则就跳到IDATALOOP地址去。很显然这是一个循环,那么RO的值是多少呢,在以下窗口显示:

可见r0的初值为0x7f,这里将要循环0x7f(128)次,具体在这里r0值的含义可查看一下子的。那么在这个循环之后程序又将去哪里呢?跳过这个循环后程序运行的地方如下:

在单步运行一次:

根据Disassembly的内容,此条语句执行了就又要回到main函数中去了,执行一下试试:

是的!

所以,在51单片机中,程序的执行流程就是会不断( 以r0的值作为延迟条件, 具体含义可继续探索 )的进入main函数中执行main函数中的代码。

为什么我们在linux等上面运行不带死循环的C语言代码后程序就会自行终止呢?这是不同的操作流程:

(1) C51单片机不带OS(操作系统),代码的执行形势在此看来就由STARTUP.A51来安排了,没有一个更大的程序来管理怎么调用main函数。

(2) 像Linux这类的平台是带了OS的,运行一个C语言程序对linux来说就是一个任务,除了运行C语言程序这个任务外还有其它的任务。当运行一个C语言程序完毕时,此次的任务也算是完成了。如在linux shell界面运行一个文件名为“hello.c”功能为输出“hello world!”的C语言程序,过程如下:

编译:gcc hello.c –o hello

运行:./hello

在运行hello可执行文件时,可以当做是shell调用了hello这个可执行程序。在hello运行完毕后,将返回值等返回给shell界面。整个C语言文件的生死全有linux shell程序管理。

归其原因,还是代码规定的机制不一样吧。

本文转载自网络,如涉版权请联系我们删除

看完本文有收获?请分享给更多人

回复关键词有干货: 电路设计丨电容丨三极管丨PCB丨接地‧‧‧‧‧‧

长按二维码识别关注

阅读原文可一键关注+历史信息

Stm32单片机开发KEIL启动文件汇编语言详解

文章目录

简介 启动文件汇编代码相关指令 堆栈空间的定义 初始化中断向量表 复位中断函数 中断函数的弱(WEAK)声明 用户栈和堆初始化

简介

我们在做单片机编程的时候,大部分都是用KEIL自带的启动文件来使程序进入C语言main函数,然后进行C语言编程开发的工作。那么这个启动文件到底做了什么呢?相信朋友们肯定和我一样好奇,想弄明白启动文件到底都干了些什么。那么本文就来介绍下,本文介绍stm32启动文件汇编代码,对应文件名startup_stm32f10x_hd.s。其他Cortex-M3内核的单片机都是大同小异的。

其实启动文件存在的目的就是构建可以供C语言代码运行的工作环境,比如传递参数时需要的栈空间初始化,动态分配内存时的堆初始化,一些初始化为0的变量空间的初始化等等。如果这些没有配置好,无法达到C语言代码运行的工作环境,那么后面的C语言代码执行的结果就是不对的,也会导致总个系统无法工作。所以启动文件很重要,也正是因为我们觉得它重要,所以才想搞懂它。

startup_stm32f10x_hd.s启动文件中的汇编代码主要做了下面5个工作。

1.堆栈空间的定义;

2.初始化中断向量表;

3.复位中断函数(Reset_Handler){系统初始化,然后进入main函数};

4.中断函数的弱(WEAK)声明

5.用户栈和堆初始化

再介绍这5个部分的详细代码前,这里已经先总结了启动文件中用到的汇编代码与编译器相关的指令,下面就先来介绍下这些指令。

启动文件汇编代码相关指令

启动文件代码主要由ARM指令代码和与编译器相关的汇编指令组成,下表罗列了启动文件中用到的相关汇编指令。

上面只是对指令做了简要的说明,后面代码用到时我们再一一讲解。我们也可以通过在代码中选中指令,按F1按键调出帮助说明,查看具体指令的相关介绍。

接下来我们就对代码做详细分析吧。

一、堆栈空间的定义

首先这里使用指令EQU定义了一个数值常量符号Stack_Size指明栈大小为0x00000400即1K,这个值可以根据实际需求更改。使用QEU定义常数,类似与C语言的#define定义常数。

然后这里又使用指令AREA定义了一个未初始化,可以读写并要求8字节边界对齐的段,段名,为STACK。这里可以为段选择任何段名。但是,以一个数字开始的名称必须包含在竖杠号内,否则会产生一个缺失段名错误。例如, |1_DataArea|。有些名称是习惯性的名称。例如,|.text| 用于表示由 C 编译程序产生的代码段,或用于以某种方式与 C 库关联的代码段。这里的NOINIT表示数据段是未初始化的或初始化为零。其只包含零初始化的空间保留命令 SPACE 或 DCB, DCD, DCDU, DCQ, DCQU, DCW 或 DCWU 。可以决定在链接时 AREA 是未初始化的,还是零初始化的,后面一条指令是SPACE,所以这里要初始化为0,READWRITE指明段可以读写,ALIGN=3指明段要在2^3=8字节边界上对齐。

再然后使用SPACE定义了一个初始化为0的存储块Stack_Mem,可以理解为存储块是归别到段里的。标号__initial_sp紧挨着SPACE语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的。

同样的堆也是这样定义的,只是这里先是指明了堆开始__heap_base(堆起始地址),再指明堆存储块,最后指明__heap_limit(堆终止地址)。堆是由低向高生长的,跟栈的生长方向相反。堆主要用来动态内存的分配,像malloc()函数申请的内存就在堆上面。这里堆默认大小为0x00000200即512字节,一般的程序中我们很少用到malloc函数,所以这里也就不做过多更改,如果要使用malloc函数,需要将此处堆大小定义的值根据需求改大。

后面的PRESERVE8,指明当前文件的堆栈按照8字节对齐。

二、初始化中断向量表

THUMB指示汇编器将随后的指令解释为16位的Thumb指令。Cortex-M3使用的是Thumb-2指令集,是一种介于Thumb指令集和ARM指令集。ARM指令集全部是32位的,Thumb指令集全部是16位的,Thumb-2指令集是即有部分16位Thumb指令的也有部分32位的ARM指令。

后面定义一个只读数据段RESET,用于保存中断向量表,和三个标号__Vectors(向量表开始)、__Vectors_End(向量表结束)和__Vectors_Size(向量表大小)并使用EXPORT指明其具有全局性。这样可以使在其他文件中访问此文件中的这三标号。

DCD 命令分配一个或多个字的存储器,在四个字节的边界上对齐,并定义存储器的运行时初值。

__Vectors DCD __initial_sp ; Top of Stack

这里就指示了段的开始为向量表的开始,标号__Vectors(向量表开始)编译器会根据不同单片机为其指定值,比如stm32单片机就是0x08000000,然后我们定义的RESET段就被分在了0x08000000开始的地址处,其结束位置就是从0x08000000开始依次加4个字节,因为这里每个DCD命令占存储器4个字节,这样一直到__Vectors_End(向量表结束),__Vectors_Size(向量表大小)就是这个RESET段所占大小。比如复位的时候,复位中断来了,就从这个段的第二个存储地址0x08000004处对应的值0x08000144作为复位函数Reset_Handler的地址。

三、复位中断函数

这里先使用AREA定义了一个只读代码段。这里的标号Reset_Handler就代表了复位函数的入口地址(函数名),使用PROC标记函数入口,使用ENDP标记函数结束。

EXPORT Reset_Handler [WEAK]

这里EXPORT声明Reset_Handler是一个全局性的。WEAK表示其他地方没有定义Reset_Handler函数时,就将此处作为Reset_Handler函数的实例。IMPORT用于指示如果在当前汇编代码中未找到其引用,则不导入该名称,很显然,下面有用到__main和SystemInit。

从上那些代码我就就知道,程序上电后,从0x08000000地址处加载SP,上电复位从0x08000004处加载PC,0x08000004处的地址就是复位函数的地址,然后复位函数里面先调用SystemInit函数来初始化系统的各种时钟,再调用__main函数(由编译器实现)。

复位函数中用到的Thumb-2指令介绍如下:

LDR从存储器中加载字到一个寄存器中

BL跳转到由寄存器/标号给出的地址,并把跳转前的下条指令地址保存到LR

BLX跳转到由寄存器给出的地址,并根据寄存器的LSE确定处理器的状态,还要把跳转前的下条指令地址保存到LR

BX跳转到由寄存器/标号给出的地址,不用返回

四、中断函数的弱(WEAK)声明

这里定义了各种中断函数,使用PROC表示函数开始,ENDP表示函数结束,EXPORT说明函数的全局性,WEAK说明如果其他地方没有定义这个函数,那么就把此处作为函数的实例。这里函数的代码都是B . 。这里的B表示跳转到一个标号,这里跳转到一个'.',即表示无限循环,所以我们在写C语言程序时如果没有写中断函数,那么对应的中断来了会运行这里到中断函数,即B .那么将无限循环在此。

ALIGN命令通过用零或空指令NOP填充,来使当前位置与一个指定的边界对齐。使用ALIGN来确保数据和代码对齐到适当的边界上。这里使用了默认为字对齐方式。

五、用户栈和堆初始化

栈和堆初始化部分,这里IF、ELSE、ENDIF是条件编译。

先判断是否定义了__MICROLIB ,如果定义了则赋予标号__initial_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局属性,可供外部文件调用,这样我们使用到molloc函数申请的空间就是从这里有关堆的两个标号之间的内存中申请的。如果没有定义(实际的默认情况就是我们没定义__MICROLIB)则通过 IMPORT __use_two_region_memory 表明使用双段模式,即一部分储存区用于栈空间,其他的存储区用于堆空间,堆区空间可以为0,但是,这样就不能调用malloc()内存分配函数。然后使用__user_initial_stackheap标号处的代码用于初始化用户堆栈,这部分由编译器提供的__main来调用。

END 命令指示汇编器,已到达一个源文件的末尾。

关于STM32单片机的Keil启动文件汇编代码就讲解完了,大家有没有看明白呢,欢迎评论交流,如果觉得我这篇文章写到很好到话,就转发出去分享给更多到朋友吧。最后欢迎大家点赞评论转发收藏,跟多好文章欢迎关注我——单片机嵌入式爱好者

相关问答

keil程序 怎么载入 单片机 ?

keil程序载入单片机,先要将烧录器联接上电脑,然后打开keil软件,对应单片机型号,将单片机插到烧录器上,打开单片机程序通过烧录器载入单片机程序。keil程序载...

单片机keil 51编程方法?

1.先安装KeilC51并破解,再安装MDK5并破解。2.分别打开KeilC51和MDK5的安装目录。将KeilC51安装目录下的C51文件夹复制到MDK5的安装目录文件夹下。3.分别...

怎么把 keil 上的 程序 烧录在 单片机 ?

1首先需要将程序编译成HEX文件。2然后将单片机连接到电脑上,选择合适的烧录工具,如ST-Link或J-Link等。3打开烧录工具软件,选择HEX文件和单片机型号。4点...

单片机 :在 keil 里从新建工程——书写 程序 ——编译——生成HEX...

第1步:打开KeiluVision2/3第2步:新建工程project->newproject将弹出crea...很多单片机,上面可能没有列举,我们任意选一个即可,51单片机选AT...

怎么在 单片机 编程中输入单引号-ZOL问答

在单片机编程软件KEIL中,请注意输入法。楼上的你不识字?人家都说了不要汇编的.到哪儿copy的?哥们咋这么偷懒啊?如果真想偷懒的话你得把悬赏分弄高点,像你...

keilc51怎么讲 程序 下载到 单片机 中?

新手呀,不过没关系,每个人都这样过来的首先在keilc51中把程序写好,编译成hex,你如果是用地isp把hex文件下载到单片机里,当然具体的东西是不一样的,具体的...

51 单片机 怎么用 keil 编写俩c 程序 合成一个hex文件?

.c是C语言源程序,具体的代码;.h是C语言头文件,宏定义一些函数、变量;.HEX是生成的十六进制文件,烧写文件,最终下载到单片机的;.UV2是工程文件,用....

keil 怎么烧录代码到 单片机 上?

在Keil软件中开发完成程序后,需要将程序烧录到目标单片机上才能使其运行。常用的烧录方法包括使用外部编程器和使用Keil自带的烧录工具ULINK等。以使用ULINK...

KEIL 可以用作哪些 单片机 的开发(除了51 单片机 )?

KEIL有不同版本,分别支持51和ARM,至于MIPS架构的单片机,一般厂家自己提供开发平台,例如Microchip。KEIL有不同版本,分别支持51和ARM,至于MIPS架构的单片机,一...

51 单片机 用的是哪个软件?

51单片机的编译软件现在最流行的是keilc51,你可以用这个软件进行编程和编译。单片机自身完成不了什么任务需要有外电路的配合,所以你需要购买一块51单片机开...

猜你喜欢