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 2void 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; //时钟源为HSERCC_OscInitStructure.HSEState = RCC_HSE_ON; //打开HSERCC_OscInitStructure.PLL.PLLState = RCC_PLL_ON; //打开PLLRCC_OscInitStructure.PLL.PLLSource = RCC_PLLSOURCE_HSE; //PLL时钟源选择HSERCC_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和PCLK2RCC_ClkInitStructure.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);RCC_ClkInitStructure.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; //设置系统时钟时钟源为PLLRCC_ClkInitStructure.AHBCLKDivider = RCC_SYSCLK_DIV1; //AHB分频系数为1RCC_ClkInitStructure.APB1CLKDivider = RCC_HCLK_DIV4; //APB1分频系数为4RCC_ClkInitStructure.APB2CLKDivider = RCC_HCLK_DIV2; //APB2分频系数为2ret = HAL_RCC_ClockConfig(&RCC_ClkInitStructure, FLASH_LATENCY_7); //同时设置FLASH延时周期为7WS,也就是8个CPU周期。if(ret != HAL_OK) while(1);
}