嵌入式进阶——MCU上电后的动作 MCU上电后的动作配套视频p04_mcu_startup_1080p30.mp41920×1080307 分 12 秒10 章【俺系一坨硅的作品】【芯片拆解室】 按下复位后芯片内部发生了什么【MCU拆解室–MCU的启动过程-哔哩哔哩】 https://b23.tv/oaufs54参考芯片STM32F103C8T6Cortex-M372 MHz64 KB Flash20 KB SRAM本文定位对照阅读版。地址、段大小、等待周期都来自本集真实 ELF / map / 数据手册。阅读指南章节对应视频时间码一句话0——三个问题1L1 上电00:00复位后 1 毫秒里的五拍2L2 三块地皮00:57手册 p11/p34 与地址空间3L3 时钟心跳01:32HSI→HSE→PLL 72 MHz4L4 链接脚本02:04ENTRY/MEMORY/SECTIONS 三件事5L5 启动文件03:48向量表 → Reset_Handler → main6L6 取指执行04:18Thumb 16/32 位与流水线7L7 栈04:45push/sub/pop 与 1540 字节8L8 中断05:24硬件自动压 8 个寄存器9L9 总线06:05等待周期与 ramfunc10L10 总览06:53十拍路径第 0 章 三个问题复位之后CPU 怎么知道第一条指令在哪谁把.data从 Flash 搬进 RAM谁把.bss清零中断发生时现场是谁保存的第 1 章 上电后的 1 毫秒图 1-1 STM32F103 最小系统板3.3 V、晶振、复位键视频 00:15复位后硬件只做两件事读向量表前两项拍读哪里装进值本工程1[0x0000_0000]SP0x2000_50002[0x0000_0004]PC0x0800_086CReset_Handler然后启动代码接手SystemInit配时钟HSI 8 MHz → PLL 72 MHz把.data从 Flash 拷到 SRAM把.bss从0x2000_0060清到0x2000_0224bl main→ PC 0x0800_0958。图 1-2 五拍读栈顶 / 读复位向量 / 搬 data / 清 bss / 跳 main视频 00:40为什么先设 SP 再跳因为 C 函数一进去就要用栈没有 SP 就没有栈任何push都会写飞。第 2 章 三块地皮与地址空间图 2-1 RM0008/DS 框图内核 72 MHz、Flash 64 KB、SRAM 20 KB、BusMatrix视频 01:15区域起始大小权限Flash0x0800_000064 KB读/执行SRAM0x2000_000020 KB读/写/执行外设0x4000_0000—读/写内核外设NVIC/SysTick0xE000_0000—读/写地址就是门牌号CPU 不区分内存和外设总线译码器按地址分发。本集与 P02 的差别P02 用 L552512 KB/256 KB讲概念从本集起统一到 F10364 KB/20 KB。所以本集所有地址都以 F103 为准_estack 0x2000_5000。第 3 章 时钟芯片的心跳图 3-1 HSI/HSE → PLL → SYSCLK → AHB/APB视频 01:45步骤时钟频率上电默认HSI内部 RC8 MHz外部晶振稳定HSE8 MHzPLL ×9SYSCLK72 MHzAHB 分频HCLK72 MHzAPB1 分频 /2PCLK136 MHzAPB2 分频 /1PCLK272 MHz为什么要先 HSI 再 HSE上电时外部晶振还没起振需要几毫秒先用内部 RC 保证芯片能跑等 HSE 稳定再切换。时钟决定了一拍有多长后面第 9 章的等待周期、第 8 章的 1 ms 中断都以它为前提。第 4 章 链接脚本三块骨架图 4-1 ENTRY / MEMORY / SECTIONS权限字母 rx / rwx通配符ATFLASH视频 02:454.1 ENTRY 与 MEMORYENTRY(Reset_Handler) MEMORY { FLASH (rx) : ORIGIN 0x08000000, LENGTH 64K RAM (rwx) : ORIGIN 0x20000000, LENGTH 20K }(rx)可读可执行不可写→ 所以变量不能住在 Flash(rwx)可读可写可执行 → RAM。4.2 SECTIONS 与权限的含义.isr_vector : { KEEP(*(.isr_vector)) } FLASH .text : { *(.text .text.*) } FLASH .rodata : { *(.rodata .rodata.*) } FLASH .data : { *(.data) } RAM AT FLASH .bss : { *(.bss) } RAM ._user_heap_stack : { ... } RAM括号里不是文件名是通配符把所有.o的同名段收进来.data的AT FLASH运行地址在 RAM装载地址在 FlashLMA/VMA.bss不占 Flash启动时清零。4.3 对着真实固件看段地址大小.isr_vector0x0800_0000236 B.text0x0800_00EC3,268 B.rodata0x0800_0DB016 B.dataLMA0x0800_0DC8/ VMA0x2000_000096 B.bss0x2000_0060452 B._user_heap_stack0x2000_02241,540 B_estack0x2000_5000—_estack钉死在 SRAM 顶端栈从这里往下长。第 5 章 启动文件逐行看图 5-1 向量表前三项小端 复位那一拍 SP/PC 同时点亮视频 04:00__Vectors: .word 0x20005000 ; [0] 初始 SP .word Reset_Handler ; [4] 复位入口 .word NMI_Handler ; [8] ... .word SysTick_Handler ; [0x3C] 第 15 项Reset_Handler的真实流程Reset_Handler: bl SystemInit ; 配时钟 ldr r0, _sdata ldr r1, _sidata ; Flash 里的 .data 模板 ldr r2, _edata CopyDataInit: ldr r3, [r1], #4 str r3, [r0], #4 cmp r0, r2 bcc CopyDataInit ; 搬 .data ldr r0, _sbss ldr r1, _ebss movs r3, #0 FillZerobss: str r3, [r0], #4 cmp r0, r1 bcc FillZerobss ; 清 .bss bl main ; 进 main注意ldr r3,[r1],#4带后索引的 ldr——读完自动把指针加 4是拷贝循环的经典写法。第 6 章 取指与执行图 6-1 PC 指向 Flash指令经 AHB 取回0x4803 是一条 16 位 Thumb 指令视频 04:30每条指令先按 PC 从 Flash 读Cortex-M3 是三级流水线取指/译码/执行Thumb-2大部分指令 16 位复杂指令 32 位0x4803ldr r0, [pc, #12]16 位。取指要经过 AHB、FlashFlash 有等待周期第 9 章这就是流水线气泡的来源。第 7 章 栈1540 字节的家底图 7-1 C/汇编/栈三栏对照push 压帧、sub 腾空间、pop 返回视频 05:05; 函数入口 push {r4, lr} ; 保存现场 sub sp, #8 ; 局部变量int s ... pop {r4, pc} ; 恢复并返回上电 SP 0x2000_5000链接脚本没有显式分配栈栈和堆共享剩下的1,540 字节0x2000_0224向上到0x2000_5000撞上就 HardFault或踩坏 .bss。工程建议用-Wl,--print-memory-usage和 map 文件确认余量中断嵌套、递归、大局部数组都会加深栈。第 8 章 中断硬件替你做的那部分图 8-1 SysTick 打断主循环硬件自动压 R0–R3/R12/LR/PC/xPSR视频 05:45SysTick 每 1 ms 打断一次硬件自动把8 个寄存器压栈R0 R1 R2 R3 R12 LR PC xPSR从向量表第 15 项偏移0x3C取SysTick_Handler装进 PC返回时硬件自动弹栈EXC_RETURN决定用 MSP 还是 PSP。重要边界自动压栈是Cortex-M 的硬件特性AVR、多数 RISC-V 需要软件保存现场。面试常考。被中断的现场真实反汇编主循环0x0800_0978 ldr r2,[r4]→097A cmp→097C bne压栈时 PC 记的是0x0800_097C返回后从这里继续。第 9 章 总线等待周期与 ramfunc图 9-1 HCLK 扫描九拍、Flash 一次读要 3 拍2 个等待周期视频 06:20AHB 72 MHzCPU 取指/数据都走它Flash 跟不上 72 MHz一次读要2 个等待周期共 3 拍对策一打开预取缓冲FLASH-ACR的 PRFTBE减少气泡对策二把最热的函数搬进 RAMramfunc。ramfunc 三步__attribute__((section(.ramfunc)))voidhot(void){...}.ramfunc : { *(.ramfunc .ramfunc.*) } RAM AT FLASH; 启动文件里已有的 CopyDataInit 稍作修改即可搬 .ramfunc代价占 RAM、启动多一次拷贝、只适合最热的函数。第 10 章 总览十拍路径上电/复位 → 读[0]设 SP读[4]跳 Reset_HandlerSystemInit 配时钟搬.data清.bssbl main取指Flash → AHB → CPU函数用栈push/sub/pop中断自动压栈总线等待周期与 ramfunc 优化。自测10 题复位后前两条指令读的是什么为什么链接脚本里 Flash 没有w.data的 LMA 和 VMA 分别在哪_estack是多少为什么是这个值SysTick 中断硬件压了哪 8 个寄存器为什么中断返回能自动弹栈Flash 等待周期是多少影响什么ramfunc 的三步与代价栈和堆共享多少字节撞上会怎样为什么先 HSI 再 HSE参考答案向量表第 0 项SP 初值和第 1 项复位入口。Flash 只读/可执行不可写变量必须在 RAM。LMA 在 Flash0x0800_0DC8VMA 在 RAM0x2000_0000。0x2000_500020 KB SRAM 顶端栈从这里向下长。R0 R1 R2 R3 R12 LR PC xPSR。EXC_RETURN特殊值告知硬件从哪个栈恢复。2 个等待周期一次读 3 拍影响取指/数据吞吐。section(.ramfunc) 链接脚本RAM ATFLASH 启动拷贝代价是 RAM 占用与启动拷贝。1,540 字节撞上 HardFault 或踩坏 .bss。上电时外部晶振未稳定先用内部 RC 保证可运行。