STM32 HAL 库(汇编启动)和瑞萨 FSP(C 启动)核心启动代码片段对比,聚焦最关键的「复位处理函数」「中断向量表」「系统初始化」三大核心模块,保留代码原貌并标注关键差异:

一、核心模块1:中断向量表(硬件强制的核心入口)

1. STM32 HAL 库(startup_stm32f103xe.s - 纯汇编)
; STM32 汇编定义中断向量表(固定地址 0x00000000)
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; 栈顶地址(硬件第一个读取的值)
                DCD     Reset_Handler              ; 复位向量(硬件复位后执行的第一个函数)
                DCD     NMI_Handler                ; 不可屏蔽中断
                DCD     HardFault_Handler          ; 硬件错误中断
                DCD     MemManage_Handler          ; 内存管理异常
                ; ... 省略其余中断/异常(共数十行)
__Vectors_End

; 弱定义中断处理函数(用户可重写)
                AREA    |.text|, CODE, READONLY
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  SystemInit
                IMPORT  main

                ; 1. 初始化栈(汇编直接操作 SP 寄存器)
                LDR     SP, =__initial_sp

                ; 2. 调用 CMSIS 系统初始化(仅时钟)
                BL      SystemInit

                ; 3. 跳转到 main 函数
                BL      main

                ; 4. main 返回后死循环
                B       .
                ENDP

; 弱定义默认中断处理(死循环)
HardFault_Handler    PROC
                EXPORT  HardFault_Handler [WEAK]
                B       .
                ENDP
2. 瑞萨 FSP(startup.c - 极简汇编 + 核心 C)
// 步骤1:仅1行汇编声明向量表地址(硬件强制,其余全C)
#if defined(__ARMCC_VERSION)
__attribute__((section(".intvec")))
#elif defined(__GNUC__)
__attribute__((section(".vectors")))
#endif
// C 定义中断向量表(替代 STM32 的汇编数组)
const fsp_vector_t g_vector_table[] =
{
    (fsp_vector_t)__STACK_TOP,                // 栈顶地址(和STM32一致)
    (fsp_vector_t)Reset_Handler,              // 复位向量(指向C函数)
    (fsp_vector_t)NMI_Handler,
    (fsp_vector_t)HardFault_Handler,
    (fsp_vector_t)MemManage_Handler,
    // ... 省略其余中断/异常
};

// 步骤2:C 实现复位处理函数(核心逻辑)
void Reset_Handler(void)
{
    // 1. FSP 封装的系统初始化(替代 STM32 汇编栈初始化 + 极简 SystemInit)
    // 包含:栈配置、时钟初始化、FSP 底层配置、芯片专属初始化
    SystemInit();

    // 2. FSP 模块初始化(启动流程与FSP深度整合,STM32无此步骤)
    R_BSP_Init();

    // 3. 按编译器适配跳转用户入口(STM32 仅直接调 main)
#ifdef __ARMCC_VERSION
    main();                  // ARMCC 编译器直接调 main
#elif defined(__GNUC__)
    extern int entry(void);
    entry();                 // GCC 编译器通过 entry 中转到 main
#endif

    // 4. main 返回后执行 FSP 错误处理(STM32 仅死循环)
    while (1)
    {
        FSP_ERROR_TRAP(FSP_ERR_ASSERTION);
    }
}

// C 定义异常处理函数(替代 STM32 汇编弱定义)
void HardFault_Handler(void)
{
    // FSP 内置异常捕获(STM32 需用户手动重写)
    R_BSP_HardFaultHandler();
    while (1);
}

二、核心模块2:SystemInit() 对比(系统初始化核心)

1. STM32 HAL 库(system_stm32f1xx.c - 极简时钟初始化)
void SystemInit(void)
{
    // 仅初始化系统时钟(HSE/HSI/PLL),无其他逻辑
    // 1. 复位 RCC 寄存器
    SET_BIT(RCC->CR, RCC_CR_HSION);
    while((READ_BIT(RCC->CR, RCC_CR_HSIRDY)) == 0);

    // 2. 配置 PLL 时钟源、分频系数(简化版)
    MODIFY_REG(RCC->CFGR, RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL,
               RCC_CFGR_PLLSRC_HSI_Div2 | RCC_CFGR_PLLMULL9);

    // 3. 启用 PLL 并等待就绪
    SET_BIT(RCC->CR, RCC_CR_PLLON);
    while((READ_BIT(RCC->CR, RCC_CR_PLLRDY)) == 0);

    // 4. 设置系统时钟源为 PLL
    MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL);
    while((READ_BIT(RCC->CFGR, RCC_CFGR_SWS)) != RCC_CFGR_SWS_PLL);

    // 无栈初始化、无外设配置(STM32 栈初始化在汇编中完成)
}
2. 瑞萨 FSP(system_ra6m3.c - 全功能初始化)
void SystemInit(void)
{
    // 1. 栈/堆初始化(替代 STM32 汇编栈初始化)
    __set_MSP(__STACK_TOP);  // C 操作栈指针(STM32 用汇编 LDR SP, =__initial_sp)
#ifdef __STACK_SIZE
    __set_PSP(__STACK_TOP - __STACK_SIZE);
#endif

    // 2. 时钟初始化(和 STM32 功能一致,但整合到 C 中)
    system_clock_init();     // 封装的时钟树配置(HSE/HSI/PLL)

    // 3. FSP 专属:中断控制器初始化(STM32 需用户在 main 中配置 NVIC)
    R_ICU_Init(&g_icu_ctrl, &g_icu_cfg);

    // 4. FSP 专属:内存保护、缓存初始化(Cortex-M 高端核特性)
    #if defined(__FPU_PRESENT) && (__FPU_PRESENT == 1U)
        SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); // 启用 FPU
    #endif

    // 5. 芯片专属硬件初始化(如 RA6M3 的 BSP 配置)
    R_BSP_SystemInit();
}

三、核心模块3:main() 执行前的初始化对比

1. STM32 HAL 库(需手动初始化 HAL 库)
int main(void)
{
    // STM32 启动流程不自动初始化 HAL,必须手动调用
    HAL_Init();  // 初始化 HAL 库(时钟、中断、延时等)

    // 手动配置系统时钟(若 SystemInit 不满足需求)
    SystemClock_Config();

    // 手动初始化外设(如 UART、GPIO)
    MX_GPIO_Init();
    MX_USART1_UART_Init();

    // 用户业务逻辑
    while (1)
    {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
        HAL_Delay(500);
    }
}
2. 瑞萨 FSP(FSP 自动初始化,仅需调用统一接口)
int main(void)
{
    fsp_err_t err;

    // FSP 启动流程已完成基础初始化,仅需调用统一接口加载配置
    err = fsp_init();  // 加载 FSP 配置工具生成的外设/中断配置
    if (FSP_SUCCESS != err) __BKPT(0);

    // 直接使用 FSP 驱动(无需手动初始化外设)
    err = R_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);
    if (FSP_SUCCESS != err) __BKPT(0);

    // 用户业务逻辑
    while (1)
    {
        R_IOPORT_PinToggle(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_00);
        R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
    }
}

四、核心差异总结(代码层面)

代码模块 STM32 HAL 库特点 瑞萨 FSP 特点
中断向量表 纯汇编定义,需手动修改汇编文件 极简汇编+核心C定义,可视化工具自动生成
Reset_Handler 纯汇编实现,仅做“栈初始化+调SystemInit+跳main” 纯C实现,整合FSP初始化、编译器适配、错误处理
SystemInit() 仅初始化时钟,功能极简 整合栈/时钟/中断/硬件初始化,功能全面
main() 前置操作 需手动调用 HAL_Init() + 外设初始化 仅需调用 fsp_init(),外设配置自动加载
异常/中断处理 汇编弱定义,用户需手动重写 C 定义+FSP 内置处理框架,无需手写汇编

这份对比片段保留了核心逻辑,剔除了冗余代码,你可以清晰看到:STM32 是「汇编主导、分层松耦合」,瑞萨 FSP 是「C 主导、全流程整合」——两者最终都能完成启动,但 FSP 更侧重“少写代码、可视化配置”,STM32 更侧重“灵活可控、底层透明”。

Logo

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

更多推荐