STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解

STM32使用HAL库之Msp回调函数

1.问题提出

在STM32的HAL库使用中,会发现库函数大都被设计成了一对:

HAL_PPP/PPPP_Init

HAL_PPP/PPPP_MspInit

而且HAL_PPP/PPPP_MspInit函数的defination前面还会有__weak关键字

上面的PPP/PPPP代表常见外设的名称为3个字符或者4个字符

怎么理解这个设计呢?

2.问题分析

2.1 结论

首先说结论:

  • HAL_PPP/PPPP_Init 是与具体芯片(无论是STM32F4/F1/F7)无关的设置

  • HAL_PPP/PPPP_MspInit 是与具体芯片相关的配置(如STM32F429IGTx)

这样的设计是将不变的东西以库函数HAL_PPP/PPPP_Init的形式固定下来,而将需要用户根据

芯片进行编写的部分抽象成函数HAL_PPP/PPPP_MspInit的形式,用户只需要编写这部分函数

即可,这样做减少了用户的代码编写量

__weak关键字的使用是定义一个弱函数,这个函数的函数体通常是空的

方便用户重写一个自己的函数HAL_PPP/PPPP_MspInit,来覆盖之前库函数中定义的函数带有

__weak关键字的HAL_PPP/PPPP_MspInit函数,编译器在编译的时候,如果检查到有重名的

(但不含__weak关键字)HAL_PPP/PPPP_MspInit的函数,此时就会默认编译这个用户写的函数

2.2 实例分析

下面以串口通信为例进行分析:

在编写串口通信的代码的时候,常使用正点原子提供的usart.c&usart.h组合,正点原子在usart.c中

定义了HAL_UART_MspInit作为回调函数:

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{// GPIO configurationGPIO_InitTypeDef GPIO_Initure;if(huart->Instance==USART1){__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA 时钟			__HAL_RCC_USART1_CLK_ENABLE();	// 使能USART1 时钟	GPIO_Initure.Pin=GPIO_PIN_9;			//PA9GPIO_Initure.Mode=GPIO_MODE_AF_PP;	// AF复用,PP为推挽(push pull)	GPIO_Initure.Pull=GPIO_PULLUP;  // 设置上拉GPIO_Initure.Speed=GPIO_SPEED_FAST;  // 设置为高速GPIO_Initure.Alternate=GPIO_AF7_USART1;	  // 复用为USART1HAL_GPIO_Init(GPIOA,&GPIO_Initure);	  // 初始化PA9  	GPIO_Initure.Pin=GPIO_PIN_10;	//PA10HAL_GPIO_Init(GPIOA,&GPIO_Initure);  // 初始化PA10	   	#if EN_USART1_RXHAL_NVIC_EnableIRQ(USART1_IRQn);  // 使能USART1中断通道			HAL_NVIC_SetPriority(USART1_IRQn,3,3);	// 抢占优先级3, 子优先级3		
#endif	}}

这个库同时提供了一个调用串口初始化的接口:void uart_init(u32 bound) // bound为波特率

void uart_init(u32 bound)
{	//UART initializationUART1_Handler.Instance=USART1;					    // USART1UART1_Handler.Init.BaudRate=bound;				    // 设置波特率UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B;   // 字长为8位的数据格式UART1_Handler.Init.StopBits=UART_STOPBITS_1;	    // 一个停止位UART1_Handler.Init.Parity=UART_PARITY_NONE;		    // 无奇偶校验位UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;   // 无硬件流控UART1_Handler.Init.Mode=UART_MODE_TX_RX;		    // 收发模式HAL_UART_Init(&UART1_Handler);					    // HAL_UART_Init() 会使能UART1HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);
// 该函数会开启接收中断,标志位UART_IT_RXNE,并设置接收缓冲以及接收缓冲的最大接收数量}

这样在main函数中,首先调用函数uart_init()

然后uart_init()函数就会去调用HAL_UART_Init,这个函数就是HAL库中的函数

在这里插入图片描述

跳转到文件stm32f4xx_hal_uart.c,找到函数HAL_UART_Init的定义:

/*** @brief  Initializes the UART mode according to the specified parameters in*         the UART_InitTypeDef and create the associated handle.* @param  huart: pointer to a UART_HandleTypeDef structure that contains*                the configuration information for the specified UART module.* @retval HAL status*/
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
{/* Check the UART handle allocation */if(huart == NULL){return HAL_ERROR;}/* Check the parameters */if(huart->Init.HwFlowCtl != UART_HWCONTROL_NONE){ /* The hardware flow control is available only for USART1, USART2, USART3 and USART6 */assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance));assert_param(IS_UART_HARDWARE_FLOW_CONTROL(huart->Init.HwFlowCtl));}else{assert_param(IS_UART_INSTANCE(huart->Instance));}assert_param(IS_UART_WORD_LENGTH(huart->Init.WordLength));assert_param(IS_UART_OVERSAMPLING(huart->Init.OverSampling));if(huart->gState == HAL_UART_STATE_RESET){  /* Allocate lock resource and initialize it */huart->Lock = HAL_UNLOCKED;/* Init the low level hardware */HAL_UART_MspInit(huart);}huart->gState = HAL_UART_STATE_BUSY;/* Disable the peripheral */__HAL_UART_DISABLE(huart);/* Set the UART Communication parameters */UART_SetConfig(huart);/* In asynchronous mode, the following bits must be kept cleared: - LINEN and CLKEN bits in the USART_CR2 register,- SCEN, HDSEL and IREN  bits in the USART_CR3 register.*/huart->Instance->CR2 &= ~(USART_CR2_LINEN | USART_CR2_CLKEN);huart->Instance->CR3 &= ~(USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN);/* Enable the peripheral */__HAL_UART_ENABLE(huart);/* Initialize the UART state */huart->ErrorCode = HAL_UART_ERROR_NONE;huart->gState= HAL_UART_STATE_READY;huart->RxState= HAL_UART_STATE_READY;return HAL_OK;
}

可以看到函数HAL_UART_Init中调用了函数HAL_UART_MspInit

在库文件中本身是有一个同名的使用__weak关键字定义的函数,

/*** @brief  UART MSP Init.* @param  huart: pointer to a UART_HandleTypeDef structure that contains*                the configuration information for the specified UART module.* @retval None*/__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{/* Prevent unused argument(s) compilation warning */UNUSED(huart);/* NOTE: This function Should not be modified, when the callback is needed,the HAL_UART_MspInit could be implemented in the user file*/ 
}

由于使用了正点原子的库,所以编译器在编译的时候就不会再编译这个HAL库自带的函数HAL_UART_MspInit

而是编译引入的库函数HAL_UART_MspInit

3. STM32程序的一般执行流程

由上面1.2节的分析,对于一个真实的STM32应用程序可以总结其运行一般执行(编写)流程如下:

以一个真实的点亮跑马灯的main.c为例进行分析(工程使用HAL库):

#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();                     Stm32_Clock_Init(360,25,2,8);   __HAL_RCC_GPIOB_CLK_ENABLE();        GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1; 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);	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);				Delay(0x7FFFFF);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);	Delay(0x7FFFFF);}}

这里插入正点原子的图进行解释:
在这里插入图片描述

一个项目首先是引导程序先运行,汇编函数会引导SystemInit函数进行系统初始化的设置,再HAL库版本的项目中有这个函数的定义,在寄存器版本中通常会将汇编代码中引导SystemInit函数的语句删掉。然后引导程序会引导main函数,main函数被引导完成之后就会开始执行用户写的main函数中的代码。然后HAL_Init()函数会调用函数进行全局的MSP初始化,然后调用了正点原子提供的库函数Stm32_Clock_init函数,这个函数调用HAL_RCC_Oscconfig和HAL_RCC_ClockConfig函数进行系统时钟初始化,使用该函数需要导入SYSTEM库(正点原子提供),上面的一系列初始化都是常规操作,也就是每一个项目必做的系统的初始化。下面正式进入了用户自己编写得到逻辑,假设用户要使用PPP外设,那么就会调用HAL库中的函数HAL_PPP_Init,这个函数又会去尝试调用用户自定义的HAL_PPP_MspInit,然后进入用户自己定义的逻辑。
————————————————
原文链接:《[STM32] NOTE07-STM32使用HAL库之Msp回调函数理解》

STM32HAL库中外设初始化MSP回调机制及中断回调机制详解

我们开始学习HAL库的过程中,一定会发现与固件库开发中外设初始化流程和中断处理机制不相同,在这里将为大家解答一下心中的译文。

HAL外设初始化MSP回调机制

在外设初始化函数中,HAL_PPP_Init();中需配置外设的相关参数,外设用到的IO和NVIC和时钟等放到HAL_PPP_MspInit()回调函数中。初始化函数会自动调用回调函数.
在这里插入图片描述
在这里插入图片描述

HAL库中断回调机制

HAL库中中断处理机制与固件库中不同,他是经过公共中断处理函数,自动调用中断处理回调函数。用户想要再中断中实现的逻辑代码则要放在回调函数中,而公共中断处理函数会帮你检测是否有中断发生,并帮你清除中断标志位。

在这里插入图片描述
HAL_PPP_IRQHandler();公共中断处理函数,它会自动调用中断处理回调函数HAL_PPP_Callback()
用户要写在中断服务处理函数中的逻辑代码要放在回调函数中,公共中断处理函数会帮你清除中断标志,并且自动调用回调函数
在这里插入图片描述
参考原文:《STM32HAL库中外设初始化MSP回调机制及中断回调机制详解》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/53106.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】MAT: Mask-Aware Transformer for Large Hole Image Inpainting

论文:https://arxiv.org/abs/2203.15270 代码:https://github.com/fenglinglwb/MAT 文章目录 PSAbstractIntroductionRelated WorkMethod总体架构卷积头Transformer主体Adjusted Transformer Block Multi-Head Contextual Attention Style Manipulation …

PHP实现首字母头像

<?php $name"哈哈"; $logoletter_avatar($name);echo <img src".$logo." style" border-radius: 50%;">;function letter_avatar($text) {$total unpack(L, hash(adler32, $text, true))[1];$hue $total % 360;list($r, $g, $b) hs…

将word每页页眉单独设置

在进行论文排版的时候&#xff0c;总是会出现页眉的页码设置问题&#xff0c;比如出现奇数或偶数页码一致&#xff0c;尝试将前面页码改掉&#xff0c;后面再修改前面也进行了变动&#xff0c;将每页页眉单独设置&#xff1a; &#xff08;1&#xff09;在第一页的最后一行输入…

设计模式概述与UML图

文章目录 一、设计模式概述1. 软件设计模式的产生背景2. 软件设计模式的概念3. 学习设计模式的必要性4. 设计模式分类&#xff08;1&#xff09;创建型模式&#xff08;2&#xff09;结构型模式&#xff08;3&#xff09;行为型模式 二、UML图1. 类图概述2. 类图作用3. 类图表示…

视频汇聚平台EasyCVR视频广场侧边栏支持拖拽

为了提升用户体验以及让平台的操作更加符合用户使用习惯&#xff0c;我们在EasyCVR v3.3版本中&#xff0c;支持面包屑侧边栏的广场视频、分组列表、收藏这三个模块拖拽排序&#xff0c;并且该操作在视频广场、视频调阅、电子地图、录像回放等页面均能支持。 TSINGSEE青犀视频…

Linux - 进程概念(进程状态、优先级)

1.进程状态 操作系统中进程有多种状态模型 三态模型 进程状态分为 就绪态&#xff0c;执行态&#xff0c;阻塞态。 就绪(Ready)状态&#xff1a;指进程已处于准备好运行的状态&#xff0c;即进程已分配到除CPU以外的所有必要资源后&#xff0c;只要再获得CPU&#xff0c;便可立…

领先实践 | 打造现象级品牌传播的5个关键步骤

有效的 品牌传播 需要有效的传播策略&#xff0c;一个好的传播策略&#xff0c;不仅可以帮助企业的品牌传播聚焦资源&#xff0c;找到目标受众&#xff0c;投其所好地进行精准投放&#xff0c;还可以在出现问题的时候及时调整&#xff0c;使品牌传播的大方向不发生偏移。这篇文…

【剑指offer】双指针7题 全刷(详解)

目录 目录 目录 全部题目链接地址 [简单]剑指 Offer 18. 删除链表的节点 题目 方法 [简单]剑指 Offer 22. 链表中倒数第k个节点 题目 方法&#xff1a;双指针距离法 [简单]剑指 Offer 25. 合并两个排序的链表 题目 方法&#xff1a;双指针 [简单]剑指 Offer 52. 两…

Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本

Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本 引言 在 Java 生态系统中&#xff0c;Spring Boot、Spring Cloud 和 Spring Alibaba 是非常流行的框架&#xff0c;它们提供了丰富的功能和优雅的解决方案。然而&#xff0c;随着不断的发展和更新&…

.Net6 Web Core API 配置 Autofac 封装 --- 依赖注入

目录 一、NuGet 包导入 二、Autofac 封装类 三、Autofac 使用 四、案例测试 下列封装 采取程序集注入方法, 单个依赖注入, 也适用, 可<依赖注入>的地方配置 一、NuGet 包导入 Autofac Autofac.Extensions.DependencyInjection Autofac.Extras.DynamicProxy 二、Auto…

应急响应-主机后门webshell的排查思路(webshell,启动项,隐藏账户,映像劫持,rootkit后门)

0x00 windows主机后门排查思路 针对主机后门windows&#xff0c;linux&#xff0c;在对方植入webshell后&#xff0c;需要立即响应&#xff0c;排查出后门位置&#xff0c;以及排查对外连接&#xff0c;端口使用情况等等 排查对外连接状态&#xff1a; 借助工具&#xff1a;p…

【Ansible】Ansible自动化运维工具之playbook剧本搭建LNMP架构

LNMP 一、playbooks 分布式部署 LNMP1. 环境配置2. 安装 ansble3. 安装 nginx3.1 准备 nginx 相关文件3.2 编写 lnmp.yaml 的 nginx 部分3.3 测试 nginx4. 安装 mysql4.1 准备 mysql 相关文件4.2 编写 lnmp.yaml 的 mysql 部分4.3 测试 mysql5. 安装 php5.1 编写 lnmp.yaml 的 …