一、单片机程序运行的本质

1.程序代码放在哪

2.变量放在哪

3.cpu上电后先执行谁

对mcu来说:

  • Flash:掉电不丢,存程序代码和常量
  • RAM:掉电丢失,存运行时变量
  • CPU:从一个固定入口开始执行指令

程序运行的本质就是:

把“该放 Flash 的东西”放到 Flash,
把“该放 RAM 的东西”安排好,
然后 CPU 从复位入口开始跑,先做初始化,再跳进 main()

二、从源代码到单片机可执行文件的流程

一、编译阶段

输入:main.cled.cuart.cstartup.s等源代码文件

  1. 预处理:编译器首先处理源代码中的宏定义、头文件包含和条件编译指令,生成预处理后的代码文件(通常以.i为后缀)。
            此阶段会将所有宏展开、嵌入头文件内容、清除注释

  2. 编译:将预处理后的代码转换为汇编语言(通常以.s为后缀)。
            此阶段进行语法分析、语义检查、中间表示生成和优化。

  3. 汇编:将汇编代码转换为目标文件(通常以.o为后缀)。
             目标文件包含二进制机器码、符号表和重定位信息

输出:main.oled.ouart.ostartup.o等目标文件

二、链接阶段

输入:所有目标文件(.o文件)和链接脚本(.ld文件)

.ld 链接文件:内存分配总规划图

  1. 合并段:链接器将所有目标文件中的相同段合并,如所有.text段(代码)合并成一个大的.text段,所有.data段(已初始化数据)合并成一个.data

  2. 符号解析:链接器查看所有目标文件的符号表,将引用与定义关联起来。

    • 例如:printf函数的引用会被链接到标准库中的实现
  3. 地址重定位:根据链接脚本(.ld文件)为每个符号分配最终的内存地址,并修正所有"浮动"的地址引用。

    • 链接脚本定义了单片机内存布局
      MEMORY {
        FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1M
        RAM  (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
      }
      SECTIONS {
        .vector_table : { KEEP(*(.vector_table)) } > FLASH
        .text : { *(.text) *(.rodata) } > FLASH
        .data : { *(.data) } > RAM AT > FLASH
        .bss : { *(.bss) } > RAM
      }

输出.elf文件、.map文件、.bin.hex文件

  • .elf文件:包含完整的代码、数据和调试信息的可执行文件
  • .map文件:内存映射文件,显示程序的内存布局和符号地址
    • 包含段交叉引用表(Section Cross References),显示"谁调用了谁"的关系
  • .bin.hex文件:通过格式转换工具生成的二进制文件,用于烧录

三、烧录

将二进制文件.bin.hex文件烧录到单片机的Flash当中

三、单片机上电的启动流程

1.完整的启动流程

1.硬件自动做的事情

  • 确定从哪里启动
  • 读取向量表
  • 设置 MSP
  • 跳转到 Reset_Handler

2.startup.s里的启动代码做的

  • 提供向量表
  • 提供 Reset_Handler
  • 初始化 C 运行环境
  • 最终调用 main()

3.SystemInit() 做的

  • 配置时钟树
  • 初始化 PLL
  • 配置 FPU
  • 设置向量表偏移寄存器 VTOR(某些工程)
  • 芯片底层系统配置

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐