尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STM32MP157 M4 LiteOS-M 工程:代码逐文件注释与框架文档

STM32MP157 M4 LiteOS-M 工程:代码逐文件注释与框架文档 STM32MP157 M4 LiteOS-M 工程代码逐文件注释与框架文档本文档是「STM32MP157 M4 LiteOS-M 入门」五篇博文系列的配套代码解释文档资源 2。建议先通读博文系列再结合本文档逐文件理解代码。配合资源 1完整工程源码使用效果最佳。博文系列覆盖环境搭建 → 源码裁剪 → os_config 配置 → Makefile → lds OpenOCD 调试目录main.c —— 主程序执行流程startup_stm32mp15xx.s —— GCC 启动文件los_dispatch.S —— LiteOS 任务上下文切换stm32mp157_m4.lds —— 链接脚本内存分区target_config.h —— 板级配置详解Makefile —— 构建系统逐段注释附录符号速查与漏洞速查表1. main.c —— 主程序执行流程上图展示了本工程的整体架构绿色os_config层通过-I优先级覆盖紫色内核默认值内核源码kernel_liteos_m分为kernel/核心逻辑、arch/arm/cortex-m4/gcc/汇编实现、kal/抽象层三个只读子模块。1.1 整体执行顺序Cortex-M4 上电后由硬件加载向量表从地址0x00000000读取初始 SP从0x00000004读取初始 PC即Reset_Handler。完整执行链路上电/复位 → Reset_Handler (startup.s, 设 MSP0x10060000) → SystemInit (system_stm32mp1xx.c, FPU/VTOR/时钟基础配置) → __main (C 运行时初始化.data 复制、.bss 清零) → main() → HAL_Init() [1] HAL 库初始化TIM6 启动作为 HAL 时基 → SystemClock_Config() [2] HSI 64MHz 配置 → led_init() [3] GPIOI / GPIOF 时钟使能 引脚初始化 → TIM6 暂停 [4] 关闭 TIM6 计数器 NVIC → LOS_KernelInit() [5] LiteOS 内核初始化堆、HWI 表、SysTick → led_task() [6] 直接调用 LED 闪烁任务绕过 LOS_Start1.2 逐段代码注释头文件与声明#includesys.h#includeled.h#includedelay.h#includelos_task.h// LiteOS 任务 API#includelos_config.h// LiteOS 内核配置通过 -I os_config 覆盖内核默认值#includestm32mp1xx_hal.h// STM32 HAL 库头文件externvoidTIM6_IRQHandler(void);// HAL 时基中断处理函数声明SystemClock_Config()—— M4 工程模式下只能使用 HSI内部高速振荡器。HSE外部晶振依赖 A7 域的时钟树初始化在 M4 单独启动的工程模式下不可用。staticvoidSystemClock_Config(void){RCC_ClkInitTypeDef clkconfig{0};RCC_OscInitTypeDef oscinit{0};// 使能 HSI 64MHz 内部振荡器oscinit.OscillatorTypeRCC_OSCILLATORTYPE_HSI;oscinit.HSIStateRCC_HSI_ON;oscinit.HSICalibrationValue0x10;// 默认校准值HAL_RCC_OscConfig(oscinit);// MCU 子系统时钟HSI 源不分频 → M4 核跑 64MHzclkconfig.ClockTypeRCC_CLOCKTYPE_HCLK;clkconfig.MCUInit.MCU_ClockRCC_MCUSSOURCE_HSI;clkconfig.MCUInit.MCU_DivRCC_MCU_DIV1;HAL_RCC_ClockConfig(clkconfig);}led_task()—— 当前版本使用忙等循环替代LOS_TaskDelay。原因LOS_TaskDelay依赖 PendSV 做上下文切换但 PendSV 在当前架构下被阻塞详见第 3 章 los_dispatch.S 分析。staticvoidled_task(void){while(1){LED0(0);LED1(1);// 红亮 绿灭for(volatileUINT32 i0;i4000000;i){__NOP();}LED0(1);LED1(0);// 红灭 绿亮for(volatileUINT32 i0;i4000000;i){__NOP();}}}volatile关键字防止编译器把忙等循环优化掉。4000000 次循环约对应 500ms 延时64MHz 主频下实际时长与编译器优化级别和指令流水线相关。main()—— 关键时序TIM6 必须在LOS_KernelInit前关闭。intmain(void){HAL_Init();// [1] HAL 库初始化SystemClock_Config();// [2] 64MHz HSIled_init();// [3] LED 引脚初始化// [4] 关闭 TIM6必须在 LOS_KernelInit 前// 原因HAL_Init 启动了 TIM6 中断。LOS_KernelInit 中 HalHwiInit// 会把 VTOR 切到 g_hwiForm未注册的中断默认落入死循环 handler。TIM6-CR1(uint16_t)~TIM_CR1_CEN;// 停硬件计数器HAL_NVIC_DisableIRQ(TIM6_IRQn);// 关 NVIC 中断线HAL_NVIC_ClearPendingIRQ(TIM6_IRQn);// 清 pending 位// [5] LiteOS 内核初始化 // 依次完成OsTaskInit创建空闲任务→ HalHwiInit构建动态向量表// → HalTickStart启动 SysTick 1000Hz→ OsQueueInit → OsSemInit ...if(LOS_KernelInit()!LOS_OK){while(1);}// [6] 直接运行 LED 任务 // 因 LOS_Start/HalStartToRun 存在 PendSV 阻塞问题// 当前绕开调度器直接从 main 调用任务函数。led_task();while(1);}1.3 为什么当前不调用 LOS_Start()图为os_config/下的三个配置文件分工。Makefile 通过-I os_config首行优先级覆盖内核默认值全程不修改kernel_liteos_m/中任何文件。一句话HalStartToRunLiteOS 启动第一个任务的汇编函数使用bx r6普通跳转而非异常返回bx lr导致 CPU 卡在 Reset 异常上下文优先级 -3中PendSV优先级 0xF0永远无法触发调度器不工作。详细解释见本文档第 3 章 los_dispatch.S 的 3.3 节「bx r6与bx lr的本质区别」以及博文第五篇 5.2.9 节。后续修复方案实现 SVC PendSV 标准启动流程FreeRTOS 同款。届时恢复标准LOS_Start调用和LOS_TaskDelay。2. startup_stm32mp15xx.s —— GCC 启动文件2.1 文件结构概览启动文件是 M4 上电后最先执行的代码。STM32MP157 是双核芯片向量表使用.startup_copro_fw段标记为协处理器固件。工程模式拨码 001下RETRAM 被映射到0x00000000。startup_stm32mp15xx.s ├── .syntax unified / .thumb # 汇编指令集声明 ├── .fpu fpv4-sp-d16 # 硬件浮点声明M4 单精度 FPv4-SP-D16 ├── .section .isr_vector / .startup_copro_fw # STM32MP1 双核向量段 ├── 向量表SP、Reset_Handler、NMI... # Cortex-M 异常 16 个 外设 IRQ 160 个 ├── Default_Handler弱别名 # 未定义的中断默认处理bx lr 或死循环 ├── Reset_Handler # 复位入口设 MSP → .data/.bss → SystemInit → main └── .weak PendSV_Handler → HalPendSV # ★ 本工程关键修复2.2 中断向量表关键条目标注向量表是 Cortex-M 上电后读取的第一个数据结构。硬件从0x00000000取 SP、从0x00000004取 PC。.section .isr_vector, a g_pfnVectors: .word _estack // [0] 初始栈顶指针 0x10060000 .word Reset_Handler // [1] 复位入口 .word NMI_Handler // [2] 不可屏蔽中断 .word HardFault_Handler // [3] 硬件错误除零/非法地址等 .word MemManage_Handler // [4] 内存管理错误MPU 违例 .word BusFault_Handler // [5] 总线错误非法外设地址等 .word UsageFault_Handler // [6] 用法错误未定义指令等 .word 0 // [7] 保留 .word 0 // [8] 保留STM32MP1 不设安全模式 .word 0 // [9] 保留 .word 0 // [10] 保留 .word SVC_Handler // [11] 系统调用FreeRTOS 用此启动首个任务 .word DebugMon_Handler // [12] 调试监视器 .word 0 // [13] 保留 .word PendSV_Handler // [14] ← LiteOS 调度器核心入口 .word SysTick_Handler // [15] LiteOS 系统节拍 1000Hz // ... 外设中断 ... .word TIM6_IRQHandler // TIM6 HAL 时基中断⚠️向量表对齐要求STM32MP1 M4 向量表必须512 字节对齐。lds 链接脚本中通过. ALIGN(512)保证这一点。不满足对齐会导致外设中断随机触发 HardFault且 CFSR/HFSR 寄存器无任何故障标志位。2.3 Reset_Handler 逐行注释Reset_Handler是上电后执行的第一段代码。它必须按严格顺序完成五项工作Reset_Handler: ldr sp, _estack // [1] 设 MSP 0x10060000SRAM 384KB 顶部 // 不设 任何压栈都写非法地址 立刻 HardFault // Cortex-M 上电 MSP 默认是 ROM 的极小值(约 0x100) // 数据段初始化 ldr r0, _sdata // data 段运行地址SRAM 中的目标位置 ldr r1, _edata // data 段结束地址 ldr r2, _sidata // data 段加载地址初值在 Flash/ROM 中的存放位置 movs r3, #0 b LoopCopyDataInit CopyDataInit: ldr r4, [r2, r3] // 从 _sidata[offset] 读初始值 str r4, [r0, r3] // 写入 _sdata[offset]运行地址 adds r3, r3, #4 // 偏移 4 字节下一个 32-bit 变量 LoopCopyDataInit: adds r4, r0, r3 cmp r4, r1 // 是否到 _edata 末尾 bcc CopyDataInit // 未到则继续复制 // BSS 段清零 ldr r2, _sbss ldr r4, _ebss movs r3, #0 b LoopFillZerobss FillZerobss: str r3, [r2] // 将 0 写入 BSS 段当前地址 adds r2, r2, #4 LoopFillZerobss: cmp r2, r4 bcc FillZerobss // 跳转到 C 运行时 bl SystemInit // FPU 使能、VTOR0x10000000、Flash 预取配置 bl __main // C 库入口调用全局构造函数 → main()为什么ldr sp, _estack是第一行场景MSP 值后果上电默认ROM 预设的极小值约 0x100无法用栈执行 BL 指令后需要压栈保存 LR写到非法地址 →立刻 HardFault执行第一行后0x10060000SRAM 顶部有 176KB 栈空间正常使用2.4 关键修复PendSV 向量/* 本工程关键修复 * 改前PendSV_Handler Default_Handler bx lr空返回 * LiteOS 的 LOS_Schedule 触发 PendSV 后什么都不做 * LOS_TaskDelay 永远不切任务调度链断裂CPU 掉进 HalSysExit 死循环 * 改后PendSV_Handler HalPendSVLiteOS 上下文切换汇编函数 */ .weak PendSV_Handler .thumb_set PendSV_Handler,HalPendSVDefault_Handler是所有未手动实现的中断处理函数的弱别名总线Default_Handler: b . // 死循环或 bx lr 直接返回3. los_dispatch.S —— LiteOS 任务上下文切换kernel_liteos_m/kernel/arch/arm/cortex-m4/gcc/los_dispatch.S是 LiteOS 内核中最核心的汇编文件。它实现了 Cortex-M4 上的任务上下文保存与恢复是整个调度器能够运转的物理基础。3.1 三大关键函数函数功能调用时机HalStartToRun启动第一个任务LOS_Start()→HalStartSchedule()HalPendSVPendSV 上下文切换SysTick →LOS_Schedule()→ PendSV 触发HalIntLock/HalIntUnLock关/开中断内核临界区保护读写 PRIMASK3.2 HalStartToRun —— 逐行解读上图展示了从 PC 到 M4 SRAM 的完整调试链路上层五级硬件链路PC → GDB → OpenOCD → ST-Link → SWD → M4下层是 GDB 六步标准命令序列file→target remote→halt→load_image→set pc→continue。工程模式 001 下固件下载至 SRAM断电即丢失。步骤 1 —— 配置 PendSV 优先级为最低0xF0ldr r4, OS_NVIC_SYSPRI2 r4 0xE000ED20SHPR3 寄存器地址 ldr r5, OS_NVIC_PENDSV_PRI r5 0xF0F00000 str r5, [r4] SHPR3[23:16] 0xF0 → PendSV 优先级 15最低为什么 PendSV 要最低优先级它负责上下文切换只能在所有硬件中断处理完毕后执行。如果 PendSV 优先级高于某个外设中断那个中断的处理中就无法做任务切换会导致高优先级任务饿死。步骤 2 —— 设置 CONTROL 寄存器mov r0, #2 bit1 SPSEL用 PSPbit0 nPRIV特权级 msr CONTROL, r0 ⚠️ 但 Handler 模式下写 SPSEL 会被硬件忽略这是 PendSV 阻塞机制中的第一环。在 Handler 模式下向CONTROL.SPSEL写 1 是无效操作。CPU 继续使用 MSP 作为栈指针而非任务专属的 PSP。后续bx r6普通跳转不退出 Handler 模式导致 PendSV 永远排不到。步骤 3 —— 获取第一个任务的栈指针ldr r1, g_losTask r1 g_losTask 结构体地址 ldr r0, [r1, #4] r0 g_losTask.runTask当前最高优先级就绪任务 TCB ldr r12, [r0] r12 runTask-stackPointer任务上下文 TaskContext*步骤 4 —— 检查 FPU 是否启用ldr.w r1, OS_FPU_CPACR r1 0xE000ED88CPACR 协处理器访问控制寄存器 ldr r1, [r1] 读取当前 CPACR 值 and r1, r1, #OS_FPU_CPACR_ENABLE 掩码 0x00F00000提取 CP10/CP11 权限位 cmp r1, #OS_FPU_CPACR_ENABLE bne __DisabledFPU FPU 未启用 → 走简化路径跳过 FPU 寄存器保存步骤 5FPU 路径—— 从任务栈恢复上下文并跳转add r12, r12, #100 跳过 FPU 上下文区S16-S31 64B 部分填充 100B ldmfd r12!, {R0-R7} ★ 从任务栈加载 8 个寄存器 R0 taskID任务 ID 号 R1 0x01010101, R2 0x02020202, R3 0x03030303魔数占位 R4 0x12121212占位 R12 原值 R5 HalSysExit任务的 LR返回时跳转到系统退出 R6 OsTaskEntry任务的 PC任务入口函数地址 R7 0x01000000任务的 xPSRThumb 模式标记 add r12, r12, #72 跳过 FPU 剩余区域S0-S15 FPSCR NO_NAME 72B msr psp, r12 PSP 指向剩余栈空间 vpush {s0} 触发 FPU 惰性上下文保存 vpop {s0} mov lr, r5 LR HalSysExit任务函数返回后进入此地址 cpsie i 开中断PRIMASK 0 bx r6 ★ 跳转到 OsTaskEntryr6 uwPC 0x100099F1步骤 5无 FPU 路径—— 跳过 FPU 相关操作__DisabledFPU: add r12, r12, #36 跳过 R4-R11 PriMask9 个寄存器 × 4 36 字节 ldmfd r12!, {r0-r7} ★ 从任务栈加载 R0-R7同上含 PC/LR/xPSR msr psp, r12 PSP 指向剩余栈 mov lr, r5 LR HalSysExit cpsie I 开中断 bx r6 ★ 跳转到 OsTaskEntry3.3 bx r6 与 bx lr 的本质区别——PendSV 阻塞的终极根因bx r6当前 LiteOS-Mbx lrLR 0xFFFFFFFD操作类型普通分支跳转仅修改 PC 寄存器异常返回CPU 恢复整套硬件上下文CPU 模式变化保持 Handler 模式不变退出 Handler进入 Thread 模式NVIC 异常状态Reset 异常始终标记为活跃Reset 异常被标记为已完成PendSV 能否触发不能——PendSV 优先级15远低于 Reset-3可以——Thread 模式无活跃异常抢占Cortex-M 标准做法—FreeRTOS / RT-Thread / ThreadX 全部使用此方式这就是LOS_Start无法启动调度器的全部真相。Cortx-M 架构规定优先级数值越小的异常越优先。Reset 优先级为 -3不可改变的固定最高优先级PendSV 被配置为 15可改变的可编程最低优先级。只要 Reset 异常保持活跃即没有执行异常返回PendSV 就永远排队等不到执行机会。FreeRTOS 的解决方案本工程后续实施方向在HalStartToRun末尾先通过str PENDSVSET, [ICSR]将 PendSV 标记为待处理然后执行svc 0触发系统调用。在SVC_Handler中修改异常返回帧的 xPSR 字段将异常号清零 表示 Thread 模式再bx lr做真正的异常返回。CPU 进入 Thread 模式后之前标记的 PendSV 立即被 NVIC 响应HalPendSV完成第一个任务的正式上下文切换。3.4 HalPendSV —— PendSV 异常处理HalPendSV: cpsid i 关中断临界区防止中断嵌套打断上下文保存 mrs r0, psp r0 当前任务的 PSP任务私有栈指针 stmdb r0!, {r4-r11} 压栈 R4-R11被调者保存寄存器8 × 4 32 字节 ... 保存当前任务栈指针到 TCB ... ... 从就绪队列选下一个最高优先级任务 ... ldmia r0!, {r4-r11} 出栈新任务的 R4-R11 msr psp, r0 切换 PSP 到新任务 ... 恢复 FPU 上下文如果启用... bx lr ★ 异常返回CPU 自动弹出 R0-R3/R12/LR/PC/xPSR4. stm32mp157_m4.lds —— 链接脚本内存分区4.1 M4 SRAM 384KB 完整分区STM32MP157 为 M4 内核配备 384KB 片上 SRAM地址范围0x10000000 ~ 0x10060000。链接脚本将其精确划分为四个功能区0x10000000 ┌─────────────────────────┐ │ .isr_vector (512 B) │ ← 中断向量表STM32MP1 硬件要求 ALIGN(512) │ .startup_copro_fw │ ← STM32MP1 特有协处理器固件段KEEP 防止 -gc-sections 删除 0x10000200 ├─────────────────────────┤ │ .text 代码段 │ ← 函数机器码arm-none-eabi-gcc 编译产出 │ .rodata 只读数据 │ ← const 变量、printf 格式字符串 │ .data 已初始化 │ ← 有初值的全局/静态变量Reset_Handler 从 Flash 复制 │ .bss 未初始化 │ ← 无初值的全局/静态变量Reset_Handler 清零 0x10014000 ├────── ASSERT 边界 ──────┤ ← lds 编译期断言_ebss 不得超过此行 │ LiteOS 系统堆 (128KB) │ ← LOS_KernelInit 中 OsMemInit 初始化 │ 0x10014000-0x10033FFF │ ← 任务栈、信号量、互斥锁、消息队列全部从此分配 0x10034000 ├─────────────────────────┤ │ MSP 系统栈区 (176KB) │ ← Reset_Handler 设 MSP 0x10060000栈顶 │ │ ← 异常/中断的压栈均使用 MSP 向下增长 │ │ ← led_task 当前直接由 main 调用也在 MSP 区 0x10060000 └──── _estack ───────────┘ ← MSP 栈顶 _estack 0x100600004.2 关键 lds 指令注解ENTRY(Reset_Handler) 入口点符号调试器设 PC 时使用 _estack 0x10060000; MSP 栈顶384KB SRAM 末端 MEMORY { SRAM (rwx) : ORIGIN 0x10000000, LENGTH 384K }/* 向量表必须 512 字节对齐 */ .isr_vector : { . ALIGN(512); ★ 强制 512 对齐STM32MP1 硬件要求不满足 → 随机 HardFault KEEP(*(.isr_vector)) KEEP 防止 --gc-sections 把看似未引用的向量表整体删除 KEEP(*(.startup_copro_fw*)) STM32MP1 特有段用于 ROM 代码识别 M4 固件 } SRAM/* BSS 段 编译期 ASSERT 边界保护 */ .bss : { _sbss .; BSS 起始地址链接时确定为绝对数值 *(.bss) *(COMMON) _ebss .; BSS 结束地址 __bss_end__ _ebss; 备用的结束符号部分脚本/调试器使用此名称 } SRAM /* ★ 编译期内存越界保护BSS 不能侵入堆区 */ ASSERT(_ebss 0x10014000, BSS overflow into LiteOS heap area! Reduce code/data.)实测值本工程make后build/m4_liteos.map文件记录符号地址说明_etext0x1000F4BC代码段结束含 .text .rodata .data 加载地址_ebss0x1001151CBSS 段结束代码 数据总计占用约 69KB堆边界0x10014000距 _ebss 还有0x2AE4约 11KB安全余量5. target_config.h —— 板级配置详解5.1 配置项速查表target_config.h是本工程的板级配置核心。所有与芯片型号、时钟、内存、RTOS 功能开关相关的宏都集中在此文件中。宏名值作用OS_SYS_CLOCK64000000ULM4 系统时钟 HSI 64MHzLOSCFG_BASE_CORE_TICK_PER_SECOND1000SysTick 频率 1000Hz 1ms/tickLOSCFG_SYS_HEAP_ADDR0x10014000ULiteOS 堆起始地址跳过代码数据区约 80KBLOSCFG_SYS_HEAP_SIZE0x20000U128KB堆大小任务栈/IPC 控制块内存池LOSCFG_PLATFORM_HWI_LIMIT160g_hwiForm动态向量表项数STM32MP157 外设 IRQ 多达 150LOSCFG_BASE_CORE_SWTMR0软件定时器关闭当前因 PendSV 阻塞的临时对策LOSCFG_BASE_CORE_SWTMR_ALIGN0软定时器对齐开关SWTMR 0 时必须也为 0LOSCFG_BASE_IPC_SEM1信号量功能开启LOSCFG_BASE_IPC_MUX1互斥锁功能开启LOSCFG_BASE_IPC_QUEUE1消息队列功能开启5.2 HWI_LIMIT 160 为什么如此重要LiteOS 内核默认HWI_LIMIT 32这意味g_hwiForm数组大小为 16系统异常 32 48 个条目。而 STM32MP157 有超过 150 个外设中断——例如 TIM6 的 IRQ 编号约为 54。如果 HWI_LIMIT 保持 32LOS_HwiCreate(TIM6_IRQn, ...)会向g_hwiForm[16 54] g_hwiForm[70]写入数据。数组只有 48 个元素——严重越界写入。这恰好会破坏紧邻g_hwiForm之后的任务控制块内存区域导致任务入口指针被随机覆盖、bx r6跳转地址错乱、系统崩溃。本工程设置为 160充分覆盖 STM32MP157 的所有外设中断。5.3 os_config 的头文件优先级覆盖机制INCLUDES : -I$(PROJ_ROOT)/os_config # ← 第一优先级 INCLUDES -I$(PROJ_ROOT)/kernel_liteos_m/kernel/include # ← 内核默认GCC 按-I从左到右搜索头文件。los_config.h同时存在于两个位置os_config/target_config.h→ 我们的板级配置kernel_liteos_m/kernel/include/los_config.h→ 内核通用默认配置因为os_config/排在 Makefile INCLUDES 的第一行编译器优先读取我们的值。这就是不修改内核任何源码的实现原理。6. Makefile —— 构建系统逐段注释五步构建流水线源码收集wildcard 遍历过滤 ll_ 驱动→ 编译GCC-DCORE_CM4 必须→ 链接-T lds–gc-sections 省 ~20KB→ 导出objcopy 生成 bin/hex/map→ 烧录make flashOpenOCD 动态解析 Reset_Handler。6.1 工具链定义CROSS_COMPILE ? arm-none-eabi- # 交叉编译前缀 CC : $(CROSS_COMPILE)gcc # C 编译器 AS : $(CROSS_COMPILE)gcc -x assembler-with-cpp # 汇编器需 C 预处理器处理 .S 文件中的 #include LD : $(CROSS_COMPILE)gcc # 链接器与编译器同一工具非独立 ld OBJCOPY : $(CROSS_COMPILE)objcopy # 格式转换elf → bin/hex OBJDUMP : $(CROSS_COMPILE)objdump # 反汇编和符号表导出 SIZE : $(CROSS_COMPILE)size # 代码段 / 数据段大小统计6.2 编译参数逐行含义CPUFLAGS : -mcpucortex-m4 CPU 架构Cortex-M4 -mthumb 指令集Thumb-2M4 原生指令集 -mfloat-abihard 浮点 ABI硬件 FPU 寄存器传参非软浮点库模拟 -mfpufpv4-sp-d16 FPU 类型单精度 FPv4-SP-D16M4 标配浮点单元 CHIP_DEFS : -DSTM32MP157Cxx 芯片具体型号宏HAL 据此选择正确的头文件 -DSTM32MP1 芯片系列宏 -DCORE_CM4 ★ 必须CMSIS/HAL 根据此宏加载 M4 核心外设定义 缺失 → GPIO/UINT32 等类型无法识别编译报错 CFLAGS -ffunction-sections 每个函数放进独立 .text.funcname 段 -fdata-sections 每个全局变量放进独立 .data.varname 段 配合下面的 --gc-sections未引用的段会被链接器丢弃 LDFLAGS -Wl,--gc-sections 死代码回收丢弃所有未被引用函数/变量段 开 → 省 ~20KB不开 → 固件可能溢出 SRAM 容量 -Wl,-Mapbuild/m4_liteos.map,--cref 生成交叉引用 map 文件查内存布局 --specsnano.specs 使用 newlib-nano 精简 C 库缺少 printf float 支持6.3 flash 目标OpenOCD 一键烧录# ★ 从 elf 符号表动态读取 Reset_Handler 的实际地址永不硬编码 # 每次 make 重编后入口地址可能因代码量变化而漂移硬编码必然翻车 ENTRY $(shell arm-none-eabi-objdump -t build/m4_liteos.elf \ | grep Reset_Handler | awk {print 0x$$1}) flash: all echo Flashing via OpenOCD... $(OPENOCD) -s $(OCD_SCRIPTS) \ -f openocd/board_stm32mp157_m4.cfg \ -c init \ -c targets stm32mp15x.cm4 \ ★ 必须显式选 M4 目标否则默认连 A7 的 3333 端口 -c halt \ -c load_image build/m4_liteos.elf \ -c reg pc $(ENTRY) \ 动态解析的 Reset_Handler 地址永不硬编码 -c resume \ -c shutdown附录符号速查与漏洞速查表A.1 常见符号速查符号定义值 / 位置_estackMSP 栈顶指针固定值0x10060000384KB SRAM 顶部Reset_Handler复位入口函数startup.s中定义地址随编译变化PendSV_HandlerPendSV 异常入口本工程通过.thumb_set映射到HalPendSVHalPendSV任务上下文切换函数los_dispatch.SHalStartToRun首个任务启动函数los_dispatch.SOsTaskEntry任务包装入口los_task.c:628调用用户任务函数 → 返回后LOS_TaskDeleteHalSysExit系统退出死循环los_context.c:72所有任务的 LR 默认指向此处g_losTask.runTask当前运行的任务控制块LiteOS 全局结构体含stackPointer和taskEntryg_hwiFormLiteOS 动态中断向量表HalHwiInit()中构建VTOR 最终指向此处VTOR0xE000ED08向量表偏移寄存器当前值指向g_hwiForm地址如0x10010F00SHPR30xE000ED20系统异常优先级寄存器 3PendSV 优先级在[23:16]位配置ICSR0xE000ED04中断控制与状态寄存器bit28PENDSVSET 1 时触发 PendSVA.2 漏洞速查表以下为调试过程中发现并修复的所有问题按影响程度排列#现象根因修复1LED 不闪 / 系统反复进入HalSysExitPendSV 被 Reset 异常阻塞调度器从未工作绕开LOS_Start直接从main调用led_task()2调度器从未触发上下文切换PendSV_Handler Default_Handler空函数bx lr直接返回.thumb_set PendSV_Handler,HalPendSV3首个任务被OsSwtmrTask抢占后调度链断裂软件定时器任务优先级 0先于 LED 任务启动并返回LOSCFG_BASE_CORE_SWTMR 0关闭4LOS_KernelInit后系统卡死TIM6 中断在 VTOR 切换后落入HalHwiDefaultHandler死循环KernelInit前停 TIM6 计数器 清 NVIC5set $pc Reset_Handler报 CP1252 → UTF-32 编码错误中文 Windows GDB 终端编码问题符号名被错误转码用monitor reg pc 十六进制地址代替符号6set $sp 0x10060000后context restore failedSP 值踩在 M4 SRAM 精确边界0x10060000GDB 读[sp4]越界删除手动set $sp让固件ldr sp, _estack自己设7monitor resume与continue混用导致状态异常GDB 和 OpenOCD 的状态机不同步resume 后 GDB 不知目标已在跑统一使用 GDB 的continue本文档为资源 2代码解释与框架文档。资源 1完整工程源码请从 CSDN 资源页下载——解压后make即可编译含 Makefile、OpenOCD 脚本、GDB 调试脚本。五篇博文系列提供从环境搭建到源码裁剪、配置原理、编译系统、OpenOCD 调试的完整教程。建议先通读博文再配合本文档和源码使用。
返回列表