概述总结

  • 上电复位,复位脉宽至少20ns。上电复位或者外部手动拉低RST引脚造成的复位,都会使得外设和内核的寄存器都恢复成默认值。而内部看门狗复位,只复位内核寄存器的值,外设寄存器的值保持为原本的配置值。
  • 上电后,CPU先执行内部的出厂Bootloader程序,这个程序会只初始化自身代码需要的硬件外设,然后去读取Boot0和Boot1引脚,然后决定将0x0地址映射到哪一块区域:内部flash所在地址,还是内部sram所在地址,还是说等待接收串口数据进行应用程序的更新。可见,出厂Bootloader中包含了必要外设的初始化、读取Boot引脚电平、重映射0x0地址以及通过串口接收外部应用程序等功能。
  • 如果是从内部flash开始运行:则查看启动文件.s

一、上电复位


在STM32F407的启动过程中,复位信号触发是系统进入确定状态的起点,此时所有硬件模块和寄存器会被强制重置为默认值,确保芯片从一个稳定、可预测的状态开始运行。以下是复位信号触发的详细说明:


1. 复位信号来源与触发条件

STM32F407的复位可由以下事件触发:

  • 上电复位(POR/PDR):当电源电压从0V上升至有效范围(VDD ≥ 2.0V)时自动触发。
  • 外部复位(NRST引脚低电平):NRST引脚被拉低并保持至少20ns后释放。
  • 看门狗复位
    • 独立看门狗(IWDG):计数器超时未刷新。
    • 窗口看门狗(WWDG):计数器超时或提前刷新。
  • 软件复位:通过置位应用中断与复位控制寄存器(AIRCR)的SYSRESETREQ位。
  • 低功耗模式复位:从待机模式唤醒时触发。

2. 复位后的寄存器状态

复位信号触发后,所有寄存器(除少数例外)会被强制重置为默认初始值,具体包括:

  • 内核寄存器(Cortex-M4)

    • 通用寄存器(R0-R12):值未定义(复位后可能残留随机值,需软件初始化)。
    • 堆栈指针(SP):从向量表首地址(0x0000_0000)加载初始值(由启动文件定义)。
    • 程序计数器(PC):从向量表第二个条目(Reset_Handler地址)加载。
    • 特殊寄存器(LR, APSR等):重置为默认值(如APSR状态标志清零)。
  • 系统控制寄存器

    • RCC(复位和时钟控制寄存器)
      • RCC_CR:HSI(16MHz)启用,其他时钟源(HSE、PLL)禁用。
      • RCC_CFGR:系统时钟源为HSI,AHB/APB分频器为默认值(不分频)。
    • FLASH_ACR(Flash访问控制寄存器):复位为0,Flash访问延迟为默认值(需根据时钟频率重新配置)。
    • NVIC(嵌套向量中断控制器):所有中断禁用,优先级复位为默认。
  • 外设寄存器

    • GPIOx_MODER:所有引脚模式复位为输入浮空(安全状态)。
    • TIMx_CR1(定时器控制寄存器):定时器禁用,计数器清零。
    • USARTx_CR1(串口控制寄存器):串口收发功能关闭。
  • 例外情况

    • 备份域寄存器:若备份电源(VBAT)存在,RTC、备份寄存器(RTC_BKPxR)等可能保留值。
    • 调试模块寄存器:调试相关寄存器(如DBGMCU_CR)可能不受复位影响。

3. 复位后的硬件行为
  • 时钟系统
    • 默认使用HSI(16MHz)作为系统时钟(SYSCLK)。
    • HSE、PLL、外部时钟输入等均处于关闭状态。
  • 内存状态
    • Flash和SRAM内容:保留复位前的数据(除非发生电源完全断电)。
    • 内存映射:根据BOOT引脚配置,Flash或SRAM被映射到0x0000_0000
  • 外设状态
    • 所有外设(如ADC、DMA、SPI等)默认禁用,寄存器值为复位初始值。
    • GPIO引脚处于高阻态(输入浮空),避免意外电平输出。

4. 复位时序与稳定性
  • 复位延迟
    • 上电复位(POR)后,芯片内部复位电路会等待电源稳定(tRSTTEMPO ≈ 1ms),确保电压达到阈值后再释放复位信号。
  • 复位信号宽度
    • 外部复位(NRST引脚低电平)需保持至少20ns,以满足最小脉冲宽度要求。
  • 时钟稳定时间
    • 使用HSE时,需等待晶振起振(RCC_CR的HSERDY位置1)后再切换时钟源。

5. 复位对启动流程的影响
  • 关键寄存器初始化
    • VTOR(向量表偏移寄存器):复位后为0,向量表默认位于0x0000_0000(即映射后的Flash或SRAM)。
    • FPU(浮点单元):复位后禁用,需在SystemInit中显式启用(如设置CPACR寄存器)。
  • 默认时钟配置
    • 复位后系统以HSI(16MHz)运行,用户需在SystemInit中重新配置HSE和PLL以提升性能。

6. 验证复位状态的调试方法
  • 寄存器查看
    • 使用调试工具(如ST-Link、J-Link)读取RCC、GPIO、NVIC等寄存器的复位值。
  • 示波器检测
    • 监控NRST引脚电平,确认复位信号宽度和释放时序。
    • 检查HSE晶振是否起振(若使用外部时钟)。
  • 代码检查
    • Reset_Handlermain()函数起始位置插入断点,观察寄存器初始值。

总结

复位信号触发的核心作用是强制芯片硬件和寄存器回到已知的初始状态,确保启动流程的可靠性和可重复性。理解复位后寄存器的默认值对以下场景至关重要:

  1. 外设初始配置:避免残留值导致意外行为(如GPIO误输出)。
  2. 时钟系统优化:从默认的HSI切换到更高性能的时钟源。
  3. 调试异常复位:通过寄存器状态定位复位原因(如看门狗触发)。

二、启动过程

STM32F407上电启动过程详解

STM32F407的上电启动过程是一个由硬件自动触发、软件逐步初始化的复杂流程,涉及芯片底层硬件配置、时钟管理、内存初始化和用户代码执行等多个阶段。以下从 硬件行为软件流程调试要点 进行全面解析:


1. 硬件复位与初始化

1.1 复位信号触发
  • 复位源
    • 上电复位(POR):电源电压达到阈值(VDD ≥ 2.0V)时自动触发。
    • 外部复位(NRST引脚低电平):NRST引脚被外部电路拉低超过20ns。
    • 看门狗复位:独立看门狗(IWDG)或窗口看门狗(WWDG)超时。
    • 软件复位:通过设置内核寄存器(如AIRCR的SYSRESETREQ位)。
  • 复位生效
    • 所有数字逻辑(除备份域)被强制复位,寄存器恢复默认值(见下表)。
    • 内核停止执行,时钟源切换为HSI(16MHz),外设全部关闭。
1.2 复位后硬件状态
模块/寄存器 复位值/状态 说明
Cortex-M4内核 SP从向量表加载,PC指向Reset_Handler 初始SP值由启动文件定义,PC指向复位向量地址。
RCC_CR HSION=1(HSI启用),其他时钟源关闭 默认使用内部16MHz时钟。
RCC_CFGR SW=00(HSI为系统时钟) AHB/APB分频器为默认值(不分频)。
GPIOx_MODER 所有引脚模式为输入浮空 防止上电时引脚意外输出。
NVIC_ICERx 所有中断禁用 中断优先级复位为默认值。
FLASH_ACR 访问延迟为0周期 需根据时钟频率重新配置。
1.3 复位时序
  • 复位延迟:POR电路等待电源稳定(约1ms)后释放复位信号。
  • 时钟稳定:HSI在复位后立即运行,HSE需额外起振时间(若启用)。

2. 启动模式与内存映射

2.1 BOOT引脚配置
BOOT1 BOOT0 启动模式 执行地址 应用场景
0 0 主闪存启动 0x0800_0000 常规代码执行(用户程序在Flash中)。
0 1 系统存储器启动 0x1FFF_0000 使用内置Bootloader通过UART/USB更新固件。
1 1 SRAM启动 0x2000_0000 调试临时代码或运行内存驻留程序。
  • 上电后,CPU先执行内部的出厂Bootloader程序,这个程序会只初始化自身代码需要的硬件外设,然后去读取Boot0和Boot1引脚,然后决定将0x0地址映射到哪一块区域:内部flash所在地址,还是内部sram所在地址,还是说等待接收串口数据进行应用程序的更新。可见,出厂Bootloader中包含了必要外设的初始化、读取Boot引脚电平、重映射0x0地址以及通过串口接收外部应用程序等功能。
2.2 内存映射机制
  • 别名地址:Flash/SRAM/系统存储器的物理地址被映射到0x0000_0000(通过SYSCFG的MEM_MODE位控制)。
  • 代码执行
    • 无论实际存储位置如何,CPU从0x0000_0000开始取指令。
    • 例如:当BOOT0=0时,0x0800_0000(Flash)映射到0x0000_0000

3. 执行启动文件内容(Startup Code)

3.1 启动文件组成
  • 向量表(Vector Table)

    __Vectors       DCD     _estack             ; 栈顶地址
                    DCD     Reset_Handler       ; 复位处理函数
                    DCD     NMI_Handler         ; NMI中断
                    ...                         ; 其他中断向量
    
    • 位于Flash起始位置(0x0800_0000),包含所有中断服务程序(ISR)的入口地址。
  • 启动代码(Reset_Handler)

    Reset_Handler:
        ldr   sp, =_estack          ; 设置堆栈指针
        bl    SystemInit            ; 调用系统初始化函数
        bl    __main                ; 跳转到C库初始化
    
    • 初始化堆栈指针(SP)、调用SystemInit()配置时钟,最终跳转到main()
3.2 链接脚本(Linker Script)
  • 内存分区
    MEMORY {
        FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 1024K
        RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 192K
    }
    
    • 定义Flash和SRAM的起始地址与大小。
  • 段分配
    .isr_vector : { *(.isr_vector) } > FLASH  ; 向量表存储在Flash
    .text       : { *(.text*) }       > FLASH  ; 代码段
    .data       : { *(.data*) }       > RAM AT> FLASH  ; 初始化数据(Flash→RAM拷贝)
    .bss        : { *(.bss*) }        > RAM    ; 未初始化数据(清零)
    

4. 系统初始化(SystemInit函数)

4.1 时钟配置流程
  1. 启用HSI
    • 默认使用HSI(16MHz),确保系统在配置外部时钟前可运行。
  2. 配置HSE
    • 若使用外部晶振(如8MHz),启用HSE并等待稳定(检查RCC_CR的HSERDY位)。
  3. 配置PLL
    RCC->PLLCFGR = (PLL_M << 0) | (PLL_N << 6) | (PLL_P << 16) | (RCC_PLLCFGR_PLLSRC_HSE);
    
    • 例:HSE=8MHz,PLL_M=8, PLL_N=336, PLL_P=2 → PLL输出频率= (8MHz / 8) * 336 / 2 = 168MHz。
  4. 切换系统时钟
    RCC->CFGR |= RCC_CFGR_SW_PLL;  // 切换SYSCLK到PLL输出
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // 等待切换完成
    
  5. 配置分频器
    • AHB分频器=1 → HCLK=168MHz。
    • APB1分频器=4 → PCLK1=42MHz(定时器时钟为84MHz,因APB1定时器有倍频器)。
    • APB2分频器=2 → PCLK2=84MHz。
4.2 其他关键配置
  • FPU启用
    SCB->CPACR |= (0xF << 20);  // 启用浮点单元
    
  • Flash等待周期
    FLASH->ACR = FLASH_ACR_LATENCY_5WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN;
    
    • 根据时钟频率设置等待周期(168MHz需5个等待周期)。

5. C运行时环境初始化

在跳转到main()前,C库(如__main)完成以下操作:

  1. .data段初始化
    • 将Flash中的初始化数据(如const int data[] = {1,2,3};)拷贝到RAM。
  2. .bss段清零
    • 将未初始化的全局变量(如int buffer[100];)所在内存清零。
  3. C++全局构造函数
    • 若使用C++,调用静态对象的构造函数。
  4. 堆和栈初始化
    • 根据链接脚本分配堆(_heap_start)和栈(_estack)空间。

6. 用户代码执行(main函数)

6.1 典型初始化流程
int main(void) {
    // 1. 外设时钟使能
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;   // 启用GPIOA时钟
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;  // 启用USART2时钟

    // 2. 外设配置
    GPIOA->MODER |= GPIO_MODER_MODER2_1;   // PA2设为复用功能(USART_TX)
    USART2->BRR = 0x111;                   // 波特率配置(假设为115200)
    USART2->CR1 |= USART_CR1_UE;           // 启用USART

    // 3. 中断配置
    NVIC_SetPriority(USART2_IRQn, 1);      // 设置中断优先级
    NVIC_EnableIRQ(USART2_IRQn);           // 使能中断
    __enable_irq();                        // 全局中断使能

    // 4. 主循环或RTOS启动
    while (1) {
        // 用户代码
    }
}
6.2 外设初始化的最佳实践
  • 时钟优先原则:先启用外设时钟,再配置寄存器。
  • 状态检查:对于依赖时钟稳定的外设(如USB),需等待时钟就绪标志位。
  • 中断管理:配置NVIC优先级分组(NVIC_SetPriorityGrouping())以避免优先级冲突。

7. 调试与异常处理

7.1 常见问题排查
问题现象 可能原因 解决方案
无法进入main() BOOT引脚配置错误、堆栈溢出 检查BOOT引脚电平,增大Stack_Size。
时钟配置失败 HSE未起振、PLL参数错误 检查晶振电路,验证PLL_M/N/P值。
HardFault 非法内存访问、未处理的中断 使用调试器查看LR和PC寄存器定位错误代码。
7.2 调试工具使用
  • ST-Link Utility:查看Flash内容、擦写芯片。
  • Keil Debugger:单步执行启动代码,观察寄存器和内存变化。
  • 逻辑分析仪:监测NRST引脚、晶振信号和GPIO电平。

总结

STM32F407的启动流程是一个 硬件初始化→软件配置→用户代码执行 的严密过程:

  1. 复位触发强制硬件进入确定状态;
  2. 启动文件完成堆栈、时钟和内存初始化;
  3. C环境准备确保变量和内存正确映射;
  4. 用户代码最终接管控制权,实现应用逻辑。

理解这一过程对以下场景至关重要:

  • 优化启动速度(如跳过Bootloader直接启动)。
  • 调试硬件异常(如时钟配置错误导致死机)。
  • 定制启动流程(如在SRAM中运行代码或实现安全启动)。
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐