3.5.2.3 os_hw_board_init 函数详解
3.5.2.3 os_hw_board_init 函数详解
📚 本节导读
学习时长: 约 25 分钟
难度级别: ⭐⭐⭐☆☆(中级)
前置知识:
- 3.5.2.2 cortex_m_set_vector 函数详解
🎯 学习目标
- 理解板级硬件初始化的内容和流程
- 掌握堆内存初始化的原理和配置
- 了解 MPU 配置与 SDRAM 初始化的作用
- 理解中断使能与禁用的配合使用
一、函数概述
1.1 函数定位
os_hw_board_init 是板级硬件初始化的核心函数,负责完成芯片厂商 HAL 库初始化、系统时钟配置、外设初始化以及堆内存准备等关键工作。每个芯片平台都有自己的实现。
1.2 基本信息
| 属性 | 值 |
|---|---|
| 函数名 | os_hw_board_init |
| 源文件 | drivers/hal/st/drivers/drv_common.c(以 STM32 为例) |
| 初始化级别 | OS_INIT_LEVEL_PRE_KERNEL_1 |
| 子级别 | OS_INIT_SUBLEVEL_MIDDLE |
| 地址(来自 project.map) | 0x080227a8 |
1.3 为什么放在 MIDDLE 子级别?
板级初始化依赖于中断系统已经准备好(因为 hardware_init() 中 HAL 库的初始化操作可能触发中断),但又必须在设备框架初始化之前完成,因此放在中间级别。
二、函数详解
2.1 完整代码
来源: drivers/hal/st/drivers/drv_common.c(第 153-192 行)
static os_err_t os_hw_board_init(void)
{
MPU_Config();
hardware_init_done = OS_FALSE;
os_irq_enable();
hardware_init();
#ifdef DEFAULT_SYSTEM_CLOCK_CONFIG
SystemCoreClockUpdate();
#endif
HAL_SuspendTick();
#ifdef HAL_SDRAM_MODULE_ENABLED
SDRAM_Init();
#endif
os_irq_disable();
/* Heap initialization */
#if defined(OS_USING_HEAP)
if ((os_size_t)HEAP_END > (os_size_t)HEAP_BEGIN)
{
os_default_heap_init();
os_default_heap_add((void *)HEAP_BEGIN, (os_size_t)HEAP_END - (os_size_t)HEAP_BEGIN, OS_MEM_ALG_DEFAULT);
#if defined(HEAP_BEGIN2) && defined(HEAP_END2)
os_default_heap_add((void *)HEAP_BEGIN2, (os_size_t)HEAP_END2 - (os_size_t)HEAP_BEGIN2, OS_MEM_ALG_DEFAULT);
#endif
}
#endif
#ifdef OS_USING_DMA_RAM
os_dma_mem_init();
#endif
return OS_SUCCESS;
}
OS_INIT_CALL(os_hw_board_init, OS_INIT_LEVEL_PRE_KERNEL_1, OS_INIT_SUBLEVEL_MIDDLE);2.2 分步详解
第一步:MPU 配置(内存保护单元)
MPU_Config();详细解释:
- MPU:Memory Protection Unit(内存保护单元),用于配置内存区域的访问权限
- 作用:提高系统安全性,防止非法访问内存
- 函数定义:
drv_common.c第 124-151 行,为OS_WEAK弱函数
MPU_Config 实际代码:
OS_WEAK void MPU_Config(void)
{
#ifdef MPU_REGION_BASE
MPU_Region_InitTypeDef MPU_InitStruct;
/* Disable the MPU */
HAL_MPU_Disable();
/* Configure the MPU attributes */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = MPU_REGION_BASE;
MPU_InitStruct.Size = MPU_REGION_SIZE;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enable the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
#endif
}STM32F103ZET6 实际配置
MPU_REGION_BASE 在项目中未定义(在 projects/stm32f103zet6-atk-elite 目录下没有搜索到该宏定义)。
结论:这一步的 MPU_Config() 实际上为一个空函数调用,不会执行任何实质性操作。Cortex-M3 的 MPU 是可选的,本开发板未使用。
第二步:标记硬件初始化未完成并启用中断
hardware_init_done = OS_FALSE;
os_irq_enable();详细解释:
| 代码行 | 作用 |
|---|---|
hardware_init_done = OS_FALSE | 标记硬件初始化尚未完成 |
os_irq_enable() | 启用全局中断,为 HAL 库初始化做准备 |
STM32F103ZET6 实际配置
hardware_init_done:初始化为 OS_FALSE(在 drivers/hal/st/drivers/drv_common.c 第 53 行)- 在 board_post_init 中设为 OS_TRUE
第三步:执行芯片厂商硬件初始化
hardware_init();详细解释:
hardware_init()由 STM32CubeMX 生成的代码实现- 定义在
projects/stm32f103zet6-atk-elite/board/CubeMX_Config/Src/bsp.c
STM32F103ZET6 的 hardware_init 实际内容:
| 调用顺序 | 函数 | 用途 |
|---|---|---|
| 1 | HAL_Init() | STM32 HAL 库主初始化 |
| 2 | SystemClock_Config() | 配置系统时钟(HSE 8MHz → PLL x9 → 72MHz) |
| 3 | MX_GPIO_Init() | GPIO 初始化 |
| 4 | MX_DMA_Init() | DMA 初始化 |
| 5 | MX_RTC_Init() | RTC 初始化 |
| 6 | MX_USART2_UART_Init() | USART2 初始化(串口2) |
| 7 | MX_USART1_UART_Init() | USART1 初始化(串口1) |
| 8 | MX_ADC1_Init() | ADC1 初始化 |
| 9 | MX_DAC_Init() | DAC 初始化 |
| 10 | MX_TIM4_Init() | 定时器4初始化(PWM) |
| 11 | MX_TIM8_Init() | 定时器8初始化 |
| 12 | MX_TIM3_Init() | 定时器3初始化(编码器) |
| 13 | MX_TIM1_Init() | 定时器1初始化 |
| 14 | MX_SPI2_Init() | SPI2 初始化 |
| 15 | MX_ADC3_Init() | ADC3 初始化 |
| 16 | MX_CAN_Init() | CAN 初始化 |
| 17 | MX_FSMC_Init() | FSMC 初始化(SRAM) |
系统时钟配置(SystemClock_Config):
// 时钟源:HSE 8MHz
// PLL 倍频:x9
// SYSCLK:8MHz * 9 = 72MHz
// AHB 分频:/1 → HCLK = 72MHz
// APB1 分频:/2 → PCLK1 = 36MHz
// APB2 分频:/1 → PCLK2 = 72MHz
// Flash 等待周期:2第四步:系统时钟更新(可选)
#ifdef DEFAULT_SYSTEM_CLOCK_CONFIG
SystemCoreClockUpdate();
#endif详细解释:
- 条件:仅当定义了
DEFAULT_SYSTEM_CLOCK_CONFIG时执行 - 作用:更新
SystemCoreClock变量,使其反映当前实际时钟频率
STM32F103ZET6 实际配置
DEFAULT_SYSTEM_CLOCK_CONFIG:未定义- 系统时钟配置在
hardware_init()内部通过SystemClock_Config()直接完成 HAL_RCC_ClockConfig()内部已经自动更新了SystemCoreClock变量
结论:条件不满足,这一步不会执行。
第五步:暂停 Systick 定时器
HAL_SuspendTick();详细解释:
- Systick:系统滴答定时器,在
HAL_Init()中被默认启用 - 为什么暂停:在调度器启动前,内核不需要 Systick 中断
- 何时恢复:在后续
board_post_init中通过cortexm_systick_init()重新配置并启用
第六步:SDRAM 初始化(可选)
#ifdef HAL_SDRAM_MODULE_ENABLED
SDRAM_Init();
#endif详细解释:
- SDRAM:Synchronous Dynamic Random-Access Memory(同步动态随机存取内存)
- 条件:仅当 HAL 配置中启用了
HAL_SDRAM_MODULE_ENABLED才执行
STM32F103ZET6 实际配置
HAL_SDRAM_MODULE_ENABLED:未定义(在stm32f1xx_hal_conf.h第 62 行被注释掉了)
结论:条件不满足,这一步不会执行。
第七步:禁用中断
os_irq_disable();详细解释:
- 与第二步的
os_irq_enable()配对使用 - 在
hardware_init()执行期间需要中断支持(HAL 库可能需要),初始化完成后重新关闭中断 - 确保后续的堆内存初始化过程不受中断干扰
- 中断会在调度器启动时由内核重新统一管理
第八步:堆内存初始化
/* Heap initialization */
#if defined(OS_USING_HEAP)
if ((os_size_t)HEAP_END > (os_size_t)HEAP_BEGIN)
{
os_default_heap_init();
os_default_heap_add((void *)HEAP_BEGIN, (os_size_t)HEAP_END - (os_size_t)HEAP_BEGIN, OS_MEM_ALG_DEFAULT);
#if defined(HEAP_BEGIN2) && defined(HEAP_END2)
os_default_heap_add((void *)HEAP_BEGIN2, (os_size_t)HEAP_END2 - (os_size_t)HEAP_BEGIN2, OS_MEM_ALG_DEFAULT);
#endif
}
#endif详细解释:
堆初始化分为三个子步骤:
| 子步骤 | 函数 | 作用 |
|---|---|---|
| 1 | os_default_heap_init() | 初始化堆管理结构体 |
| 2 | os_default_heap_add(HEAP_BEGIN, size, alg) | 添加主堆内存区域 |
| 3 | os_default_heap_add(HEAP_BEGIN2, size, alg) | 添加第二堆内存区域(可选) |
HEAP_BEGIN 和 HEAP_END 的定义(来自 projects/stm32f103zet6-atk-elite/board/board.h):
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern int Image$$RW_IRAM1$$ZI$$Limit;
#define HEAP_BEGIN (&Image$$RW_IRAM1$$ZI$$Limit)
#elif __ICCARM__
#pragma section = "HEAP"
#define HEAP_BEGIN (__segment_end("HEAP"))
#else
extern int __bss_end;
#define HEAP_BEGIN (&__bss_end)
#endif
#define HEAP_END STM32_SRAM1_ENDSRAM 内存布局:
SRAM: 0x20000000 - 0x2000FFFF(64KB)
┌───────────────────┐ 0x20000000
│ .data 段 │ 初始化数据
├───────────────────┤
│ .bss 段 │ 未初始化数据
├───────────────────┤ ← HEAP_BEGIN = &__bss_end
│ │
│ 堆内存区域 │ ← 供 os_malloc() 动态分配
│ (可用空间) │
│ │
├───────────────────┤ ← HEAP_END = 0x20010000
│ .stack 段 │ 系统栈
└───────────────────┘ 0x2000FFFF堆内存算法:
OS_MEM_ALG_DEFAULT 使用 FirstFit 算法(由 OS_USING_ALG_FIRSTFIT 定义):
| 算法 | 特点 |
|---|---|
| FirstFit | 首次适应算法,分配速度快,适合通用场景 |
STM32F103ZET6 实际配置
OS_USING_HEAP:已定义(在 oneos_config.h 第 113 行)OS_USING_ALG_FIRSTFIT:已定义(在 oneos_config.h 第 114 行)HEAP_BEGIN2/HEAP_END2:未定义(本项目没有第二堆区域)- SRAM 大小:64KB(STM32F103ZET6)
- Stack 大小:2KB(在 link.sct 中通过 SRAM 区域自动分配)
结论:条件满足,会执行 os_default_heap_init() 和一次 os_default_heap_add(),第二堆区域的代码不会执行。
第九步:DMA RAM 初始化(可选)
#ifdef OS_USING_DMA_RAM
os_dma_mem_init();
#endif详细解释:
- 条件:仅当启用了
OS_USING_DMA_RAM特性时执行 - 作用:初始化 DMA 专用的 RAM 内存池,为 DMA 传输准备专用的内存区域
- 意义:DMA 传输对内存地址对齐和物理连续性有特殊要求
STM32F103ZET6 实际配置
OS_USING_DMA_RAM:已定义(在 oneos_config.h 第 201 行)
结论:条件满足,这一步会执行,用于初始化 DMA 内存池。
三、关键概念
3.1 中断使能与禁用的配对使用
os_irq_enable() ──── 开启中断
│
▼
hardware_init() ──── HAL库初始化(需要中断)
│
▼
os_irq_disable() ──── 关闭中断设计原因:
- STM32 HAL 库的部分初始化函数可能需要中断支持(如等待标志位)
- 堆内存初始化必须在无中断环境下进行,避免重入问题
- 中断最终由内核在调度器启动时统一管理
3.2 堆内存初始化 vs hardware_init_done
| 变量/函数 | 设置位置 | 设置值 |
|---|---|---|
hardware_init_done | os_hw_board_init | OS_FALSE |
hardware_init_done | board_post_init(后续阶段) | OS_TRUE |
hardware_init_done = OS_TRUE 不在 os_hw_board_init 中设置,而是在后续的 board_post_init(OS_INIT_LEVEL_POST_KERNEL 阶段)中设置。
3.3 堆内存的重要性
后续初始化(如设备框架、驱动注册等)需要使用 os_malloc 分配内存,因此必须在板级初始化阶段就准备好堆内存。
四、在启动流程中的位置
4.1 执行顺序
pre_kernel_1_start()
↓
cortex_m_set_vector() ← 设置向量表(HIGH)
↓
os_hw_board_init() ← 这里(MIDDLE)
↓
device_core_init() ← 设备框架初始化(LOW)4.2 为什么放在中间?
子级别依赖关系:
HIGH(中断系统) ──→ MIDDLE(板级初始化) ──→ LOW(设备框架)
依赖中断已就绪 依赖内存已就绪💡 本节总结
重点回顾
- 九个执行步骤:
| 步骤 | 操作 | STM32F103ZET6 是否执行 |
|---|---|---|
| 1 | MPU 配置 | ❌(空函数) |
| 2 | 标记初始化未完成 + 开中断 | ✅ |
| 3 | 硬件初始化(HAL + 时钟 + 外设) | ✅ |
| 4 | 系统时钟更新 | ❌(DEFAULT_SYSTEM_CLOCK_CONFIG 未定义) |
| 5 | 暂停 Systick | ✅ |
| 6 | SDRAM 初始化 | ❌(HAL_SDRAM_MODULE_ENABLED 未定义) |
| 7 | 关中断 | ✅ |
| 8 | 堆内存初始化 | ✅ |
| 9 | DMA RAM 初始化 | ✅ |
为什么放在 MIDDLE 子级别:
- 依赖 HIGH 级别的向量表/中断系统
- 为 LOW 级别的设备框架提供堆内存基础
关键符号定义:
HEAP_BEGIN:BSS 段结束地址(&__bss_end)HEAP_END:SRAM 结束地址(0x20010000)hardware_init_done:硬件初始化完成标志
下一步
接下来请学习:
- 3.5.2.4 device_core_init 函数详解 - 了解设备框架初始化