文章目录
- 一、新建工程模板——寄存器版本
- 二、HAL入门
- 1.固件库和寄存器的区别
- 2.STM32CubeF4
- 3.HAL库包介绍
- 三、新建HAL库工程模板
一、新建工程模板——寄存器版本
- 开发环境:MDK5
- 软件包:STM32CubeF4包
新建工程模板的一般步骤为:
-
新建工程目录,复制需要的文件到工程目录
首先,打开MDK
软件。然后点击Project->New uVision Project
弹出如图所示界面:
之后,弹出选择器件的对话框,如图所示。因为ALIENTEK
阿波罗STM32F429
开发板所使用的STM32
型号为STM32F429IGT6
,所以在这里我们选择:STMicroelectronics->STM32F4 Series->STM32F429->STM32F429IG->STM32F429IGTx
点击 OK,MDK
会弹出Manage Run-Time Environment
对话框,如图所示:
这是MDK5
新增的一个功能,在这个界面,我们可以添加自己需要的组件,从而方便构建开发环境,不过这里我们不做介绍。所以在图中所示界面,我们直接点击Cancel
,即可,得到如下界面。
到这里,我们还只是建了一个框架,还需要添加启动代码,以及.c
文件等。需要引入下面文件
启动文件放入USER
文件夹,头文件放入CORE
文件夹。
-
把工程需要的文件添加到工程
启动代码是一段和硬件相关的汇编代码。是必不可少的!这代码主要作用如下:1、堆栈(SP)的初始化;2、初始化程序计数器(PC);3、设置向量表异常事件的入口地址;4、调用 main 函数。
这个启动文件,修改了Reset_Handler 函数,该函数修改后代码如下:; Reset handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] ;IMPORT SystemInit //寄存器代码,不需要在这里调用 SystemInit 函数, //故屏蔽掉,库函数版本代码,可以留下. //不过需要在外部实现 SystemInit 函数,否则会报错. IMPORT __main LDR R0, =0xE000ED88 // 使能浮点运算 CP10,CP11 LDR R1,[R0] ORR R1,R1,#(0xF << 20) STR R1,[R0] ;LDR R0, =SystemInit //寄存器代码,未用到,屏蔽 ;BLX R0 //寄存器代码,未用到,屏蔽 LDR R0, =__main BX R0 ENDP
这段代码,我们主要加入了开启
STM32F429
硬件FPU
的代码,以使能STM32F429
的浮点运算单元。其中,0xE000ED88
就是协处理器控制寄存器(CPACR
)的地址,该寄存器的第20~23
位用来控制是否支持浮点运算,这里我们全设置为 1,以支持浮点运算。
特别注意:我们在汇编代码里面使能了FPU
,所以在MDK
里面,我们也要设置使用FPU
,否则可能代码会无法运行,设置方法如下:选择Options for Target ‘Target1’
,打开Target
选项卡,在Code Generation
里面,选择Use Single Precision
,如图所示:
-
在
MDK
中设置把头文件存放路径
-
配置
MDK
:全局宏定义等
重要的预编译全局宏定义标识符:STM32F429xx
-
编写用户函数
新建test.c
文件,并添加到工程组中
此时,编译代码有错误!
这是因为寄存器代码,不需要在这里调用SystemInit
函数,故屏蔽掉。库函数版本代码,可以留下;不过需要在外部实现SystemInit
函数,否则会报错。
此时,再次编译工程,没有警告,没有错误!
-
添加
ALIENTEK
系统文件夹SYSTEM
将SYSTEM
中的文件添加到工程组中
设置头文件存放路径
此时,可以删除CORE
文件夹,因为SYSTEM
文件夹中已经包含。
二、HAL入门
1.固件库和寄存器的区别
STM32
固件库到底是什么,和寄存器开发有什么关系?其实一句话就可以概括:固件库就是函数的集合,把寄存器操作封装起来。固件库函数的作用是向下负责与寄存器直接打交道,向上提供用户函数调用的接口(API)。
在 51 的开发中我们常常的作法是直接操作寄存器,比如要控制某些 IO
口的状态,我们直接操作寄存器:
P0=0x11;
而在 STM32
的开发中,我们同样可以操作寄存器:
GPIOF->BSRR=0x00000001; //这里是针对 STM32F4 系列
这种方法当然可以,但是这种方法的劣势是你需要去掌握每个寄存器的用法,你才能正确使用STM32
,而对于 STM32
这种级别的 MCU
,数百个寄存器记起来又是谈何容易。于是 ST
推出了官方固件库,固件库将这些寄存器底层操作都封装起来,提供一整套接口(API)供开发者调用,大多数场合下,你不需要去知道操作的是哪个寄存器,你只需要知道调用哪些函数即可。比如上面的控制 BSRRL
寄存器实现电平控制,官方 HAL
库封装了一个函数:
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
if(PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16;
}
}
这个时候你不需要再直接去操作BSRRL
寄存器了,你只需要知道怎么使用HAL_GPIO_WritePin
这个函数就可以了。
2.STM32CubeF4
STM32CubeF4
包目录结构,如下图所示:
接下来介绍一下 STM32CubeF4
中几个关键的文件夹,
-
Drivers
文件夹。Drivers
文件夹包含BSP
,CMSIS
和STM32F4xx_HAL_Driver
三个子文件夹。BSP
文件夹
此支持包提供的是直接与硬件打交道的 API,例如触摸屏,LCD,SRAM 以及 EEPROM等板载硬件资源等驱动。BSP 文件夹下面有多种 ST官方 Discovery开发板, Nucleo开发板以及 EVAL 板的硬件驱动 API 文件,每一种开发板对应一个文件夹。CMSIS
文件夹
顾名思义就是符合 CMSIS 标准的软件抽象层组件相关文件。文件夹内部文件比较多。主要包括 DSP库(DSP_LIB 文件夹),Cortex-M 内核及其设备文件(Include 文件夹),微控制器专用头文件/启动代码/
专用系统文件等(Device 文件夹)。STM32F4xx_HAL_Driver
文件夹
这个文件夹非常重要,它包含了所有的 STM32F4xx系列 HAL 库头文件和源文件,也就是所有底层硬件
抽象层 API 声明和定义。它的作用是屏蔽了复杂的硬件寄存器操作,统一了外设的接口函数。该文件
夹包含 Src 和 Inc 两个子文件夹,其中 Src 子文件夹存放的是.c 源文件,Inc 子文件夹存放的是与之对应
的.h 头文件。每个.c 源文件对应一个.h 头文件。源文件名称基本遵循 stm32f4xx_hal_ppp.c 定义格式,
头文件名称基本遵循 stm32f4xx_hal_ppp.h 定义格式。比如 gpio 相关的 API 的声明和定义在文件
stm32f4xx_hal_gpio.h 和 stm32f4xx_hal_gpio.c 中。
-
Middlewares
文件夹。
该文件夹下面有ST
和Third_Party
2 个子文件夹。ST
文件夹下面存放的是STM32
相关的一些文件,包括STemWin
和USB
库等。Third_Party
文件夹是第三方中间件,这些中间价都是非常成熟的开源解决方案。ST
文件夹
Third_Party
文件夹
-
Projects
文件夹。
该文件夹存放的是一些可以直接编译的实例工程。 -
Utilities
文件夹。
该文件夹下面是一些其他组件,在项目中使用得不多。
3.HAL库包介绍
新建一个工程常需要以下HAL库文件:
接下来我们看看 HAL
库工程模板中各个文件之间的包含关系,
从上面工程文件包含关系图可以看出,顶层头文件 stm32f4xx.h
直接或间接包含了其他所有工程必要头文件,所以在我们的用户代码中,我们只需要包含顶层头文件 stm32f4xx.h
即可。这里我们还需要说明一下,在我们 ALIENTEK
提供的 SYSTEM
文件夹内部的 sys.h
头文件中,我们默认包含了 stm32f4xx.h
头文件,所以在我们用户代码中,只需要包含 sys.h
头文件即可,当然也可以直接包含顶层头文件 stm32f4xx.h
。
三、新建HAL库工程模板
- 作用:方便编写用户代码
- 开发环境:MDK5
- 所需软件包:HAL库包
其步骤如下:
-
在电脑的某个目录下面建立一个文件夹,后面所建立的工程都可以放在这个文件夹下面,并新建下面 4 个子文件夹:
CORE
,HALLIB
,OBJ
和USER
。
-
打开
MDK
,点击菜单Project –>New Uvision Project
,然后将目录定位到刚才建立的文件夹下的USER
子目录,工程取名为Template
之后点击保存,工程文件就都保存到USER
文件夹下面。
接下来会出现一个选择Device
的界面,就是选择我们的芯片型号。这里我们选择STMicroelectronics->STM32F4 Series->STM32F429->STM32F429IG->STM32F429IGTx
。
点击 OK,MDK
会弹出Manage Run-Time Environment
对话框,
这是MDK5
新增的一个功能,在这个界面,我们可以添加自己需要的组件,从而方便构建开发环境,不过这里我们不做介绍。我们直接点击Cancel
即可, -
USER
目录内容如下图
这里我们说明一下,Template.uvprojx
是工程文件,非常关键,不能轻易删除。DebugConfig
,Listings
和Objects
三个文件夹是MDK
自动生成的文件夹。其中DebugConfig
文件夹用于存储一些调试配置文件,Listings
和Objects
文件夹用来存储MDK
编译过程的一些中间文件。这里,我们把Listings
和Objects
文件夹删除,我们会在下一步骤中新建一个OBJ
文件夹,用来存放编译中间文件。当然,我们不删除这两个文件夹也没有关系,只是我们不用它而已。 -
接下来我们将从官方
stm32cubeF4
包里面复制一些我们新建工程需要的关键文件到我们的工程目录中。首先,我们要将STM32CubeF4
包里的源码文件复制到我们的工程目录文件夹下面。打开官方STM32CubeF4
包,定位到我们之前准备好的HAL
库包的目录:\STM32Cube_FW_F4_V1.10.0\Drivers\STM32F4xx_HAL_Driver
下面,将目录下面的Src
,Inc
文件夹复制到我们刚才建立的HALLIB
文件夹下面。Src
存放的是固件库的.c
文件,Inc
存放的是
对应的.h
文件
-
接下来,我们要将
STM32CubeF4
包里面相关的启动文件以及一些关键头文件复制到我们的工程目录CORE
之下。打开STM32CubeF4
包,定位到目录\STM32Cube_FW_F4_V1.11.0\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm
下面,将文件startup_stm32f429xx.s
复制到CORE
目录下面 。然后定位到目录\STM32Cube_FW_F4_V1.11.0\Drivers\CMSIS\Include
,将里面的五个头文件:cmsis_armcc.h
,
core_cm4.h
,core_cmFunc.h
,core_cmInstr.h
,core_cmSimd.h
同样复制到CORE
目录下面。
现在看看我们的CORE
文件夹下面的文件,
-
接下来,我们要复制工程模板需要的一些其他头文件和源文件到我们工程。首先定位到目录:
\STM32Cube_FW_F4_V1.11.0\Drivers\CMSIS\Device\ST\STM32F4xx\Include
将里面的 3 个文件stm32f4xx.h
,system_stm32f4xx.h
和stm32f429xx.h
复制到USER
目录之下。这三个头文件是STM32F4
工程非常关键的头文件。然后进入目录\STM32Cube_FW_F4_V1.11.0\Projects\STM32F429I-Discovery\Templates
目录下,我们需要从Src
和Inc
文件夹下面复制我们需要的文件到USER
目录。首先我们打开Inc
目录,将目录下面的3个头文件stm32f4xx_it.h
,stm32f4xx_hal_conf.h
和main.h
全部复制到USER
目录下面。然后我们打开Src
目录,将下面的四个源文件system_stm32f4xx.c
,stm32f4xx_it.c
,stm32f4xx_hal_msp.c
和main.c
同样全部复制到USER
目录下面。
-
接下来,我们还需要复制
ALIENTEK
编写的SYSTEM
文件夹内容到工程目录中。
到这里,工程模板所需要的所有文件都已经复制进去。接下来,我们将在MDK
中将这些文件添加到工程。 -
下面我们将前面复制过来的文件加入我们的工程中。右键点击
Target1
,选择Manage Project Items
,
-
建立四个
Groups
:USER
,SYSTEM
,CORE
,和HALLIB
。
-
下面我们往
Group
里面添加我们需要的文件。第一步我们选择HALLIB
,然后点击右边的Add Files
,定位到我们刚才建立的目录\HALLIB\Src
下面,将里面所有的文件选中(Ctrl+A
),然后点击Add
,然后Close
。可以看到Files
列表下面包含我们添加的文件。这里有 几 个 文件比较特殊 , 例 如stm32f4xx_hal_dsc.c
,stm32f4xx_hal_iptim.c
和stm32f4xx_hal_msp_template.c
三个文件不需要引入工程。stm32f4xx_hal_dsi.c
是mipi
接口相关函数,STM32F429
没有这个接口,所以这个文件可以不用引入。stm32f4xx_hal_iptim.c
文件是低功耗定时器相关函数,STM32F429
也没有这 个功能,也不需要引入。stm32f4xx_hal_msp_template.c
文件内容是一些空函数,一般也不需要引入。
-
用上面同样的方法,将
Groups
定位到CORE
,USER
和SYSTEM
分组之下,添加需要的文件
-
接下来我们要在
MDK
里面设置头文件存放路径。也就是告诉MDK
到那些目录下面去寻找包含了的头文件。这一步骤非常重要。如果没有设置头文件路径,那么工程会出现报错头文件路径找不到。
-
接下来,对于
STM32F429
系列的工程,还需要添加全局宏定义标识符,所谓全局宏定义标识符,就是在工程中任何地方都可见。添加方法是点击魔术棒之后,进入C/C++
选项卡,然后在Define
输入框连输入:USE_HAL_DRIVER,STM32F429xx
。
-
接下来我们要编译工程,在编译之前我们首先要选择编译中间文件编译后存放目录。
MDK
默认编译后的中间文件存放目录为USER
目录下面的Listings
和Objects
子目录,这里为了和我们ALIENTEK
工程结构保持一致,我们重新选择存放到目录OBJ
目录之下。
这里我们还要勾上“Create HEX File”
选项和Browse Information
选项。Create HEX File
选项选上是要求编译之后生成HEX
文件。 -
接下来,在编译之前,我们先把
main.c
文件里面的内容替换为如下内容:#include "sys.h" #include "delay.h" #include "usart.h" void Delay(__IO uint32_t nCount); void Delay(__IO uint32_t nCount) { while(nCount--){} } int main(void) { GPIO_InitTypeDef GPIO_Initure; HAL_Init(); //初始化HAL库 Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz __HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟 GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1; //PB1,0 GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 HAL_GPIO_Init(GPIOB,&GPIO_Initure); while(1) { HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET); //PB1置1 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET); //PB0置1 Delay(0x7FFFFF); HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET); //PB1置0 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET); //PB0置0 Delay(0x7FFFFF); } }
-
点击编译按钮编译工程,可以看到工程编译通过没有任何错误和警告。
-
到这里,一个基于
HAL
库的工程模板就建立完成,同时在工程的OBJ
目录下面生成了对应的hex
文件。 -
这里还一个地方需要修改一下,那就是关于系统初始化之后的中断优先级分组组号的设置。默认情况下调用
HAL
初始化函数HAL_Init
之后,会设置分组为组 4,这里正点原子所有实验使用的是分组 2,所以我们修改HAL_Init
函数内部,重新设置分组为组 2 即可。具体方法是:打开HALLIB
分组之下的stm32f4xx_hal.c
文件,搜索函数HAL_Init
,找到函数体,里面默认有这样一行代码:HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
我们将入口参数
NVIC_PRIORITYGROUP_4
修改为NVIC_PRIORITYGROUP_2
即可。