stm32启动过程、cortex-m3架构、堆栈代码位置、编译汇编链接分析

16 篇文章 3 订阅

一、 寄存器、架构、工作流程

1.这里以stm32的架构:cortex-m3(也即ARMv7)的寄存器的作用、在指令取,指令的译码,指令的执行在其中的作用以及是如何配合实现代码的执行的

哈佛结构和冯诺依曼结构是如何体现的?

编译后的代码为什么分为code、堆、栈、bss、data、符号等部分,分别存储在哪些地方?

首先看寄存器

寄存器分为寄存器组,和外设寄存器
寄存器组对用户是不可见的(用户不能直接操作),它是CPU处理数据时需要调用的
外设寄存器对用户是可见的(用户可以通过地址的映射来对寄存器操作,从而控制相应的外设动作)

在这里插入图片描述
上面的是寄存器组,是在Cortex-M3中用来临时存放运算的数据或者指示程序运行的位置

1、 寄存器架构

参考官方Cortex-M3权威指南

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

二 、总线与各个部件之间的关系(主要是I-Code Bus、D-Code Bus、System Bus)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

上图所示的指令总线与数据总线与CM3内部的数据总线和数据总线不一样,具体可以看下面Cortex-M3内部结构图,而且上图的两个总线共用一个总线矩阵,就导致取指令和访问数据不能同时访问flash(在CM3里,指令和数据都是存放在flash中,部分数据存放在SRAM中,具体可以看指令和数据在stm32中存放的结构那一节),

而CM3外部总线部分是有芯片产商来决定,所以stm32的指令和数据总线是由意法半导体来设计制造的,具体架构如下图所示,可以看到该架构,指令和数据总线并没有共用一个总线矩阵,数据总线和系统总线是共用总线矩阵,从Cortex-M3的地址映射关系(从存储器映射来看stm32架构(内存与外设)那一节和指令和数据在stm32中存放的结构那一节),可以知道数据总线是是访问flash中的程序编译好后生成的常量数据(RO-data ),系统总线是访问SRAM中程序运行过程中产生的动态数据(ZI-data、)
RW-data在程序复位启动时是数据总线访问然后RW-data被复制到SRAM中通过系统总线访问

因为静态数据和动态数据不会同时访问,所以不用设计成可以同时访问的,也就可以共用一个总线矩阵
而读取指令和访问数据是可以同时进行的,所以就没有共用一个总线矩阵,这里就体现了哈佛结构的特性,指令与数据可以同时访问

下面这个是32官方参考手册,才是我们真正使用stm32的MCU具备的系统结构

在这里插入图片描述

体系结构:哈佛结构与冯诺依曼结构的区别

上述I-code与D-code与flash的连接就体现了哈佛结构的特性:
在这里插入图片描述
CPU采用的是哈佛结构还是冯诺依曼结构?

复位有三种启动方式,从哪里开始启动、启动代码的所在的位置

复位后启动的位置都是从内存地址为0x00000000的位置开始,如下图:
在这里插入图片描述
但是根据boot引脚的硬件配置,有三种不同的硬件映射方式,将处于内存(Flash、system memery、SRAM)中不同地址(参考上面存储器映射)的启动代码,映射到0x00000000这个位置;

在这里插入图片描述

一般都是将位于Flash的启动代码映射到0地址的位置,这个时候,取指令是通过ICode总线,取数据靠的是DCode和System 总线,通过不同的总线和总线矩阵,以及流水线的实现,可以实现指令和数据的同时访问,这样就体现了哈佛结构;

三、从存储器映射来看stm32架构(内存与外设)

STM32架构相关

内存的映射就是数据和代码实际存放的地址

外设映射的地址其实是控制该外设的寄存器的地址

整个架构说的就是处理核心、内存与外设寄存器它们的关系,是怎么通过各种总线连接起来的

如果取指令和访问数据能同时进行,那么这种总线连接的关系,就可以称之为哈佛结构;

存储器地址映射如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上三图都是在 Cortex-M3权威指南 中定义的,说明该架构的这些存储器映射是有arm定义好的,不是由芯片生产厂商来定义

下图为 stm32中文参考手册中定义的,这具体的就是有芯片生产厂家来定义的,主要介绍了code区、SRAM、片上外设(编程时主要就是用这里的地址来控制相应的模块)
在这里插入图片描述

由上四图可知,外设分为片上外设(USART、GPIO等模块)、片外外设(这个需要自己扩展)、核内外设(中断NVIC、路径跟踪TPIU等、调试接口SWJ,它并不是调试组件),这里的核内是相对于Cortex-M3,在它内部的外设叫核内外设(用户程序没有权限访问);
下图的附加调试组件与再下一个图的外部私有外设总线相连
在这里插入图片描述

Cortex-M3内部还有一个核心CM3Core,它周围的外设叫核内外设(调试系统(调试组件)为核内外设,核内外设是由arm公司设计的); CM3Core和核内外设组成Cortex-M3;
Cortex-M3周围的外设叫片上外设;
Cortex-M3和片上外设组成stm32芯片;
在stm32外部自己在通过PCB添加外设组件叫片外外设; 如下图所示:
库函数介绍,STM32采用的是CORTEX-M3的内核,内核是ARM公司设计的处理器体系结构,ST公司负责设计的是内核之外的部件,被称为核外外设,片上外设,设备外设

Cortex-M3模块结构图:

在这里插入图片描述
在这里插入图片描述

上述两张图第一张是CM3Core和核内外设(包括调试组件,调试组件分为核内和核外调试组件,核内调试组件由arm公司设计,核外调试组件由st公司设计),主要由ARM设计;
第二张图除了CM3Core和核内外设组成的Cortex-M3,以及Trace controller(调试组件),其它的是芯片制造公司设计制造的
抽象图如下两张图所示:
在这里插入图片描述
下图的调试系统应该也属于Cortex-M3内核中的一部分,由arm设计的应该是属于核内外设(在图4G地址空间划分中block 7 属性中有说)
在这里插入图片描述stm32 调试系统知识
添加链接描述
在这里插入图片描述
提示:Cortex-M3内核内含的硬件调试模块是ARM CoreSight开发工具集的子集。

ARM Cortex-M3内核提供集成的片上调试功能。它由以下部分组成:

SWJ-DP:串行/JTAG调试端口
AHP-AP: AHB访问端口
ITM:执行跟踪单元
FPB:闪存指令断点
DWT:数据触发
TPUI:跟踪单元接口(仅较大封装的芯片支持)
ETM:嵌入式跟踪微单元(在较大的封装上才有支持此功能的引脚),专用于STM32F1的调试特性
灵活的调试引脚分配
MCU调试盒(支持低电源模式,控制外设时钟等)

四、从CM3内核架构来看CPU流水线–分析Cortex-M3内核架构

从上一节我们了解到Cortex-M3的内核CM3,它就相当于日常电脑的CPU,架构图如下:Processor Core System那部分

在这里插入图片描述
上图的Register Bank又可以分为以下:
在这里插入图片描述
ARM CPU的组成
ARM——Cortex系列体系结构
在这里插入图片描述

三级流水线
在这里插入图片描述
在这里插入图片描述
ARM Cortex-A8体系结构
在这里插入图片描述
在这里插入图片描述
流水线中出现的三个相关:
1、数据相关:是指令在流水bai线中du重叠执行时,当后继指令需要用到zhi前面的指令产生的结果dao时发生的。
2、控制相关:是当流水线遇到转移指令引起的。统计表明,转移指令约占总指令的四分之一左右,比起数据相关,它会使流水线丧失更多的功能。
3.结构相关:多条指令进入流水线后在同一机器周期内争用同一功能部件所发生的冲突。

使用五级指令流水的优点:
1.并行性更好
2.周期:机器周期可以设置地更短、时钟周期也更短、主频更高

在这里插入图片描述
具体为以下5级
在这里插入图片描述
从上面指令译码那个可以看到与下一节x86CPU组成不一样的在于,x86CPU还有一个操作控制器OC,而arm相当于是在译码器包含了这个

五、 stm32(ARM CPU)与x86 CPU架构组成的异同点

x86CPU组成:
CPU的内部架构和工作原理
在这里插入图片描述
cpu架构一
在这里插入图片描述

这里的AC累加器、缓冲寄存器和ARM里的通用寄存器R0、R1是一个作用

在这里插入图片描述

ARM

在这里插入图片描述

六、stm32中指令、汇编语言、机器码

在这里插入图片描述
CPU的内部架构和工作原理

根据操作数的地址是在寄存器还是在内存还是立即数,将这些数据传输类型分为以下几种:

1、Cortex-CM3中的数据传输类型

1)、两个寄存器间的传输数据。MOV

2)、寄存器与存储器间传输数据。LDR、STR

3)、寄存器与特殊功能寄存器间传输数据。

4)、把一个立即数加载到寄存器。MOV

STM32学习之路入门篇之指令集及cortex——m3的存储系统

ARM 反汇编基础(六)(Thumb 汇编指令集)

可以通过命令行来汇编与反汇编

在这里插入图片描述

ARM-CPU原理,基于ARM的SOC讲解
在这里插入图片描述
Thumb/Thumb2指令集
[ARM-Cortex-M3/4] 机器码、汇编与反汇编
arm汇编指令与机器码对应分析
ARM汇编指令集与机器码
在这里插入图片描述

看下面这个例子:
在这里插入图片描述
LED0=0这条C指令编译器把它转换成了3条汇编指令MOVS、LDR、STR,这三句汇编分别对应的机器码就是2000、490B、6008。
图中,0x08。。。是CODE地址,然后该地址开始的机器码,最后就是这个机器对应的汇编语句。
F04F0001是对应汇编的机器码,你不用关心它的长度,有兴趣的可以去查汇编指令表,其中有对应机器码的格式

上面的机器码对三种操作数的寻址:寄存器(R0这些)、内存(flash等)、立即数
对立即数寻址就是确定该立即数的大小,

在这里插入图片描述
详细参考如下文章:
ARM体系架构总结

首先我们分析下上面的例子:

490B LDR R1,[PC,#44]
490A LDR R1,[PC,#40]

这两的机器码为啥只差了1,而立即数差了4

这是因为立即数寻址是有8位图决定的,不是直接映射数值的

在这里插入图片描述在这里插入图片描述
我们可以看到它是将机器码的0-4位乘以4得到最终的立即数
0xb=11,114=44
0xa=10,10
4=40

在这里插入图片描述
参考
ARM指令计算机器码,自己归纳整理的ARM THUMB指令机器码表

32位指令的寻址方式如下:
ARM立即寻址中有效立即数的计算
在这里插入图片描述

为什么要通过这种方式来寻址:

寄存器寻址可以直接控制寄存器,在机器码中所需的位数不多,够用
内存中寻址,如果是32位的那地址就有0-2的32次方,32位指令的话不够用,但这里就可以采用【】的方式间接寻址
而立即数就没有办法通过直接和间接来,32位指令中间除去操作码,剩下的位数根本覆盖不了0-2的32次方,所有采用了位图加循环右移的方式

详细如下:
ARM指令中如何判断一个立即数是有效立即数

在这里插入图片描述

七、指令和数据在stm32中存放的结构、堆栈大小及位置

这一节应该和**从存储器映射来看stm32架构(内存与外设)**这一节结合来看
在这里插入图片描述
上面的代码区(Block0)又细分为以下:

在这里插入图片描述
下面左边是物理区分,右边常规字体是以st公司专有划分,右边粗体是以代码角度来区分

Flash =Code + RO-Data + RW-Data(.data);

SRAM = RW-data+ZI-data(.bss+heap+stack) ;

.bss:未初始化的全局变量和静态变量(包括全局和局部)
heap:申请的动态内存(如果代码中没有动态申请内存map文件中是没有heap定义的
在freertos中molloc函数申请的动态内存有4种方式,一般用的是heap_4.c文件中的,定义一个静态数组)
stack:局部变量都是放在栈中
在这里插入图片描述
STM32内存管理以及堆和栈的理解

说说STM32的堆栈与内存

下面是附件代码生成的map图

==============================================================================

Memory Map of the image

  Image Entry point : 0x08000131

  Load Region LR_IROM1 (Base: 0x08000000, Size: 0x0000293c, Max: 0x00010000, ABSOLUTE)

    Execution Region ER_IROM1 (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x00002904, Max: 0x00010000, ABSOLUTE)

    Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x08000000   0x08000000   0x00000130   Data   RO          231    RESET               startup_stm32f10x_hd.o   
    ##RESET是startup_stm32f10x_hd.s文件中AREA 定义的一块名为RESET的中断向量表数据
    ##下面的Code RO 都是代码段,.text是启动文件中AREA定义代码,其它是库代码段
    0x08000130   0x08000130   0x00000000   Code   RO         3423  * .ARM.Collect$$$$00000000  mc_w.l(entry.o)
    0x08000130   0x08000130   0x00000004   Code   RO         3738    .ARM.Collect$$$$00000001  mc_w.l(entry2.o)
    0x08000134   0x08000134   0x00000004   Code   RO         3741    .ARM.Collect$$$$00000004  mc_w.l(entry5.o)
    0x08000138   0x08000138   0x00000000   Code   RO         3743    .ARM.Collect$$$$00000008  mc_w.l(entry7b.o)
    0x08000138   0x08000138   0x00000000   Code   RO         3745    .ARM.Collect$$$$0000000A  mc_w.l(entry8b.o)
    0x08000138   0x08000138   0x00000008   Code   RO         3746    .ARM.Collect$$$$0000000B  mc_w.l(entry9a.o)
    0x08000140   0x08000140   0x00000000   Code   RO         3748    .ARM.Collect$$$$0000000D  mc_w.l(entry10a.o)
    0x08000140   0x08000140   0x00000000   Code   RO         3750    .ARM.Collect$$$$0000000F  mc_w.l(entry11a.o)
    0x08000140   0x08000140   0x00000004   Code   RO         3739    .ARM.Collect$$$$00002712  mc_w.l(entry2.o)
    0x08000144   0x08000144   0x00000024   Code   RO          232    .text               startup_stm32f10x_hd.o
    0x08000168   0x08000168   0x00000062   Code   RO         3426    .text               mc_w.l(ldiv.o)
    0x080001ca   0x080001ca   0x00000064   Code   RO         3689    .text               mf_w.l(fmul.o)
    0x0800022e   0x0800022e   0x0000007c   Code   RO         3691    .text               mf_w.l(fdiv.o)
    0x080002aa   0x080002aa   0x0000014e   Code   RO         3693    .text               mf_w.l(dadd.o)
    0x080003f8   0x080003f8   0x000000e4   Code   RO         3695    .text               mf_w.l(dmul.o)
    0x080004dc   0x080004dc   0x000000de   Code   RO         3697    .text               mf_w.l(ddiv.o)
    0x080005ba   0x080005ba   0x00000022   Code   RO         3699    .text               mf_w.l(dflti.o)
    0x080005dc   0x080005dc   0x0000001a   Code   RO         3701    .text               mf_w.l(dfltui.o)
    0x080005f6   0x080005f6   0x00000028   Code   RO         3703    .text               mf_w.l(ffixui.o)
    0x0800061e   0x0800061e   0x00000026   Code   RO         3705    .text               mf_w.l(f2d.o)
    0x08000644   0x08000644   0x00000038   Code   RO         3707    .text               mf_w.l(d2f.o)
    0x0800067c   0x0800067c   0x00000014   Code   RO         3709    .text               mf_w.l(cfrcmple.o)
    0x08000690   0x08000690   0x00000062   Code   RO         3755    .text               mc_w.l(uldiv.o)
    0x080006f2   0x080006f2   0x0000001e   Code   RO         3757    .text               mc_w.l(llshl.o)
    0x08000710   0x08000710   0x00000024   Code   RO         3759    .text               mc_w.l(llsshr.o)
    0x08000734   0x08000734   0x00000000   Code   RO         3768    .text               mc_w.l(iusefp.o)
    0x08000734   0x08000734   0x0000006e   Code   RO         3769    .text               mf_w.l(fepilogue.o)
    0x080007a2   0x080007a2   0x000000ba   Code   RO         3771    .text               mf_w.l(depilogue.o)
    0x0800085c   0x0800085c   0x0000002e   Code   RO         3773    .text               mf_w.l(dscalb.o)
    0x0800088a   0x0800088a   0x00000002   PAD
    0x0800088c   0x0800088c   0x00000030   Code   RO         3777    .text               mf_w.l(cdrcmple.o)
    0x080008bc   0x080008bc   0x00000024   Code   RO         3779    .text               mc_w.l(init.o)
    0x080008e0   0x080008e0   0x00000020   Code   RO         3781    .text               mc_w.l(llushr.o)
    0x08000900   0x08000900   0x000000a2   Code   RO         3783    .text               mf_w.l(dsqrt.o)
    0x080009a2   0x080009a2   0x00000004   Code   RO          144    i.BusFault_Handler  stm32f10x_it.o
    0x080009a6   0x080009a6   0x00000002   Code   RO          145    i.DebugMon_Handler  stm32f10x_it.o
    0x080009a8   0x080009a8   0x00000116   Code   RO          243    i.GPIO_Init         stm32f10x_gpio.o
    0x08000abe   0x08000abe   0x00000004   Code   RO          251    i.GPIO_SetBits      stm32f10x_gpio.o
    0x08000ac2   0x08000ac2   0x00000004   Code   RO          146    i.HardFault_Handler  stm32f10x_it.o
    0x08000ac6   0x08000ac6   0x00000002   PAD
    0x08000ac8   0x08000ac8   0x00000038   Code   RO         3164    i.IIC_Ack           myiic.o
    0x08000b00   0x08000b00   0x00000038   Code   RO         3165    i.IIC_Init          myiic.o
    0x08000b38   0x08000b38   0x00000038   Code   RO         3166    i.IIC_NAck          myiic.o
    0x08000b70   0x08000b70   0x00000058   Code   RO         3167    i.IIC_Read_Byte     myiic.o
    0x08000bc8   0x08000bc8   0x00000060   Code   RO         3168    i.IIC_Send_Byte     myiic.o
    0x08000c28   0x08000c28   0x00000038   Code   RO         3169    i.IIC_Start         myiic.o
    0x08000c60   0x08000c60   0x00000034   Code   RO         3170    i.IIC_Stop          myiic.o
    0x08000c94   0x08000c94   0x0000004c   Code   RO         3171    i.IIC_Wait_Ack      myiic.o
    0x08000ce0   0x08000ce0   0x00000050   Code   RO         2906    i.LED_Init          led.o
    0x08000d30   0x08000d30   0x00000004   Code   RO          147    i.MemManage_Handler  stm32f10x_it.o
    0x08000d34   0x08000d34   0x00000002   Code   RO          148    i.NMI_Handler       stm32f10x_it.o
    0x08000d36   0x08000d36   0x00000002   PAD
    0x08000d38   0x08000d38   0x00000070   Code   RO          551    i.NVIC_Init         misc.o
    0x08000da8   0x08000da8   0x00000014   Code   RO          552    i.NVIC_PriorityGroupConfig  misc.o
    0x08000dbc   0x08000dbc   0x00000002   Code   RO          149    i.PendSV_Handler    stm32f10x_it.o
    0x08000dbe   0x08000dbe   0x00000002   PAD
    0x08000dc0   0x08000dc0   0x00000020   Code   RO          355    i.RCC_APB2PeriphClockCmd  stm32f10x_rcc.o
    0x08000de0   0x08000de0   0x000000d4   Code   RO          363    i.RCC_GetClocksFreq  stm32f10x_rcc.o
    0x08000eb4   0x08000eb4   0x00000020   Code   RO         3172    i.SDA_IN            myiic.o
    0x08000ed4   0x08000ed4   0x00000024   Code   RO         3173    i.SDA_OUT           myiic.o
    0x08000ef8   0x08000ef8   0x00000002   Code   RO          150    i.SVC_Handler       stm32f10x_it.o
    0x08000efa   0x08000efa   0x00000008   Code   RO         2862    i.SetSysClock       system_stm32f10x.o
    0x08000f02   0x08000f02   0x00000002   PAD
    0x08000f04   0x08000f04   0x000000e0   Code   RO         2863    i.SetSysClockTo72   system_stm32f10x.o
    0x08000fe4   0x08000fe4   0x00000028   Code   RO          555    i.SysTick_CLKSourceConfig  misc.o
    0x0800100c   0x0800100c   0x00000002   Code   RO          151    i.SysTick_Handler   stm32f10x_it.o
    0x0800100e   0x0800100e   0x00000002   PAD
    0x08001010   0x08001010   0x0000003c   Code   RO         3320    i.SysTick_Init      systick.o
    0x0800104c   0x0800104c   0x00000060   Code   RO         2865    i.SystemInit        system_stm32f10x.o
    0x080010ac   0x080010ac   0x0000003c   Code   RO         3349    i.USART1_IRQHandler  usart.o
    0x080010e8   0x080010e8   0x000000b0   Code   RO         3350    i.USART1_Init       usart.o
    0x08001198   0x08001198   0x00000040   Code   RO         3379    i.USART2_IRQHandler  rs485.o
    0x080011d8   0x080011d8   0x00000012   Code   RO         1189    i.USART_ClearFlag   stm32f10x_usart.o
    0x080011ea   0x080011ea   0x00000018   Code   RO         1193    i.USART_Cmd         stm32f10x_usart.o
    0x08001202   0x08001202   0x0000001a   Code   RO         1196    i.USART_GetFlagStatus  stm32f10x_usart.o
    0x0800121c   0x0800121c   0x00000054   Code   RO         1197    i.USART_GetITStatus  stm32f10x_usart.o
    0x08001270   0x08001270   0x0000004a   Code   RO         1199    i.USART_ITConfig    stm32f10x_usart.o
    0x080012ba   0x080012ba   0x00000002   PAD
    0x080012bc   0x080012bc   0x000000d8   Code   RO         1200    i.USART_Init        stm32f10x_usart.o
    0x08001394   0x08001394   0x0000000a   Code   RO         1207    i.USART_ReceiveData  stm32f10x_usart.o
    0x0800139e   0x0800139e   0x00000008   Code   RO         1210    i.USART_SendData    stm32f10x_usart.o
    0x080013a6   0x080013a6   0x00000004   Code   RO          152    i.UsageFault_Handler  stm32f10x_it.o
    0x080013aa   0x080013aa   0x00000002   PAD
    0x080013ac   0x080013ac   0x00000020   Code   RO         3561    i.__0printf$5       mc_w.l(printf5.o)
    0x080013cc   0x080013cc   0x00000028   Code   RO         3725    i.__ARM_fpclassify  m_ws.l(fpclassify.o)
    0x080013f4   0x080013f4   0x000000aa   Code   RO         3727    i.__kernel_poly     m_ws.l(poly.o)
    0x0800149e   0x0800149e   0x00000002   PAD
    0x080014a0   0x080014a0   0x00000010   Code   RO         3711    i.__mathlib_dbl_divzero  m_ws.l(dunder.o)
    0x080014b0   0x080014b0   0x00000004   Code   RO         3713    i.__mathlib_dbl_infnan2  m_ws.l(dunder.o)
    0x080014b4   0x080014b4   0x0000000c   Code   RO         3714    i.__mathlib_dbl_invalid  m_ws.l(dunder.o)
    0x080014c0   0x080014c0   0x0000000e   Code   RO         3715    i.__mathlib_dbl_overflow  m_ws.l(dunder.o)
    0x080014ce   0x080014ce   0x00000002   PAD
    0x080014d0   0x080014d0   0x00000010   Code   RO         3717    i.__mathlib_dbl_underflow  m_ws.l(dunder.o)
    0x080014e0   0x080014e0   0x0000000e   Code   RO         3789    i.__scatterload_copy  mc_w.l(handlers.o)
    0x080014ee   0x080014ee   0x00000002   Code   RO         3790    i.__scatterload_null  mc_w.l(handlers.o)
    0x080014f0   0x080014f0   0x0000000e   Code   RO         3791    i.__scatterload_zeroinit  mc_w.l(handlers.o)
    0x080014fe   0x080014fe   0x00000002   PAD
    0x08001500   0x08001500   0x0000000c   Code   RO         3763    i.__set_errno       mc_w.l(errno.o)
    0x0800150c   0x0800150c   0x000002c0   Code   RO         3568    i._printf_core      mc_w.l(printf5.o)
    0x080017cc   0x080017cc   0x000001b0   Code   RO         3257    i.bmp280CompensateP  bmp280.o
    0x0800197c   0x0800197c   0x00000048   Code   RO         3258    i.bmp280CompensateT  bmp280.o
    0x080019c4   0x080019c4   0x00000088   Code   RO         3259    i.bmp280GetData     bmp280.o
    0x08001a4c   0x08001a4c   0x0000004c   Code   RO         3260    i.bmp280GetPressure  bmp280.o
    0x08001a98   0x08001a98   0x00000084   Code   RO         3261    i.bmp280Init        bmp280.o
    0x08001b1c   0x08001b1c   0x0000007c   Code   RO         3262    i.bmp280PressureToAltitude  bmp280.o
    0x08001b98   0x08001b98   0x0000004c   Code   RO         3321    i.delay_ms          systick.o
    0x08001be4   0x08001be4   0x0000004c   Code   RO         3322    i.delay_us          systick.o
    0x08001c30   0x08001c30   0x00000024   Code   RO         3351    i.fputc             usart.o
    0x08001c54   0x08001c54   0x00000060   Code   RO         3174    i.iicDevRead        myiic.o
    0x08001cb4   0x08001cb4   0x00000040   Code   RO         3175    i.iicDevReadByte    myiic.o
    0x08001cf4   0x08001cf4   0x00000030   Code   RO         3177    i.iicDevWriteByte   myiic.o
    0x08001d24   0x08001d24   0x000000a0   Code   RO            1    i.main              main.o
    0x08001dc4   0x08001dc4   0x000009d8   Code   RO         3414    i.pow               m_ws.l(pow.o)
    0x0800279c   0x0800279c   0x00000006   Code   RO         3263    i.presssureFilter   bmp280.o
    0x080027a2   0x080027a2   0x0000004c   Code   RO         3731    i.sqrt              m_ws.l(sqrt.o)
    0x080027ee   0x080027ee   0x00000002   PAD
    0x080027f0   0x080027f0   0x00000088   Data   RO         3415    .constdata          m_ws.l(pow.o)
    0x08002878   0x08002878   0x00000008   Data   RO         3729    .constdata          m_ws.l(qnan.o)
    0x08002880   0x08002880   0x00000064   Data   RO            2    .conststring        main.o
    0x080028e4   0x080028e4   0x00000020   Data   RO         3787    Region$$Table       anon$$obj.o


    Execution Region RW_IRAM1 (Exec base: 0x20000000, Load base: 0x08002904, Size: 0x00000498, Max: 0x00005000, ABSOLUTE)

    Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x20000000   0x08002904   0x00000014   Data   RW          383    .data               stm32f10x_rcc.o
    0x20000014   0x08002918   0x00000014   Data   RW         3265    .data               bmp280.o
    0x20000028   0x0800292c   0x00000004   Data   RW         3323    .data               systick.o
    0x2000002c   0x08002930   0x00000001   Data   RW         3381    .data               rs485.o
    0x2000002d   0x08002931   0x00000003   PAD
    0x20000030   0x08002934   0x00000004   Data   RW         3752    .data               mc_w.l(stdout.o)
    0x20000034   0x08002938   0x00000004   Data   RW         3764    .data               mc_w.l(errno.o)
    0x20000038        -       0x0000001c   Zero   RW         3264    .bss                bmp280.o
    0x20000054        -       0x00000040   Zero   RW         3380    .bss                rs485.o
    0x20000094   0x0800293c   0x00000004   PAD
    0x20000098        -       0x00000400   Zero   RW          229    STACK               startup_stm32f10x_hd.o


==============================================================================

上面分为两个部分:
怎么看

1.Load Region 范围(0x08000000 – 0x0800293c)
可以称之为加载域,加载的指令在这里,初始化时将RW-data从flash中加载到SRAM中

2.Execution Region 范围(0x20000000 – 0x20000098)
可以称之为执行域,在代码运行过程中,生成的中间变量和全局变量的修改都是在这个区域进行的(SRAM)
在这一部分我们可以看到在最左边执行地址的后面又Load Addr,这个地址就是当前地址上的数据是从哪里加载过来的,可以看到RW-data是从第一部分RO-data后面加载过来的

第二部分最后有STACK这个section,它是从0x20000098地址开始,然后在startup_stm32f10x_hd启动文件里定义了0x00000400大小的栈,所以在编译完后栈指针sp就等于0x00000498

同样可以在map文件 全局符号那块看到,如下图
在这里插入图片描述
下面为局部符号STACK,为栈底指针

在这里插入图片描述

      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   

      9920        616        580         56       1120     272362   Grand Totals
      9920        616        580         56       1120     272362   ELF Image Totals
      9920        616        580         56          0          0   ROM Totals
      ==============================================================================

    Total RO  Size (Code + RO Data)                10500 (  10.25kB)
    Total RW  Size (RW Data + ZI Data)              1176 (   1.15kB)
    Total ROM Size (Code + RO Data + RW Data)      10556 (  10.31kB)

==============================================================================

可以看到上述RW Data + ZI Data其实就是编译后加载到SRAM内的数据,转换为16进制就是0x00000498
ZI Data 在map文件中就是type为zero attr为RW的数据,也就是(.bss+heap+stack)

STM32 | map文件详解

在这里插入图片描述
下面看一下编程中实际字符串在flash中的位置,字符串属于RO-Data类型,.conststring
下面的printf打印的字符串就属于

在这里插入图片描述
在map文件局部符号中找到该段的地址,如下:
因为该段属于对象main.o对象,即在主函数中定义,所以选它,而不是最上面那个段
在这里插入图片描述

在keil中输入该地址,找到该地址,
在这里插入图片描述
上图框中前100个字节就是printf函数里的字符串,通过ascall表可以知道每个字节翻译过来加起来就代表了该字符串

上面是找了RO-data的地址位置
下面找一下RW-data和ZI-data的位置,根据之前的描述,他们分别是全局初始化变量和静态变量、未初始化的全局变量;
如下代码:

#include <math.h>
#include "stdbool.h"
#include "SysTick.h"
#include "myiic.h"
#include "bmp280.h"
#include "usart.h"

/*bmp280 气压和温度过采样 工作模式*/
#define BMP280_PRESSURE_OSR		  	(BMP280_OVERSAMP_8X)
#define BMP280_TEMPERATURE_OSR		(BMP280_OVERSAMP_16X)
#define BMP280_MODE					      (BMP280_PRESSURE_OSR<<2|BMP280_TEMPERATURE_OSR<<5|BMP280_NORMAL_MODE)


typedef struct 
{
    u16 dig_T1;                                                                /* calibration T1 data */
    s16 dig_T2;                                                                /* calibration T2 data */
    s16 dig_T3;                                                                /* calibration T3 data */
    u16 dig_P1;                                                                /* calibration P1 data */
    s16 dig_P2;                                                                /* calibration P2 data */
    s16 dig_P3;                                                                /* calibration P3 data */
    s16 dig_P4;                                                                /* calibration P4 data */
    s16 dig_P5;                                                                /* calibration P5 data */
    s16 dig_P6;                                                                /* calibration P6 data */
    s16 dig_P7;                                                                /* calibration P7 data */
    s16 dig_P8;                                                                /* calibration P8 data */
    s16 dig_P9;                                                                /* calibration P9 data */
    s32 t_fine;                                                                /* calibration t_fine data */
} bmp280Calib;

bmp280Calib  bmp280Cal;

static u8 bmp280ID=0;
static bool isInit=false;
static s32 bmp280RawPressure=0;
static s32 bmp280RawTemperature=0;

static void bmp280GetPressure(void);
static void presssureFilter(float* in,float* out);
static float bmp280PressureToAltitude(float* pressure/*, float* groundPressure, float* groundTemp*/);

bool bmp280Init(void)
{	
    if (isInit)
        return true;

	IIC_Init();		                                                           /*初始化I2C*/
  delay_ms(20);
	
	bmp280ID=iicDevReadByte(BMP280_ADDR,BMP280_CHIP_ID);	                   /* 读取bmp280 ID*/
	
	if(bmp280ID==BMP280_DEFAULT_CHIP_ID)
		printf("BMP280 ID IS: 0x%X\n",bmp280ID);
  else
    return false;

    /* 读取校准数据 */
  iicDevRead(BMP280_ADDR,BMP280_TEMPERATURE_CALIB_DIG_T1_LSB_REG,24,(u8 *)&bmp280Cal);	
	iicDevWriteByte(BMP280_ADDR,BMP280_CTRL_MEAS_REG,BMP280_MODE);
	iicDevWriteByte(BMP280_ADDR,BMP280_CONFIG_REG,5<<2);		               /*配置IIR滤波*/
		
    isInit=true;          
    return true;
}

static void bmp280GetPressure(void)
{
    u8 data[BMP280_DATA_FRAME_SIZE];

    // read data from sensor
    iicDevRead(BMP280_ADDR,BMP280_PRESSURE_MSB_REG,BMP280_DATA_FRAME_SIZE,data);
    bmp280RawPressure=(s32)((((uint32_t)(data[0]))<<12)|(((uint32_t)(data[1]))<<4)|((uint32_t)data[2]>>4));
    bmp280RawTemperature=(s32)((((uint32_t)(data[3]))<<12)|(((uint32_t)(data[4]))<<4)|((uint32_t)data[5]>>4));
}

// Returns temperature in DegC, resolution is 0.01 DegC. Output value of "5123" equals 51.23 DegC
// t_fine carries fine temperature as global value
static s32 bmp280CompensateT(s32 adcT)
{
    s32 var1,var2,T;

    var1=((((adcT>>3)-((s32)bmp280Cal.dig_T1<<1)))*((s32)bmp280Cal.dig_T2))>>11;
    var2=(((((adcT>>4)-((s32)bmp280Cal.dig_T1))*((adcT>>4)-((s32)bmp280Cal.dig_T1)))>>12)*((s32)bmp280Cal.dig_T3))>>14;
    bmp280Cal.t_fine=var1+var2;
	
    T=(bmp280Cal.t_fine*5+128)>>8;

    return T;
}

// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of "24674867" represents 24674867/256 = 96386.2 Pa = 963.862 hPa
static uint32_t bmp280CompensateP(s32 adcP)
{
    int64_t var1,var2,p;
    var1=((int64_t)bmp280Cal.t_fine)-128000;
    var2=var1*var1*(int64_t)bmp280Cal.dig_P6;
    var2=var2+((var1*(int64_t)bmp280Cal.dig_P5)<<17);
    var2=var2+(((int64_t)bmp280Cal.dig_P4)<<35);
    var1=((var1*var1*(int64_t)bmp280Cal.dig_P3)>>8)+((var1*(int64_t)bmp280Cal.dig_P2)<<12);
    var1=(((((int64_t)1)<<47)+var1))*((int64_t)bmp280Cal.dig_P1)>>33;
    if (var1==0)
        return 0;
    p=1048576-adcP;
    p=(((p<<31)-var2)*3125)/var1;
    var1=(((int64_t)bmp280Cal.dig_P9)*(p>>13)*(p>>13))>>25;
    var2=(((int64_t)bmp280Cal.dig_P8)*p)>>19;
    p=((p+var1+var2)>>8)+(((int64_t)bmp280Cal.dig_P7)<<4);
    return(uint32_t)p;
}

#define FILTER_NUM	5
#define FILTER_A	0.1f

/*限幅平均滤波法*/
static void presssureFilter(float* in,float* out)
{	
	*out=*in;

}

void bmp280GetData(float* pressure,float* temperature,float* asl)
{
    static float t;
    static float p;
	
	bmp280GetPressure();

	t=bmp280CompensateT(bmp280RawTemperature)/100.0;		
	p=bmp280CompensateP(bmp280RawPressure)/25600.0;	
	
	*temperature=(float)t;                                                     /*单位度*/                                                   
	presssureFilter(&p,pressure);                                              /*滤波后平均出压力单位hPa*/		
	*asl=bmp280PressureToAltitude(pressure);	                                 /*转换成海拔*/	
}

#define CONST_PF 0.1902630958	       //(1/5.25588f) Pressure factor
#define FIX_TEMP 25				           // Fixed Temperature. ASL is a function of pressure and temperature, but as the temperature changes so much (blow a little towards the flie and watch it drop 5 degrees) it corrupts the ASL estimates.
								                     // TLDR: Adjusting for temp changes does more harm than good.
/*
 * Converts pressure to altitude above sea level (ASL) in meters
*/
static float bmp280PressureToAltitude(float* pressure/*, float* groundPressure, float* groundTemp*/)
{
    if (*pressure>0)
    {
        return((pow((1015.7f/ *pressure),CONST_PF)-1.0f)*(FIX_TEMP+273.15f))/0.0065f;
    }
    else
    {
        return 0;
    }
}

其中的全局初始化变量有bmp280ID、islnit、bmp280RawPressure、bmp280RawTemperature、t、p
未初始化全局变量有bmp280Cal

在在map文件局部符号中找到该段的地址,如下:
在这里插入图片描述
首先在.data出就表明了该段的开始地址,该段总共有多大,其中我们看到数据只占了18字节,而一共占了20字节,还有两字节是pad,

然后在map文件的全局符号区,找到全局未初始化变量如下:
一共有28字节

在这里插入图片描述

通过上述分析我们知道该段代码中一共占了20+28字节:
在map文件的映像组大小部分可以看到统计,如下:
在这里插入图片描述

各种符号在编译后不存在内存中

在这里插入图片描述
C语言内存中是否存在一个区域,存储着变量的符号,变量的类型和变量的首地址?

变量名和内存地址及符号表

STM32启动文件分析(startup_stm32f10x_md.s)

启动文件里的具体语法和相关作用,这里我就不再分析,详情可以参考以下链接:
STM32启动文件分析(startup_stm32f10x_md.s)
STM32 标准库V3.5启动文件startup_stm32f10xxx.s分析

STM32F10x的启动汇编分析

指令:IMPORT、EXPORT、[WEAK]

IMPORT相当于C语言中的extern 用在调用第三方模块里的某个函数或变量前

在这里插入图片描述
在这里插入图片描述

EXPORT相当于声明某个符号量为全局,第三方模块可以调用

PendSV_Handler  PROC
                EXPORT  PendSV_Handler             [WEAK]
                B       .
                ENDP

[WEAK]指令会优先使用外部的链接符号,但是没有原型,编译器也不会出现报错
这里相当于在c语言源文件中声明了一个函数,但是没有函数的实现(实现不在该文件或则就没有实现),在该源文件中也可以调用,编译器不会报错(实际上在C语言中会报错)

这里主要说一下启动文件分为哪些模块,以及它们的作用:

启动文件下要对硬件负责,上要对软件负责

根据硬件的特性:
1.硬件复位启动时是从flash0x08000000开始读指令,所以要将程序的第一条指令存在该位置:这个是由MDK编译器来设置决定,如下:
在这里插入图片描述
2.硬件复位启动时会将0x08000000地址上的数据给栈指针sp,0x08000004地址的数据给pc,栈指针决定了所有数据在SRAM中的最大地址,所以0x08000000地址处应该是程序代码编译链接好后栈顶的位置
0x08000004处应该就是复位函数的地址
有一个中断函数表:
在这里插入图片描述
上图的两块伪指令代码,告诉编译器栈堆的大小,并在栈顶和堆顶定义了符号,用来指示栈顶和堆顶的位置,右下角为中断向量表前两个字内容;

PRESERVE8 指定当前文件保持堆栈八字节对齐。
THUMB 表示后面的指令是 THUMB 指令集 ,CM4 采用的是 THUMB - 2指令集

3.根据硬件和软件的特性,要将程序分为不同的section,将数据和指令code分开(因为硬件为哈佛结构,数据和指令通过不同的总线同时访问可以提升速度,加之编译器的通用标准编译都是喜欢将程序分为这些section以便具有更好的通用性)通过编译链接后生成的内存映射map文件,可以更好的来理解,如下:

在这里插入图片描述

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler            ; Window Watchdog
                .....................中间省略了一些中断
                DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY
                
; Reset handler
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP
                

以下两行代码是上面抽出来的,它们的含义就是将它们下面的那些代码段所占的内存定义为某个section,主要就是给编译器看的
中断服务表就为RESET段,只读数据,复位函数就是.text段,为只读代码

AREA RESET, DATA, READONLY
AREA |.text|, CODE, READONLY

内存映射表
在这里插入图片描述
符号表
在这里插入图片描述
内存映射表
在这里插入图片描述

从以上可以看出,__main函数(库自带的)与systeminit函数(systeminit.c编译来的)都是相同的属性RO-code,但位于复位函数前后,并不在同一个位置(猜想__main函数是自带库函数,systeminit函数为编译后链接的所以不同),但都与启动文件有关,__main函数是在定义的中断服务表后,所以猜测所有自带的库函数都是跟在中断服务表后,而自定义的就根据编译器来安排(编译文件的顺序)

4.编译器根据启动文件里定义的符号的位置,将主程序和其它源文件编译后形成的目标文件里的数据指令填入连接到相应位置,形成镜像bin文件:
启动文件里的数据可以在最前面,然后就是库文件的函数代码,然后是主程序和其它源代码的函数代码,然后是主程序和其它源程序的数据,最后是堆和栈;

启动文件里用的是ARM的thumb指令,伪汇编代码,伪汇编代码是给编译器看的(让编译器进行符号链接等),thumb指令是给CPU看的,用来处理数据;

stm32中通过IO口与外界交流的数据流向

在这里插入图片描述

1、没有设置DMA
根据 文章第六节,六、stm32中指令、汇编语言、机器码可知:
在芯片内部数据之间的移动只存在(下面的寄存器说的是CPU的寄存器,不是外设寄存器)

1)、两个寄存器间的传输数据。MOV

2)、寄存器与存储器间传输数据。LDR、STR

3)、寄存器与特殊功能寄存器间传输数据。

4)、把一个立即数加载到寄存器。MOV

不存在存储器到存储器之间的数据移动;
所以当需要传出数据还是需要接受数据时都需要CPU的寄存器作为中间商
接收数据:
1、接收的数据需要立即处理不保存的,流通路径:GPIO引脚—>接收数据寄存器---->CPU寄存器---->ACC累加器(数据处理)
2、接收的数据需要保存的,流通路径:GPIO引脚—>接收数据寄存器---->CPU寄存器---->内存
发送数据:
1、GPIO的引脚输出,UART串口的输出,流通路径:内存(立即数输出的话没有这一步)----->CPU寄存器—>发送数据寄存器---->GPIO引脚
下面这段LED = 0的汇编语言代码,就很好的说明了数据的流向
在这里插入图片描述
下面是UART串口输出字符串“abcdefg”的首字符a的程序,根据汇编语言可以看到先将内存中0x08000740处的数据61移到r1中,再将r1中的值做运算后放到r2中,最后再将r2中的值放到UART对应的内存(也就是uart发送数据寄存器的地址)中
在这里插入图片描述
在这里插入图片描述

  • 16
    点赞
  • 93
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值