STM32F7xx —— 启动文件和HAL库

本文深入解析STM32F7xx系列微控制器的HAL库,涵盖库开发优势、关键文件介绍、启动文件分析及系统时钟设置。通过实例讲解,帮助读者理解HAL库的工作原理和应用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                          STM32F7xx —— 启动文件和HAL库

 

目录

STM32F7xx —— 启动文件和HAL库

一.库开发的优势

二.stm32F7xx几个重要文件

1. HAL库关键文件

2.stm32f7xx_it.c/h

3.stm32f7xx.h

 

4.stm32f767xx.h

5.system_stm32f7xx.c/h

6.stm32f7xx_hal_msp.c

7.startup_stm32f767xx.s

三.HAL库中__weak修饰符

四.程序的执行过程

五.启动文件

六.时钟设置(设置系统时钟)


 

 

一.库开发的优势

    开发速度快,便于移植。

 

二.stm32F7xx几个重要文件

1. HAL库关键文件

文件

描述

stm32f7xx_hal_ppp.c/h

基本外设的操作API

stm32f7xx_hal_ppp_ex.c/h

拓展外设特性的API

stm32f7xx_hal.c/h

HAL通用API(如HAL_Init, HAL_Delay等)

stm32f7xx_hal_conf.h

HAL的配置文件,选择使能某种功能

stm32f7xx_hal_def.h

HAL的通用数据类型和定义

stm32f7xx_ll_ppp.c/h

复杂外设的底层实现

 

2.stm32f7xx_it.c/h

         中断服务函数。基本都是空函数,我们可以去掉里面的函数,把中断服务函数写在任意可见的文件中。

 

3.stm32f7xx.h

         所有F7系列的顶层头文件。通过条件编译包含某型号的头文件(此宏一般在target——c/c++里面定义STM32F767xx,后面都以stm32f767为例)。

#if defined(STM32F756xx)

  #include "stm32f756xx.h"

#elif defined(STM32F746xx)

  #include "stm32f746xx.h"

#elif defined(STM32F745xx)

  #include "stm32f745xx.h"

#elif defined(STM32F765xx)

  #include "stm32f765xx.h"

#elif defined(STM32F767xx)

  #include "stm32f767xx.h"

#elif defined(STM32F769xx)

  #include "stm32f769xx.h"

#elif defined(STM32F777xx)

  #include "stm32f777xx.h"

#elif defined(STM32F779xx)

  #include "stm32f779xx.h" 

#else

 #error "Please select first the target STM32F7xx device used in your application (in stm32f7xx.h file)"

#endif

 

4.stm32f767xx.h

         主要就是寄存器的声明(一堆结构体和宏定义)。

 

5.system_stm32f7xx.c/h

         主要定义了SystemInit和SystemCoreClockUpdate两个函数,后续会在这个文件里面加一个SetSysClock()函数. SystemInit主要是时钟系统的初始化和中断向量偏移地址设置(有bootloader的向量偏移需要修改)。SystemCoreClock是一个全局的值(这个在FreeRTOS的时钟配置的时候需要)。

 

6.stm32f7xx_hal_msp.c

         带MspInit的函数进行MCU级别硬件初始化设置,通常被上一层调用(如HAL_UART_Init——HAL_UART_MspInit)。

 

7.startup_stm32f767xx.s

启动文件,上电先运行SystemInit,再运行到main。

; Reset handler

Reset_Handler    PROC

                 EXPORT  Reset_Handler             [WEAK]

        IMPORT  SystemInit

        IMPORT  __main

 

                 LDR     R0, =SystemInit

                 BLX     R0

                 LDR     R0, =__main

                 BX      R0

                 ENDP

 

三.HAL库中__weak修饰符

         weak:弱。被__weak修饰的函数就是“弱函数”,我们可以在自定义的文件中重新定义此函数(去掉__weak),编译器会执行我们自定义的函数。HAL库中一般在回调函数时用到。HAL中默认定义一个空的回调函数,保证编译器不会报错。

 

四.程序的执行过程

         Reset_Handler —— SystemInit —— main —— HAL_init —— SetSysClock —— xxxInit…(FreeRTOS:xTaskCreate —— xTaskCreate)

 

五.启动文件

主要指令:

指令名称 

作用

EQU

给数字常量取一个符号名,相当于 C语言中的 define

AREA

汇编一个新的代码段或者数据段

SPACE

分配内存空间

PRESERVE8

当前文件堆栈需按照 8字节对齐

EXPORT

声明一个标号具有全局属性,可被外部的文件使用

DCD

以字为单位分配内存,要求 4字节对齐,并要求初始化这些内存

PROC

定义子程序,与 ENDP 成对使用,表示子程序结束

WEAK

弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不出错。要注意的是:这个不是 ARM的指令,是编译器的,这里放在一起只是为了方便

IMPORT

声明标号来自外部文件,跟 C语言中的 EXTERN 关键字类似

B

跳转

ALIGN

编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示 4 字节对齐。要注意的是:这个不是 ARM 的指令,是编译器的,这里放在一起只是为了方便。

END

到达文件的末尾,文件结束

Stack——栈,Stack_Size EQU 0x00000400 (局部变量,函数形参,函数调用等),值可以人为修改(不能超过内部SRAM)。

Heap——堆,Heap_Size EQU 0x00000200(动态内存分配malloc/free)

向量表——中断向量服务函数

复位程序——先进入SystemInit——main

关于启动文件详细描述请参考:STM32启动文件详细说明

 

六.时钟设置(设置系统时钟)

/************************* PLL Parameters *************************************/
//时钟设置函数
//Fvco=Fs*(plln/pllm);
//Fsys=Fvco/pllp=Fs*(plln/(pllm*pllp));
//Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq));

//Fvco:VCO频率
//Fsys:系统时钟频率
//Fusb:USB,SDIO,RNG等的时钟频率
//Fs:PLL输入时钟频率,可以是HSI,HSE等.
//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.
//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.

//外部晶振为25M的时候,推荐值:plln=432,pllm=25,pllp=2,pllq=9.
//得到:Fvco=25*(432/25)=432Mhz
//     Fsys=432/2=216Mhz
//     Fusb=432/9=48Mhz
#define PLL_M            25
#define PLL_Q            9
#define PLL_N            432
#define PLL_P            2

void SetSysClock(void)
{
  HAL_StatusTypeDef ret = HAL_OK;
  RCC_OscInitTypeDef RCC_OscInitStructure;
  RCC_ClkInitTypeDef RCC_ClkInitStructure;

 // __HAL_RCC_PWR_CLK_ENABLE(); //使能PWR时钟

  RCC_OscInitStructure.OscillatorType = RCC_OSCILLATORTYPE_HSE;  //时钟源为HSE
  RCC_OscInitStructure.HSEState = RCC_HSE_ON;                    //打开HSE
  RCC_OscInitStructure.PLL.PLLState = RCC_PLL_ON;        //打开PLL
  RCC_OscInitStructure.PLL.PLLSource = RCC_PLLSOURCE_HSE;        //PLL时钟源选择HSE
  RCC_OscInitStructure.PLL.PLLM = PLL_M; //主PLL和音频PLL分频系数(PLL之前的分频)
  RCC_OscInitStructure.PLL.PLLN = PLL_N; //主PLL倍频系数(PLL倍频)
  RCC_OscInitStructure.PLL.PLLP = PLL_P; //系统时钟的主PLL分频系数(PLL之后的分频)
  RCC_OscInitStructure.PLL.PLLQ = PLL_Q; //USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频)
  ret = HAL_RCC_OscConfig(&RCC_OscInitStructure); //初始化
  if(ret != HAL_OK) while(1);

  ret = HAL_PWREx_EnableOverDrive(); //开启Over-Driver功能
  if(ret != HAL_OK) while(1);

  //选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
  RCC_ClkInitStructure.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStructure.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; //设置系统时钟时钟源为PLL
  RCC_ClkInitStructure.AHBCLKDivider = RCC_SYSCLK_DIV1; //AHB分频系数为1
  RCC_ClkInitStructure.APB1CLKDivider = RCC_HCLK_DIV4; //APB1分频系数为4
  RCC_ClkInitStructure.APB2CLKDivider = RCC_HCLK_DIV2; //APB2分频系数为2

  ret = HAL_RCC_ClockConfig(&RCC_ClkInitStructure, FLASH_LATENCY_7); //同时设置FLASH延时周期为7WS,也就是8个CPU周期。
  if(ret != HAL_OK) while(1);
}

 

 

 

 

 

Version: 2.12.0 (2019-07-17) Keil.STM32F7xx_DFP.2.12.0.pack Download Updated Pack to include subset of STM32Cube_FW_F7 Firmware Package version V1.15.0 using HAL Drivers V1.2.7 Added support for Low Level (LL) drivers. Corrected RTE_Device.h file (I2C3_SDA) Corrected condition for selecting HAL RCC MX_Device_h.ftl: Updated parsing of USART virtual mode Updated generation of macros: Added handling for '(' and ')' symbols Corrected launching STM32CubeMX via "play" button for existing projects overwrites with a new STM32CubeMX project file instead of loading existing. Updated Board Examples: graphics examples use Segger emWin version 5.50.0. examples enable Event Recorder in debug targets Updated LCDConf.c (ready for GUI_USE_ARGD = 1) CMSIS-Driver: CAN: Corrected SetBitrate function to leave Silent and Loopback mode as they were. Corrected SetMode function to clear Silent and Loopback mode when NORMAL mode is activated. Corrected MessageSend function to only access required data for sending. EMAC: Corrected __MEMORY_AT(x) define to be compliant with Arm Compiler 6. Corrected: ETH DMA initialization moved to enable of MAC transmitter or receiver solving netInitialize/netUnnitialize/netInitialize sequence. I2C: Corrected transfers for data sizes greater than 255 (Complete Reload handling). Corrected I2C_SlaveReceive functionality. Corrected code alignment. MCI: Added data cache handling. USART: Added check for valid pointer to USART_PIN prior to use. Corrected POWER_OFF sequence. DMA is DeInitialized after it is aborted. USB Device: Updated USBD_EndpointConfigure function to check that maximum packet size requested fits into configured FIFO (compile time configured). I/O output speed is configurable SPI: Updated SPI_TRANSFER_INFO structure - tx_buf type changed from uint8_t * to const uint8_t *. Added check for valid pointer to SPI_PIN prior to use.
<think>嗯,用户想将STM32标准外设项目迁移到HAL,这是个典型的嵌入式系统移植问题。从用户提供的引用材料看,ta应该已经对两种的差异有所了解,但需要具体操作指导。 用户可能正在维护一个老旧项目,或者想利用HAL的跨系列兼容性。考虑到标准已停止更新,迁移到HAL是合理选择。不过用户没说明具体STM32型号,这点需要注意。 核心难点在于外设驱动层的重写。引用[3]提到标准直接操作寄存器的特点,而HAL采用面向对象封装,所以不能简单替换函数调用。比如GPIO配置,标准用GPIO_InitTypeDef结构体直接赋值,HAL则需要调用HAL_GPIO_Init()并传递句柄。 中断处理是另一关键点。引用[4]展示的USART示例很典型——标准直接在中断服务函数里操作USART_DR寄存器,而HAL要求用HAL_UART_IRQHandler()统一调度。这个转换需要重写所有外设中断服务函数。 DMA配置差异更大。用户提供的引用[4]提到双缓冲DMA接收,这在HAL中对应HAL_UARTEx_ReceiveToIdle_DMA()等新API,需要完全重构DMA初始化流程。 时钟树配置可以借助CubeMX图形化工具(引用[1]提及),这是迁移过程中最高效的部分。不过用户要注意HALHAL_RCC_OscConfig()可能不会覆盖所有标准的RCC配置项,特别是PLL分频系数。 最后考虑外设状态管理。HAL用状态机(如UART_STATE_READY)严格管控外设生命周期,而标准没有这个约束。迁移后必须添加HAL_UART_GetState()检查,否则可能触发HAL_BUSY错误。 用户可能忽略的是HAL_Delay()的阻塞特性(引用[2]警示过)。如果原项目用标准实现精密延时,迁移后需要改用TIM硬件定时器,这对实时任务很关键。</think>将STM32标准项目迁移到HAL需要系统性的重构,以下是关键步骤注意事项: ### 一、迁移核心步骤 1. **工程框架重建** - 使用STM32CubeMX生成HAL工程骨架(含启动文件、链接脚本、HAL文件)[^1] - 对比标准的`system_stm32f4xx.c`(以F4为例)与CubeMX生成的时钟配置差异 - 示例时钟树迁移: ```c /* 标准时钟初始化 */ RCC_HSEConfig(RCC_HSE_ON); RCC_PLLConfig(RCC_PLLSource_HSE, 8, 336, 2, 7); /* HAL等效配置 */ RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 336; ``` 2. **外设驱动重写** - **GPIO迁移示例**: ```c // 标准 GPIO_InitTypeDef g; g.GPIO_Pin = GPIO_Pin_13; g.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(GPIOC, &g); // HAL等效 GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); ``` - **USART发送差异**: - 标准直接写DR寄存器:`USART_SendData(USART1, data)` - HAL需状态检查:`HAL_UART_Transmit(&huart1, &data, 1, timeout)` 3. **中断处理重构** - 将标准的`stm32f4xx_it.c`迁移到CubeMX生成的IRQHandler模板 - 关键修改: ```c /* 标准中断服务函数 */ void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE)) { // 直接处理数据 } } /* HAL处理方式 */ void USART1_IRQHandler(void) { HAL_UART_IRQHandler(&huart1); // HAL统一调度 } // 需实现回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { // 实际数据处理 } ``` 4. **DMA配置升级** - HAL使用`HAL_DMA_Init()`替代标准的`DMA_Init()` - 双缓冲示例参考[^4]: ```c HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buf, BUF_SIZE); ``` ### 二、关键注意事项 1. **状态机机制** - HAL严格依赖外设状态机(如`HAL_UART_STATE_READY`) - 所有操作前需检查状态:`while(HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY)` 2. **延时函数改造** - 替换标准`SysTick_Delay()`为`HAL_Delay()` - 注意其阻塞特性可能影响实时性[^2] 3. **外设句柄管理** - 每个外设需定义`XXX_HandleTypeDef`结构体 - 替代标准的直接寄存器操作 4. **低功耗兼容** - HAL的`SLEEP/STOP`模式需调用`HAL_SuspendTick()` - 唤醒后执行`HAL_ResumeTick()` ### 三、验证策略 1. **分阶段迁移**:先迁移时钟GPIO,再逐步处理通信外设 2. **逻辑对比测试**: - 使用示波器比对GPIO翻转时序 - 通信接口采用环回测试 3. **资源消耗评估**: - 对比Flash/RAM占用(HAL通常增加3-5KB) - 中断延迟测试(HAL的IRQHandler会增加少量延迟) > **移植难点警示**: > 标准中直接操作寄存器的代码(如位带操作`BIT_BAND_PERI()`)需彻底重写。HAL的硬件抽象层会损失部分性能优势,但对F4/F7等高性能芯片影响较小[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值