STM32 启动代码 __main 与用户主程序 main() 的区别

发布者:GoldenEclipse最新更新时间:2019-04-01 来源: eefocus关键字:STM32  启动代码  用户主程序 手机看文章 扫描二维码
随时随地手机看文章

1、__main 作用

__main函数是C/C++运行时库的一个函数,嵌入式系统在进入应用主程序之前必须有一个初始化的过程,使用__main标号引导系统时必须将应用程序的入口定义为main()。
    
在初始化的过程中,__main函数的作用主要有两点:


1) 完成对映像文件的初始化操作

a、映像文件

链接器把多个目标文件链接成一个映像文件。

b、加载地址和执行地址

映像文件可以有两种地址:加载地址和执行地址。

加载地址是映像文件在存储器中的存储地址;执行地址就是映像文件运行时的地址。

c、加载域和执行域

文件加载的存储区叫加载域,文件运行的存储区叫执行域。

d、从加载地址到执行地址

在结构比较简单的系统中,加载地址就是执行地址;

而在复杂系统中,程序运行前,常常会把映像文件的一部分或全部从存储区域移出去,此时执行地址就不再是加载地址。

知道以上几个概念,__main函数对映像文件的初始操作就不难理解了。

对于加载地址和执行地址不同的映像文件,__main函数会把加载地址的代码和数据复制到执行地址中,并且对被链接器指定为需要初始化为0的段,进行清零操作。


2) 调用__rt_entry函数,进入用户程序。

__rt_entry函数的运行流程如图:



 2、进入主程序

当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序,即呼叫主应用程序。

最简单的一种情况是:

IMPORT mainB main

直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。

在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。

IMPORT __mainB __main


__main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()。


所以说,前者是库函数,后者就是我们自己编写的main()主函数;


因此我们用的B __main其实是执行库函数,然后该库函数再调用我们的main() 函数,因此在单步调试时会看到先要跑一段程序(其实是库函数),然后再单步到我们自己的main函数(这个同时也说明如果有B __main 则就对应必须有main函数,否则编译出错);


如果我们用 B main来进入我们的主函数的话,那在单步调试时就看到直接进入到我们自己的main函数了,中间不会看到其他程序;


那么用B __main和用B main 这两这进入我们的main函数方式有什么不同呢?

如果采用前者则会由编译器加入一段"段拷贝"程序,即我们说的从加载域到执行域转化程序;而采用后者就没有这个了;


因此如果要进行 "段拷贝"只能自己动手编写程序来实现了,完成段拷贝后就可以进入我们的主函数了,当然这个主函数不一定是叫做main(),可以起个其他好听的名字,这个有别于使用B __main方式;不管采用哪种方式进入我们的程序,都要有一段"段拷贝"程序,跑完了段拷贝后才能可以进入我们主程序了。


startup.s 这个文件并没有所谓的"段拷贝"功能。


对含有启动程序来说,"执行地址与加载地址相同"不容易实现:


如果执行地址与加载地址相同哪当然不需要做"段拷贝",但是个人理解编译器还会加入"段拷贝"程序(如果用B __main 的话),只是因为条件不满足而不执行而已;但是对含有启动程序来说,"执行地址与加载地址相同"就不容易了.因为启动程序是要烧到非易失存储器里,用来在上电执行的,而这个程序必定会有RW段,如果RW放在非易失存储器,如FLASH,那就不好实现RW功能了,因此要给RW移动到能够实现RW功能的存储器,如SRAM等.因此,对含有启动程序来说,"执行地址与加载地址相同"就不容易实现;程序的入口点在C 库中的__main 处,在该点,库代码执行以下操作:

1)将非零(只读和读写)运行区域从其载入地址复制到运行地址。

2)清零ZI 区域。

3)跳转到__rt_entry。


关键字:STM32  启动代码  用户主程序 引用地址:STM32 启动代码 __main 与用户主程序 main() 的区别

上一篇:STM32 启动代码汇编指令详解
下一篇:STM32 分散加载文件 .sct 解析

推荐阅读最新更新时间:2024-03-16 16:26

基于STM32的uCGUI移植和优化
  首先在开始这个说明之前,要简要说明下具体的环境:   编译工具:MDK4.20   开发板:安富莱v2版开发板   调试器:JLink v8盗版 移植篇   相信大家有移植经验的都知道,移植确实是一件非常墨迹的事情,怎么说呢,代码都是别人的,风格也是别人的,文件结构,定义之类都是别人的,看别人的东西是种进步,但是,也是一个痛苦的过程,因为有时候资料确实很少,而且有时候还是E文的,专业名词一大堆,我们根本没有办法想象工作量是多么的巨大.   不过事情都是这样,你不懂他的时候他就像是巨山,但是一旦你理解他的时候,你才会感觉到原来他是那么的简单(从我的经验上来看,至少应该是这样的).   好吧,闲话少说,我们就来开始我们的移植之
[单片机]
关于stm32的GPIO的操作
首先先了解一下输出的模式 比较常用的是 推挽输出 1)GPIO_Mode_AIN 模拟输入 (2)GPIO_Mode_IN_FLOATING 浮空输入 (3)GPIO_Mode_IPD 下拉输入 (4)GPIO_Mode_IPU 上拉输入 (5)GPIO_Mode_Out_OD 开漏输出 (6)GPIO_Mode_Out_PP 推挽输出 (7)GPIO_Mode_AF_OD 复用开漏输出 (8)GPIO_Mode_AF_PP 复用推挽输出 首先简述一下stm3的gpio 接口(interface):主机(CPU)与外部设备(指MCU片上外设)之间缓冲电路。它用于完成主机与外部设备设间速
[单片机]
STM32(6) STM32时钟系统精讲(正点原子)
讲解内容: 时钟系统框图 时钟配置相关函数 参考资料 《STM32F4开发指南库函数版本》4.3小节STM32F4时钟系统 《STM32F4中文参考手册》第六章 复位和时钟系统 先看开发指南4.3小节的时钟树 时钟框图在中文参考手册的6.2小节,STM32的时钟系统还是很复杂的,为什么ARM的时钟系统要做的这么复杂,采用 多时钟源, 时钟频率越高功耗 越高。 F4与F1类似也有5个时钟来源 1 LSIRC 低速 的内部时钟 2 LSEOSC 低速的外部时钟 3 HSIRC 高速的内部时钟 4 PLLCLK 锁相环时钟输出 5 HSEOSC 也是一个很重要的时钟源,也是我们最常用的
[单片机]
<font color='red'>STM32</font>(6) <font color='red'>STM32</font>时钟系统精讲(正点原子)
STM32常用数据类型及其取值范围
带符号数: typedef signed char int8_t; // -128~127 typedef signed short int int16_t; // -32768~32767 typedef signed int int32_t; // -2147483648~2147483647 typedef signed __INT64 int64_t; //...... 无符号数: typedef unsigned char uint8_t; // 0~255 typedef unsigne
[单片机]
基于STM32的超声波HC-SR04详解
HC-SR04基本工作原理: (1)采用IO口TRIG触发测距,给最少10us的高电平信呈。 (2)模块自动发送8个40khz的方波,自动检测是否有信号返回; (3)有信号返回, 通过IO口ECHO输出一个高电平, 高电平持续的时间就是超声波从发射到返回的时间。 测试距离=(高电平时间*声速(340M/S))/2。 程序编写思路是: 1、配置好使用到的GPIO以及定时器; 2、给模块TRIG端口发送大于10us的高电平信号,当收、收到ECHO回响信号是,打开定时器开始定时; 3、当回响信号消失,关闭定时器; 4、通过定时器定时时间来确定距离。 连线 1.这里,HC-SR04模块必须使用5V供电,不能是3.3V (若
[单片机]
基于<font color='red'>STM32</font>的超声波HC-SR04详解
stm32 I2C问题 I2C_CheckEvent()
STM32的IIC接口写的比较复杂,稍不注意,很多地方都会搞错,如果是用GPIO模拟的IIC,问题应该不大,无非应答不不应答的问题。 但是既然STM32自带IIC,并且有接口函数,干嘛不用呢 问题1: I2C_CheckEvent() 检查时停在此处: /* Test on EV5 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); 问题2: 第一次读写正确后再运行程序停在此处: /* Test on EV6 and clear it */ while(!I2C_CheckEvent
[单片机]
STM32程序移植的一些注意方法
一,步骤 建立需要的子函数及将对应的函数在所有子函数之前声明。另一种方法是:新建一个子函数(.c)文件和头文件库(.h)文件,分别保存在用户文件夹中(这个文件夹是主函数所在的文件夹),将刚刚保存的子函数文件添加到程序文件夹中(保存在主函数所在的文件夹并不表示包含到了函数里,因此要在编程软件(即开发环境)添加,添加方法详见http://blog.csdn.net/ambizxzh/article/details/74324288) 移植的方法也可以参照上面这个链接。 二,注意 1,在配置子函数文件时,当出现多个结构体时结构体要在任何结构体元素赋值之前,也就是说对于A结构体,它必须放在A结构体、B结构体、C…等结构体的结构体元
[单片机]
在单片机上实现动态加载功能
本项目是一个在单片机(如:STM32)上实现动态加载功能的函数库,与Windows中的dll,Linux中的so类似,可以将代码动态地从其他的存储介质,动态加载到RAM中。 软件架构 本项目文件夹有三个,其中common存储了用于生成可重定位的.axf文件的工程与动态加载器工程交互用的函数,src提供动态加载器的源码,rel_axf_project_template提供了一个简单的可重定位的.axf文件的工程示例,example.c是一个简单的使用示例,所有文件的主要功能如下: /common/dl_extern_lib.h 描述了app程序用于调用host程序的函数向量表的基地址,以及相关的一些宏定义 /common/dl_
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved