STM32 HAL 库(汇编启动)和瑞萨 FSP(C 启动)**核心启动代码片段对比**
摘要: 对比 STM32 HAL库与瑞萨FSP的启动流程核心差异: 中断向量表 STM32采用纯汇编定义(固定地址0x00000000),复位处理需手动初始化栈和时钟; 瑞萨FSP用C定义向量表,复位函数集成栈初始化、FSP模块加载,并适配多编译器。 系统初始化 STM32的SystemInit()仅配置时钟,栈初始化由汇编完成; 瑞萨FSP的SystemInit()包含栈/堆、时钟、中断控制器及
·
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 更侧重“灵活可控、底层透明”。
更多推荐



所有评论(0)