# STM32MP157+LiteOS-M GCC极简工程:启动源码适配+Windows Make终极避坑

本文适用人群:需要落地MP157 M4 GCC工程、适配LiteOS-M、VSCode+Makefile开发、Windows编译频繁报错的开发者

核心结论:MP157 M4有专属的极简启动流程与编译规范,适配SRAM-only架构+RTOS运行需求,同时可彻底解决Windows Make玄学编译报错,提供一套可量产的工程规范与参考实现,按规范落地即可稳定运行。

配套说明:本文与《第一篇·原理避坑篇》为一组。第一篇已论证"禁用 Keil→GNU 手转启动文件、用 Cube 包 gcc 原生文件 ",并指出".data 拷不拷取决于链接脚本"。本文即采用"自定义 LMA==VMA 单 SRAM 区"简化布局,因此"删除 .data 拷贝"是合法做法,与第一篇口径完全统一。


一、工程基础文件提取(基于官方文件改写,非零修改)

直接从正点原子 V1.2.0 固件 gcc 目录取两份官方原生文件作为模板 ,再按本系列"LMA==VMA 极简布局"改写,不是原样零修改

  • startup_stm32mp15xx.s:M4 专属 GCC 纯汇编启动文件(原厂小写后缀),作为改写基底;
  • stm32mp15xx_m4.ld:M4 内核 SRAM 专属链接脚本,作为改写基底。

为何必须改写(否则"零修改"会跑不起来):

  1. 官方启动文件含 CopyDataInit 循环(.data 拷贝)+ bl __libc_init_array(C++ 构造器),而官方 .ld 的 .dataLMA≠VMA(带 AT())------你若原样用官方两份,就必须保留 .data 拷贝、且要链 C 库,与本系列"极简/RTOS"目标相反;
  2. 本系列改用"单 SRAM 区、.data LMA==VMA"布局,于是可以删掉 .data 拷贝、删掉 __libc_init_array,工程更精简。

路径核对 & 与第一篇"原样用即可跑"的关系 :本篇"改写"与第一篇第五节第1条"初学者原样用即可跑",取的是同一份 Cube 包 gcc 目录原生文件Templates/gcc/startup_stm32mp15xx.sTemplates/gcc/linker/stm32mp15xx_m4.ld),取文件路径完全相同,区别只在"改不改":

  • 第一篇"原样用" = 不改动启动文件、保留官方 .data 拷贝与 bl __libc_init_array,并链 system_stm32mp1xx.c,可直接跑;
  • 本篇"改写" = 以同一份文件为模板,按 LMA==VMA 极简布局删改几处(删 .data 拷贝 / 删 bl __libc_init_array / bl SystemInit 按需),更精简、适配 RTOS。
    务必核对:取的是 gcc 目录(非 arm/iar),否则语法体系不对。

下面第二节给出改写后的完整源码

图1:MP157 M4 启动文件 ------ 原样用 vs 改写

注:本篇图序承接第一篇,从图11起编。

二、适配LiteOS-M的标准Reset_Handler(最终修正版)

摒弃网上错误的裸机极简写法,完全适配MP157 M4架构与LiteOS-M内核运行机制。本版基于官方 startup_stm32mp15xx.s 改写,相对原厂删两处、留三处、按需一处:

  • 删除 CopyDataInit 循环:本布局 .data 的 LMA==VMA,无需拷贝(详见配套 .ld);
  • 删除 bl __libc_init_array:极简/LiteOS 工程不需要 C++ 构造器,留着反而链接报缺符号;
  • 保留 VTOR 向量表重定向:适配 SRAM 0x10000000 运行地址;
  • 保留 FPU 硬件浮点使能:避免浮点指令触发硬件异常;
  • 保留 .bss 清零:RTOS 内核运行必备;
  • ⚙️ 按需 bl SystemInit:见下方"系统初始化"说明。

核心设计要点

  • ❌ 不拷贝 .data 段:本布局 M4 SRAM 单区运行,.data 的 LMA==VMA,标准 ELF 加载器会把初值写到该同一地址,无需启动拷贝(若改用官方 LMA≠VMA 的 .ld,则必须保留拷贝------见第一篇);
  • ✅ 强制清零 .bss 段:RTOS 内核运行必备;
  • ✅ VTOR 向量表重定向:用 ldr r1, =g_pfnVectors 符号写法,自动跟随链接脚本的向量表位置;
  • ✅ FPU 硬件浮点使能:避免浮点指令触发硬件异常;
  • ⚙️ 按需保留 SystemInit:适配纯 M4 独立启动场景(保留则需链 system_stm32mp1xx.c,详见下文);
  • ✅ 剔除冗余 C 库初始化(已删 __libc_init_array),工程更精简。

完整启动汇编源码

asm 复制代码
Reset_Handler:
    /* 0. 设置栈指针:MSP = _estack
     * 手动起跑会绕过硬件复位读向量表,必须手动配置栈指针 */
    ldr     sp, =_estack

    /* 1. 设置向量表偏移:SCB->VTOR = g_pfnVectors
     * SRAM运行必须重定向,否则中断跳转异常。
     * 用 g_pfnVectors 符号(而非写死常数),自动跟随 .ld 的向量表位置:
     *   本布局向量表在 0x10000000;若换官方 .ld(向量表在 0x00000000)也无需改此行 */
    ldr     r0, =0xE000ED08          /* SCB->VTOR 地址 */
    ldr     r1, =g_pfnVectors
    str     r1, [r0]

    /* 2. 使能 FPU:CPACR.CP10|CP11 = 11
     * 未使能浮点会触发 UsageFault 卡死 */
    ldr     r0, =0xE000ED88          /* CPACR 地址 */
    ldr     r1, [r0]
    orr     r1, r1, #(0xF << 20)     /* CP10/CP11 全使能 */
    str     r1, [r0]
    dsb
    isb

    /* 3. 清零 .bss 段(上电 SRAM 数据随机,RTOS 必备)
     * 重点:本布局 MP157 M4 无需 .data 拷贝(LMA==VMA) */
    ldr     r0, =__bss_start__
    ldr     r1, =__bss_end__
    movs    r2, #0
bss_loop:
    cmp     r0, r1
    bcs     bss_done
    str     r2, [r0], #4
    b       bss_loop
bss_done:

    /* 4. 系统初始化(按需调用,与第一篇口径一致)
     *    - 保留此行:需把 Cube 包 Templates/system_stm32mp1xx.c 链进工程,
     *      由它提供 SystemInit 实现(仅完成FPU使能、VTOR向量表配置、EXTI外设中断掩码清零等最小初始化,**不关闭Cortex-M内核全局中断,也不配置系统时钟**);
     *    - HSI 默认 64MHz 纯 M4 场景:可直接删除此行,
     *      前面 FPU+VTOR 已够,免得链接报 undefined reference to SystemInit;
     *    - PLL 高频场景:需另行配置 RCC(HAL_RCC 或寄存器直写),与是否调用 SystemInit 无关 */
    bl      SystemInit   /* 纯 HSI 64MHz:可删除此行;PLL 高频:另行配置 RCC,与 SystemInit 无关 */

    /* 5. 跳转主函数,启动 LiteOS 内核 */
    bl      main
    b       .                        /* 防御死循环 */

图2:实战版 Reset_Handler ------ 删 2 / 留 3 / 按需 1(共六步)

系统初始化文件来源:Cube 包 STM32Cube_FW_MP1_V1.2.0/Drivers/CMSIS/Device/ST/STM32MP1xx/Source/Templates/system_stm32mp1xx.c。保留 bl SystemInit 时务必把它加入编译文件列表;走纯 HSI 场景删掉 bl SystemInit 后则无需此文件。
📝 补充说明(消除源码对照困惑):本工程 system_stm32mp1xx.cSystemInit 为精简版,相对官方 ST 文件省略了 EXTI_C2->IMR1..EMR3 六个寄存器的中断掩码清零 ;原因是 M4 复位后这些寄存器复位值即全 0(纯 M4 独立启动无 A7 干预),该清零属幂等保险,省略不影响行为。其余 FPU 使能、VTOR 重定向与官方一致,且同样不配置系统时钟、不关闭Cortex-M内核全局中断 。若你直接用官方原版 system_stm32mp1xx.c(含 HAL),则 EXTI 清零会照常执行,行为与上文一致。

配套链接脚本规范(完整可链接版,关键符号全部对齐)

下面是一份完整、可直接用于链接.ld(单 SRAM 区、.data LMA==VMA)。注意:第一节说的"官方文件"只是模板,这份才是你工程真正使用的脚本 ------它相对官方 stm32mp15xx_m4.ld 做了精简(合并为单 SRAM 区、去掉 AT())。

ld 复制代码
/* stm32mp15xx_m4.ld --- 自定义单 SRAM 区布局(LMA == VMA)
 * 改编自 Cube 包官方 stm32mp15xx_m4.ld,精简为 M4 极简裸机/RTOS 布局:
 *   - 官方原版把 m_interrupts / m_text / m_data 分三段、.data 带 AT() 使 LMA≠VMA;
 *   - 本脚本合并为单一 SRAM 区,.data 的 LMA==VMA,启动文件可省 .data 拷贝。
 */
MEMORY
{
  SRAM (rwx) : ORIGIN = 0x10000000, LENGTH = 256K  /* M4 SRAM 256KB */
}

_estack = ORIGIN(SRAM) + LENGTH(SRAM);   /* SRAM 顶端,作为 MSP 初值;布局无关,跟随 MEMORY 定义 */

ENTRY(Reset_Handler)

SECTIONS
{
  /* 向量表:放在 SRAM 首地址 0x10000000,VTOR 指向此处 */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector))
    . = ALIGN(4);
  } > SRAM

  /* 代码与只读数据 */
  .text :
  {
    . = ALIGN(4);
    *(.text)
    *(.text*)
    *(.rodata)
    *(.rodata*)
    . = ALIGN(4);
    _etext = .;
  } > SRAM

  /* .data 段:LMA == VMA(同一 SRAM 区),无需 _sidata、无需启动拷贝 */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;
    *(.data)
    *(.data*)
    . = ALIGN(4);
    _edata = .;
  } > SRAM

  /* .bss 段:启动文件统一清零(汇编/链接脚本符号完全对齐,无链接告警) */
  .bss :
  {
    . = ALIGN(4);
    __bss_start__ = .;
    _sbss = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
    _ebss = .;
  } > SRAM
}

图3:单 SRAM 区 .ld 布局 ------ LMA==VMA 极简(0x10000000 ~ 0x10040000,256KB 一区到底)

报错避坑:若链接出现 undefined reference to '_sidata',说明你混用了"官方启动文件(用 _sidata 做 .data 拷贝)+ 本简化 .ld(不定义 _sidata)" 。根因是 .s 与 .ld 符号没对齐------按上文把官方启动文件的 CopyDataInit 循环删掉即可(本 .ld 本就不需要它)。


三、LiteOS-M 标准main函数模板(固定规范)

启动文件适配后,必须遵循LiteOS-M官方初始化流程,不可裸机直接运行:

c 复制代码
#include "los_kernel.h"

int main(void)
{
    /* 内核初始化(依赖 bss 正确清零) */
    UINT32 ret = LOS_KernelInit();

    if (ret == LOS_OK)
    {
        /* 任务创建、外设初始化、中断配置 */
        LOS_Start(); /* 启动任务调度器 */
    }

    while (1);
}

图4:LiteOS-M main 标准初始化三步(依赖 .bss 正确清零)

四、最终标准编译命令(仅2条,无冗余、参数修正)

全程使用 fpv4-sp-d16(Cortex-M4F 专属,纠正全网 fpv5 错误,fpv5 仅适配 M7 内核),仅保留无FPU/有FPU两条标准命令。

文件后缀说明:按本篇第五节 5.3 工程规范,Windows 下板级启动文件统一使用大写 .S(规避 mingw32-make 的 vpath 小写匹配缺陷,详见 5.2 节)。

bash 复制代码
# 1、普通无FPU编译(基础裸机工程,文件后缀按 5.3 规范统一为 .S)
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb startup_stm32mp15xx.S -o startup.o

# 2、带FPU硬件加速编译(RTOS工程必备,标准正确参数)
# M4F专属 FPv4-SP-D16,严禁使用 M7 的 FPv5 参数
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard startup_stm32mp15xx.S -o startup.o

图5:M4F vs M7 FPU 参数对照(fpv4-sp-d16 vs fpv5,MP157 M4 唯一正确)

五、移植终极踩坑复盘(.s/.S 玄学报错全解析:跨平台机制 + Windows 迁移高发)

5.1 汇编后缀 .s/.S 核心争议:原厂规范 vs Windows工程实践

折中说明:原厂文件默认小写 .s,符合 GCC 纯汇编语法规范;GNU Make 的 vpath/后缀替换为大小写敏感字符串匹配(全平台行为完全一致);Windows 下因文件系统大小写不敏感,文件'明明存在'却报找不到,迷惑性最强、迁移最高频,工程落地需要折中适配,二者并不矛盾------前者是"应该是什么",后者是"工程上怎么避免踩坑"。

语法标准定义:

  • 小写 .s:纯汇编文件,无预处理,适合 startup 启动文件(原厂规范);
  • 大写 .S:带 C 预处理,支持宏与头文件,适合 LiteOS 内核汇编文件。

5.2 Windows Make 致命玄学报错根源

报错现象:

text 复制代码
make: *** No rule to make target 'xxx/startup_stm32mp15xx.o', needed by 'liteos_m.elf'.  Stop.

根因:Windows 文件系统大小写不敏感,但 GNU Make vpath 匹配为严格字符串大小写敏感,小写 %.s 无法匹配实际后缀为 .S 的文件,导致编译规则失效;Linux/Mac 同样会报错(根因差异见 5.2.4),Windows 的特殊性仅在于文件系统能正常打开该文件、报错更具迷惑性。

5.2.1 最小复现案例

构造目录结构:

text 复制代码
demo/
├── Makefile
└── src/
    └── startup_stm32mp15xx.S    ← 实际文件后缀大写 .S

用 PowerShell 验证实际文件名(Windows 资源管理器看不出大小写差异):

powershell 复制代码
PS D:\demo> Get-ChildItem src | Select-Object Name

Name
----
startup_stm32mp15xx.S    ← 实际是大写 .S

Makefile 内容:

makefile 复制代码
vpath %.s src

startup.o: startup_stm32mp15xx.s
	arm-none-eabi-gcc -c $< -o $@

执行 mingw32-make:

text 复制代码
PS D:\demo> mingw32-make
make: *** No rule to make target 'startup_stm32mp15xx.s', needed by 'startup.o'.  Stop.

文件明明存在,make 却报"找不到",玄学报错由此产生。

5.2.2 三种验证方法

注意:下面三种方法仅用于验证"文件存在但 make 找不到"的根因 ,均不是工程落地建议。Windows 工程的标准规范统一见 5.3 节(启动文件统一大写 .S,彻底规避大小写匹配坑)。

验证方法 操作 预期结果
方法 1:改 vpath 模式 vpath %.s src 改为 vpath %.S src,重新 make ✅ 编译成功
方法 2:改文件后缀 把实际文件 startup_stm32mp15xx.S 重命名为 .s,重新 make ✅ 编译成功
方法 3:改 Makefile 依赖 startup.o: startup_stm32mp15xx.s 改为 %.o: %.S,重新 make ✅ 编译成功
5.2.3 与文件系统能否访问无关

注意一个反直觉的现象:Windows 文件系统大小写不敏感,下面这段 C 代码可以正常打开文件:

c 复制代码
FILE *f1 = fopen("src/startup_stm32mp15xx.s", "r");   // 实际文件是大写 .S
FILE *f2 = fopen("src/startup_stm32mp15xx.S", "r");
// 两者都能成功打开同一个文件

但 make 的 vpath 不是用文件系统去列目录再匹配,而是先用 %.s 字符串过滤候选名单,字符串层面 %.s 不匹配 startup_stm32mp15xx.S,候选名单为空,根本没机会到文件系统那一步。

这就是 Windows 下 vpath"找不到文件"的根因所在。

图6:Windows vpath "找不到文件" 根因 ------ FS 不敏感 != Make 字符串敏感(两层机制分离)

5.2.4 Linux/Mac 对照实验

同样的 Makefile 和文件名(实际后缀 .S),在 Linux 下执行 make:

text 复制代码
$ make
make: *** No rule to make target 'startup_stm32mp15xx.s', needed by 'startup.o'.  Stop.

Linux 同样会报错,但根因不同------Linux 文件系统大小写敏感,根本就没有 startup_stm32mp15xx.s 这个文件(只有 .S)。

所以 Linux 下你早就会在写 vpath 时意识到要匹配大小写;而 Windows 下因为文件系统能访问,开发者误以为"vpath 也应该能匹配",结果栽在 make 的字符串匹配层。

5.3 量产级统一工程规范

  • 内核汇编文件(los_exc.Slos_dispatch.S):保留大写 .S,适配预处理逻辑,不修改内核源码;
  • 板级启动文件:Windows 工程统一改为大写 .S,规避 Make 匹配 BUG,文件内容无任何改动、无副作用;
  • Makefile 统一单套 .S 编译规则,彻底杜绝混合后缀冲突。

5.4 隐藏头文件路径坑

LiteOS-M 内核源文件内部大量使用相对引用 #include "xxx.h",仅配置顶层内核头文件路径无法编译通过,必须手动添加 kernel/src 头文件检索路径。该问题属于隐藏编译依赖,不会在增量编译暴露,仅在全量 clean 编译时触发,极易被忽略。


六、最终量产总结(8条核心规范)

  1. 严禁手动转换 MP157 启动文件,原厂 GCC 文件为最优方案(取其作模板改写,而非 ARMCC→GNU 手转);
  2. .data 拷不拷取决于链接脚本 :本篇采用自定义 LMA==VMA 简化 .ld(.data 直接 > SRAM),故无需 .data 拷贝;若用官方 .ld(LMA≠VMA)仍必须保留拷贝------详见第一篇;无论哪种布局,.bss 清零绝对不能省略;
  3. 纯 M4 独立启动必须确保时钟正确(默认 HSI 64MHz 或显式配置 PLL),并完成 FPU/VTOR 初始化,保证硬件与 RTOS 正常运行(VTOR 用 ldr r1, =g_pfnVectors 符号写法最稳);
  4. M4F 内核 FPU 参数固定为 fpv4-sp-d16,禁止使用 M7 的 fpv5 参数;
  5. 汇编后缀按场景区分:纯汇编 .s、带预处理 .S,原厂小写规范合规;
  6. Windows 工程折中适配:启动文件统一大写 .S,规避 Make 大小写匹配坑;
  7. 链接脚本严格匹配段定义(_estack/__bss_start__/__bss_end__ 等符号齐全),杜绝 _sidata 未定义链接错误、bss 符号不匹配告警;
  8. LiteOS 工程必须遵循标准初始化流程,依赖 .bss 正确清零。

原创不易,转载请注明出处,全套适配 STM32MP157+LiteOS-M 的极简参考工程

相关推荐
智购科技自动售卖机厂家1 小时前
2026自动售货机云端数据备份策略:从全量备份到增量恢复的工程实践~YH
数据库·stm32·嵌入式硬件·物联网·架构·零售
小何code1 小时前
STM32入门教程,第12课(下),旋转编码器计次
单片机·嵌入式硬件
AI视觉网奇1 小时前
图片转矢量图 算法总结
windows
清水白石0082 小时前
Python dataclass 高级避坑指南:从 default_factory、frozen、slots 到哈希灾难
windows·python·哈希算法
kaixin_啊啊2 小时前
Windows版ChatGPT启动失败Unable-to-locate-Codex-CLI-binary解决方法
windows·chatgpt
零号栈帧2 小时前
Codex 手机控制台——AgentDeck
windows·cloudflare·codex·pwa·ai agent
时光慢煮2 小时前
开源鸿蒙PC上手体验:从 Windows 和 macOS 切换过来,我被惊艳到了
windows·开源·harmonyos
J.T.L3 小时前
Windows 10/11 本地/微软账户密码遗忘:终极自救
windows·microsoft
zx_741484813 小时前
【深度学习入门】Windows 下 PyTorch GPU 环境搭建
pytorch·windows·深度学习