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回调机制及中断回调机制详解》
相关文章:
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代表常见外设的名称为…...
Hutool工具类FileUtil----文件(夹)创建、删除、添加数据
1.文件(夹)创建 //创建文件,多级目录会循环创建出来String path "d:/hutool_test/hutool_test.txt";File touch FileUtil.touch("d:/hutool_test/hutool_test.txt");2.文件(夹)的校验 boolean isFile FileUtil.isFil…...
Flink - souce算子
水善利万物而不争,处众人之所恶,故几于道💦 目录 1. 从Java的集合中读取数据 2. 从本地文件中读取数据 3. 从HDFS中读取数据 4. 从Socket中读取数据 5. 从Kafka中读取数据 6. 自定义Source 官方文档 - Flink1.13 1. 从Java的集合中读取数据 …...
使用vue creat搭建项目
一、查看是否安装node和npm(显示版本号说明安装成功) node -v npm -v 显示版本号说明安装成功,如果没有安装,则需要先安装。 二、安装vue-cli脚手架 查看安装的版本(显示版本号说明安装成功) vue -V 三…...
面试题 -- 基础知识
文章目录 1. 深拷贝 和 浅拷贝的区别2. 懒加载模式3. frame和bounds有什么不同?4. What is push notification?推送实现 5. 什么是序列化?6. 什么是安全释放7. 响应者链8. 简述沙盒机制 1. 深拷贝 和 浅拷贝的区别 浅拷贝是指针拷贝…...
Zabbix分布式监控快速入门
目录 1 Zabbix简介1.1 软件架构1.2 版本选择1.3 功能特性 2 安装与部署2.1 时间同步需求2.2 下载仓库官方源2.3 Zabbix-Server服务端的安装2.3.1 安装MySQL2.3.1.1 创建Zabbix数据库2.3.1.2 导入Zabbix库的数据文件 2.3.2 配置zabbix_server.conf2.3.3 开启Zabbix-Server服务2.…...
基于Spring包扫描工具和MybatisPlus逆向工程组件的数据表自动同步机制
公司产品产出的项目较多。同步数据库表结构工作很麻烦。一个alter语句要跑到N个客户机上执行脚本。超级费时麻烦。介于此,原有方案是把增量脚本放到一resource包下,项目启动时执行逐行执行一次。但由于模块开发人员较多,总有那么一两个机灵鬼…...
leetcode 面试题 0106.字符串压缩
⭐️ 题目描述 🌟 leetcode链接:面试题 0106.字符串压缩 思路: 开辟一个新的空间(空间要大一点,因为可能压缩后的字符串比原字符串大),然后遍历原字符串统计当前字符的个数,再写入到…...
三、Spring源码-实例化
Spring源码-Bean的实例化 接下来我们看看Bean的实例化处理 一、BeanDefinition 首先我们来看看BeanDefinition的存放位置。因为Bean对象的实例化肯定是BeanFactory基于对应的BeanDefinition的定义来实现的,所以在这个过程中BeanDefinition是非常重要的,…...
算法的法律框架:预测未来的关键趋势
随着科技的飞速发展,算法和人工智能(AI)已成为我们社会生活的重要组成部分。然而,它们也带来了许多新的法律和道德挑战,这使得算法的法律框架变得日益重要。在这个背景下,预测未来算法法律框架的关键趋势成…...
Ubuntu Server版 之 共享文件 samba和NFS 两种方法
NFS 和 Samba NFS : linux之间资源共享 Samba: 是windows系统与Linux系统之间资源共享的 samba 安装samba 工具 sudo apt install samba 创建共享目录 sudo mkdir /home/shared sudo chmod 777 /home/shared 配置sambd sudo vim /etc/samba/smb.con…...
实时协作:团队效率倍增的关键
实时协作是指团队在当前时刻共同完成项目的能力。无论是否使用技术,都能实现这一点。然而,随着远程工作的盛行,安全的协作工具被用来让团队成员在项目和一般业务之间保持联系和同步。 传统协作与实时协作的区别 两种类型的协作最明显的区别…...
电脑选睡眠、休眠还是关机?
关机 这是大家最熟悉的。关机时,系统首先关闭所有运行中的程序,然后关闭系统后台服务。随后,系统向主板请求关机,主板断开电源的供电使能,让电源切断对绝大多数设备的供电(只剩一些内部零件仍会维持电源供应…...
算法通关村第三关——不简单的数组增删改查
线性表基础 线性表概念 线性表就是具有相同特征数据元素的一个有限序列,其中包含元素的个数称为线性表的长度 线性表类型 从不同的角度看,线性表有不同的分类 语言实现角度 顺序表有两种实现方式 一体式 分离式 一体式结构 一体式:存储信息…...
【Linux】动静态库
目录 写在前面的话 如何编写静态库库 编写静态库 ar命令 Makefile自动化形成静态库 如何使用编写的静态库 1.拷贝到系统路径中 2.指定路径搜索 如何编写动态库 编写动态库 完善Makefile 如何使用编写的动态库 指定路径搜索(不可行及原因) 环境变量LD_LIBRARY_PAT…...
《kubernetes权威指南》-第一章学习笔记
1.什么是kubernetes? kubernetes是一个全新的基于容器技术的分布式架构领先方案。 2.为什么要用kubernetes? 使用kubernetes提供的解决方案能够减少30%的开发成本,并且能够将开发人员的精力更加集中于业务本身,同时可以降低系统…...
ubuntu 18.04 磁盘太满无法进入系统
安装了一个压缩包,装了一半提示磁盘空间少导致安装失败。我也没在意,退出虚拟机打算扩展硬盘。等我在虚拟机设置中完成扩展操作,准备进入虚拟机内部进行操作时,发现登录不进去了 shift 登入GUN GRUB设置项的问题 网上都是在开机…...
基于LNMP配置WordPress建站时出现的问题汇总
目录 wordpress上传文件报错问题描述原因分析:解决方案: wordpress裁剪图片报错问题描述原因分析:解决方案: 配置固定链接和伪链接 wordpress上传文件报错 WP内部错误,在上传文件时发生了错误,显示权限不足…...
【Spring Cloud】Gateway的配置与使用
文章目录 前言第一步,创建一个springboot工程第二步,添加依赖第三步,编写yml文件第四步,启动主启动类总结 前言 Gateway其实是springcloud 原生的东西,但是我还是想放在这里讲,因为我们使用nacos时&#x…...
概念、框架简介--ruoyi学习(一)
开始进行ruoyi框架的学习,比起其他的前后端不分离的,这个起码看的清晰一些吧。 这一节主要是看了ruoyi的官方文档后,记录了以下不懂的概念,并且整理了ruoyi框架中的相关内容。 一些概念 前端 store store是状态管理库&#x…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
