应用层的系统功能:CM启动、STM32的系统初始化、中断、HAL依赖管理、外设初始化、外设操作、业务逻辑
一、CMSIS的启动程序
CubeMX工程目录 | 系统功能函数 | STM32Cube固件包的Driver目录 |
---|---|---|
MDK-ARM\ | startup_stm32f411xe.s | CMSIS\Include\Device\ST\STM32F4xx\Source\Templates\arm |
Core\Src\ | system_stm32f4xx.c | CMSIS\Include\Device\ST\STM32F4xx\Source\Templates |
- 【厂商ST实现】STM32的启动程序:system_stm32f4xx.h、system_stm32f4xx.c
- 【厂商ST实现】CM处理器启动程序:startup_stm32f411xe.s
二、HAL的依赖管理
CubeMX工程目录 | 系统功能函数 | STM32Cube固件包的Driver目录 |
---|---|---|
Core\Inc\ | stm32f4xx_hal_conf.h |
STM32F4xx_HAL_Driver\Inc\ stm32f4xx_hal_conf_template.h(改名) |
- 编译时的条件宏:USE_HAL_DRIVER(使用HAL库)
- stm32f4xx_hal_conf.h: HAL库依赖管理(利用条件编译引入PPP的依赖文件hal_ppp.h和hal_ppp.c)
#define HAL_PPP_MODULE_ENABLED
三、HAL的外设中断
CubeMX工程目录 | 系统功能函数 | STM32Cube固件包的Driver目录 |
---|---|---|
Core\Inc\ | stm32f4xx_it.h | STM32F4xx_HAL_Driver\Inc\ |
Core\Src\ | stm32f4xx_it.c | STM32F4xx_HAL_Driver\Src\ |
中断回调(中断向量→CMSIS的中断回调函数→HAL库外设中断回调函数→前台程序)
-
startup_<device>.s
:定义<vector>_Handler
、并作为中断向量 -
<device>_it.c
:对中断回调函数声明并定义_Handler -
<device>_hal_ppp
.c:定义HAL_PPP_IRQHandler -
main.c
:具体的回调函数
HAL_GPIO_EXTI_Callback(GPIO_PIN_y);
HAL_TIM_PeriodElapsedCallback(&htimx);
/**
******************************************************************************
* 由CubeMX生成的中断相关代码
* 在<device>_hal_it.h和<device>_hal_it.c文件中
******************************************************************************
*/
// 头文件中定义
void EXTIx_IRQHandler(void);
void PPP_IRQHandler(void);
//引用外部变量:在main.c中定义的外设初始化全局变量
extern PPP_HandleTypeDef hpppx;
//调用HAL库定义的外设通用中断回调函数:HAL_PPP_IRQHandler
void EXTIx_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_x);
}
void PPP_IRQHandler(void)
{
HAL_PPP_IRQHandler(&hpppx);
}
四、外设初始化、业务流程
CubeMX工程目录 | 系统功能函数 | STM32Cube固件包的Driver目录 |
---|---|---|
Core\Src\ | stm32f4xx_hal_msp.c | |
Core\Src\ | main.c | |
Core\Inc\ | main.h |
1. main.c
- 定义全局变量XxxFlag:作为某个中断是否产生的标志
- MCU初始化、配置系统时钟
- 外设初始化:使能时钟、初始化变量定义、初始化变量赋值、初始化函数调用、设置中断优先级、中断使能
- 可选:清中断、启动外设
- 后台程序(业务逻辑:判断XxxFlag并清除,进行业务处理)
- 前台程序(中断回调:赋值XxxFlag)、用户自定义函数
#include "main.h"
//USER CODE Includes:头文件
//USER CODE PTD:数据类型typedef
//USER CODE PD:常量定义define
//USER CODE PM:宏函数定义macro
// 定义外设句柄全局变量
PPP_HandleTypeDef hpppx; //初始化全局变量
//USER CODE PV:变量定义variables
// 声明外设初始化函数
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_PPPx_Init(void);
//USER CODE PFP:函数声明function prototypes
//USER CODE0
int main(void)
{
//USER CODE1
//设置MCU
HAL_Init(); //MCU初始化:复位全部外设,初始化flash接口和systick
//USER CODE Init
SystemClock_Config(); //配置系统时钟
//USER CODE SysInit
//调用外设初始化函数
MX_GPIO_Init();
MX_PPPx_Init();
//...
//USER CODE2:外设启动
//无限循环
while (1)
{
//USER CODE3:后台程序(无限循环)
}
}
//USER CODE4:用户函数定义或前台程序(定义中断回调函数)
/**
******************************************************************************
* 时钟树
* HCLK为高速总线时钟:CortexM处理器、存储器、DMA
* - HCLK→APB分频器→PCLK→APB peripheral clock(APB的外设时钟)
* - HCLK→APB分频器→PCLK→倍频器→APB Timer Clock(APB的定时器时钟:TIM_CLK)
******************************************************************************
*/
// CubeMX根据Clock Configuration配置生成
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
//配置输出电压
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
//初始化时钟振荡器:根据RCC_OscInitTypeDef结构体的参数
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 100;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
//初始化CortexM的CPU、STM32的AHB、APB总线时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
{
Error_Handler();
}
}
// CubeMX根据PinOut&Configuration配置生成
// 使用HAL库:对GPIO的多个端口进行初始化设置
static void MX_GPIO_Init(void)
{
//【可选】设置GPIOx的GPIO_PIN_y引脚的初始电平
HAL_GPIO_WritePin(GPIOx, GPIO_PIN_y, 初始电平);
//GPIOx端口时钟使能
__HAL_RCC_GPIOx_CLK_ENABLE();
//【可选】GPIOx端口参数配置
GPIO_InitTypeDef GPIO_InitStruct = {0}; //初始化变量
GPIO_InitStruct.各种参数 = 参数值; //初始化变量赋值
HAL_GPIO_Init(GPIOx, &GPIO_InitStruct); //调用外设初始化函数
//【可选】GPIO_EXIT中断初始化
HAL_NVIC_SetPriority(EXTIy_IRQn, 抢占优先级, 子优先级); //设置中断优先级
HAL_NVIC_EnableIRQ(EXTIy_IRQn); //使能中断
}
// 使用HAL库:对某个外设PPP进行初始化设置
static void MX_PPPx_Init(void)
{
//对于多数外设PPP:UART
hpppx.Instance = PPPx; //外设对象
hpppx.Init.各种参数 = 参数值; //初始化变量赋值
if (HAL_PPP_Init(&hpppx) != HAL_OK)
{ //调用外设初始化函数:调用HAL_PPP_MspInit(&hpppx)
Error_Handler();
}
//可选:PPP内部多个模块Xxx单独初始化
PPP_XxxConfigTypeDef sXxxConfig = {0}; //初始化变量
sXxxConfig.各种参数 = 参数值; //初始化变量赋值
if (HAL_PPP_ConfigXxx(&hpppx, &sXxxConfig) != HAL_OK)
{ //调用外设初始化函数
Error_Handler();
}
}
// 当错误发生时执行的回调函数
void Error_Handler(void)
{
//USER CODE Error_Handler_Debug
//用户可自行实现:上报HAL错误返回状态
__disable_irq();
while (1)
{
}
}
2.hal_msp.c
初始化(与具体芯片型号相关的配置:将外设时钟使能、GPIO时钟使能和GPIO引脚复用、外设中断初始化)
/**
******************************************************************************
* 由CubeMX生成的外设MSP的相关代码
* 在<device>_hal_msp.c文件中
* PPP可以为UART、TIM_Base
******************************************************************************
*/
void HAL_PPP_MspInit(PPP_HandleTypeDef *hppp)
{
if (hppp->Instance == PPPx)
{
//PPP外设时钟使能
__HAL_RCC_PPPx_CLK_ENABLE();
//【可选】将GPIO引脚复用为PPP外设的引脚
__HAL_RCC_GPIOx_CLK_ENABLE(); //GPIO时钟使能
GPIO_InitTypeDef GPIO_InitStruct = {0}; //初始化变量
GPIO_InitStruct.各种参数 = 参数值; //初始化变量赋值
HAL_GPIO_Init(GPIOx, &GPIO_InitStruct); //调用GPIO初始化函数
//【可选】PPP外设中断初始化
HAL_NVIC_SetPriority(PPPx_IRQn, 抢占优先级, 子优先级); //设置中断优先级
HAL_NVIC_EnableIRQ(PPPx_IRQn); //使能中断
}
}
void HAL_PPP_MspDeInit(PPP_HandleTypeDef *hppp)
{
if (hppp->Instance == PPPx)
{
// 外设时钟失能
__HAL_RCC_PPPx_CLK_DISABLE();
// 【可选】将GPIO引脚复位
HAL_GPIO_DeInit(GPIOx, GPIO_PIN_y);
//【可选】PPP外设中断禁用
HAL_PPP_DisableIRQ(PPPx_IRQn);
}
}