打印
[其他ST产品]

STM32 | map文件详解

[复制链接]
801|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
看一下编程中实际字符串在flash中的位置,字符串属于RO-Data类型,.conststring
下面的printf打印的字符串就属于 在map文件局部符号中找到该段的地址,如下:
因为该段属于对象main.o对象,即在主函数中定义,所以选它,而不是最上面那个段

使用特权

评论回复
沙发
而服务器人|  楼主 | 2024-2-28 14:27 | 只看该作者
在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;
    }
}

使用特权

评论回复
板凳
而服务器人|  楼主 | 2024-2-28 14:28 | 只看该作者
其中的全局初始化变量有bmp280ID、islnit、bmp280RawPressure、bmp280RawTemperature、t、p
未初始化全局变量有bmp280Cal

在在map文件局部符号中找到该段的地址,如下:

首先在.data出就表明了该段的开始地址,该段总共有多大,其中我们看到数据只占了18字节,而一共占了20字节,还有两字节是pad,

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

使用特权

评论回复
地板
而服务器人|  楼主 | 2024-2-28 14:28 | 只看该作者
通过上述分析我们知道该段代码中一共占了20+28字节:
在map文件的映像组大小部分可以看到统计,如下:

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

使用特权

评论回复
5
而服务器人|  楼主 | 2024-2-28 14:29 | 只看该作者
STM32启动文件分析(startup_stm32f10x_md.s)
启动文件里的具体语法和相关作用,这里我就不再分析,详情可以参考以下链接:
STM32启动文件分析(startup_stm32f10x_md.s)
STM32 标准库V3.5启动文件startup_stm32f10xxx.s分析

STM32F10x的启动汇编分析

指令:IMPORT、EXPORT、[WEAK]
IMPORT相当于C语言中的extern 用在调用第三方模块里的某个函数或变量前
————————————————


使用特权

评论回复
6
而服务器人|  楼主 | 2024-2-28 14:29 | 只看该作者
EXPORT相当于声明某个符号量为全局,第三方模块可以调用
PendSV_Handler  PROC
                EXPORT  PendSV_Handler             [WEAK]
                B       .
                ENDP

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

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

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

使用特权

评论回复
7
而服务器人|  楼主 | 2024-2-28 14:29 | 只看该作者
根据硬件的特性:
1.硬件复位启动时是从flash0x08000000开始读指令,所以要将程序的第一条指令存在该位置:这个是由MDK编译器来设置决定,如下:

使用特权

评论回复
8
而服务器人|  楼主 | 2024-2-28 14:30 | 只看该作者
硬件复位启动时会将0x08000000地址上的数据给栈指针sp,0x08000004地址的数据给pc,栈指针决定了所有数据在SRAM中的最大地址,所以0x08000000地址处应该是程序代码编译链接好后栈顶的位置
0x08000004处应该就是复位函数的地址
有一个中断函数表:
上图的两块伪指令代码,告诉编译器栈堆的大小,并在栈顶和堆顶定义了符号,用来指示栈顶和堆顶的位置,右下角为中断向量表前两个字内容;

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

使用特权

评论回复
9
而服务器人|  楼主 | 2024-2-28 14:31 | 只看该作者
根据硬件和软件的特性,要将程序分为不同的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
               

使用特权

评论回复
10
而服务器人|  楼主 | 2024-2-28 14:31 | 只看该作者
以下两行代码是上面抽出来的,它们的含义就是将它们下面的那些代码段所占的内存定义为某个section,主要就是给编译器看的
中断服务表就为RESET段,只读数据,复位函数就是.text段,为只读代码

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

使用特权

评论回复
11
而服务器人|  楼主 | 2024-2-28 14:32 | 只看该作者
内存映射表


符号表


内存映射表


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


使用特权

评论回复
12
而服务器人|  楼主 | 2024-2-28 14:33 | 只看该作者
编译器根据启动文件里定义的符号的位置,将主程序和其它源文件编译后形成的目标文件里的数据指令填入连接到相应位置,形成镜像bin文件:
启动文件里的数据可以在最前面,然后就是库文件的函数代码,然后是主程序和其它源代码的函数代码,然后是主程序和其它源程序的数据,最后是堆和栈;

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

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


没有设置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发送数据寄存器的地址)中

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

37

主题

335

帖子

0

粉丝