1.这里以stm32的架构:cortex-m3(也即ARMv7)的寄存器的作用、在指令取,指令的译码,指令的执行在其中的作用以及是如何配合实现代码的执行的
哈佛结构和冯诺依曼结构是如何体现的?
编译后的代码为什么分为code、堆、栈、bss、data、符号等部分,分别存储在哪些地方?
首先看寄存器
寄存器分为寄存器组,和外设寄存器
寄存器组对用户是不可见的(用户不能直接操作),它是CPU处理数据时需要调用的
外设寄存器对用户是可见的(用户可以通过地址的映射来对寄存器操作,从而控制相应的外设动作)
上面的是寄存器组,是在Cortex-M3中用来临时存放运算的数据或者指示程序运行的位置
参考官方Cortex-M3权威指南
上图所示的指令总线与数据总线与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架构相关
内存的映射就是数据和代码实际存放的地址
外设映射的地址其实是控制该外设的寄存器的地址
整个架构说的就是处理核心、内存与外设寄存器它们的关系,是怎么通过各种总线连接起来的
如果取指令和访问数据能同时进行,那么这种总线连接的关系,就可以称之为哈佛结构;
存储器地址映射如下:
以上三图都是在 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调试盒(支持低电源模式,控制外设时钟等)
从上一节我们了解到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相当于是在译码器包含了这个
x86CPU组成:
CPU的内部架构和工作原理
cpu架构一
这里的AC累加器、缓冲寄存器和ARM里的通用寄存器R0、R1是一个作用
ARM
根据操作数的地址是在寄存器还是在内存还是立即数,将这些数据传输类型分为以下几种:
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等)、立即数
对立即数寻址就是确定该立即数的大小,
首先我们分析下上面的例子:
490B LDR R1,[PC,#44]
490A LDR R1,[PC,#40]
这两的机器码为啥只差了1,而立即数差了4
这是因为立即数寻址是有8位图决定的,不是直接映射数值的
我们可以看到它是将机器码的0-4位乘以4得到最终的立即数
0xb=11,114=44
0xa=10,104=40
参考
ARM指令计算机器码,自己归纳整理的ARM THUMB指令机器码表
32位指令的寻址方式如下:
ARM立即寻址中有效立即数的计算
为什么要通过这种方式来寻址:
寄存器寻址可以直接控制寄存器,在机器码中所需的位数不多,够用
内存中寻址,如果是32位的那地址就有0-2的32次方,32位指令的话不够用,但这里就可以采用【】的方式间接寻址
而立即数就没有办法通过直接和间接来,32位指令中间除去操作码,剩下的位数根本覆盖不了0-2的32次方,所有采用了位图加循环右移的方式
详细如下:
ARM指令中如何判断一个立即数是有效立即数
这一节应该和**从存储器映射来看stm32架构(内存与外设)**这一节结合来看
上面的代码区(Block0)又细分为以下:
下面左边是物理区分,右边常规字体是以st公司专有划分,右边粗体是以代码角度来区分
Flash =Code + RO-Data + RW-Data(.data);
SRAM = RW-data+ZI-data(.bss+heap+stack);
.bss:未初始化的全局变量
heap:申请的动态内存(如果代码中没有动态申请内存map文件中是没有heap定义的)
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
#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 标准库V3.5启动文件startup_stm32f10xxx.s分析
STM32F10x的启动汇编分析
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看的,用来处理数据;
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发送数据寄存器的地址)中