当前位置: 首页 > news >正文

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. 深拷贝 和 浅拷贝的区别 浅拷贝是指针拷贝&#xf…...

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…...

IDEA的基础使用——【初识IDEA】

IDEA的基础使用——【初识IDEA】 文章目录 IDEA简介前言官网 IDEA的下载与安装选择下载路径勾选自己需要的其余按默认选项进行即可 目录简介安装目录简介 运行Hello WorldIDEA快捷键常用模板模板一:psvm(main)模板二:模板三&#…...

LeetCode刷题总结-动态规划篇

LeetCode刷题总结-动态规划篇 本文总结LeetCode上有动态规划的算法题,推荐刷题总数为54道。具体考点分析如下图: 1.中心扩展法 题号:132. 分割回文串 II,难度困难 2.背包问题 题号:140. 单词拆分 II,难…...

el-table使用xlsx实现导入文件编辑功能

需求:列表根据xlsx文件导入后,和列表进行对比,之后实现编辑功能 1.下载xlsx 我下的是之前的版本,新版不知道兼不兼容,这个包900多k npm install xlsx0.14.5 2.在需要使用表格导入的页面引入 import XLSX from &quo…...

Android9、11 有线网络开关设置

Android9、11 有线网络开关设置 Android9、11 有线网络开关设置_android 以太网开关_峥嵘life的博客-CSDN博客...

【MySQL】mysql问题 | [ERROR] unknown variable ‘column-statistics=0‘

一、说明 1、用到一个开源项目,dbBkTool[asurplus] 2、这个项目用于MySQL定时备份的 3、然后有个执行的时候,发下报错 [ERROR] unknown variable column-statistics0 二、解决 1、把MySQL客户端升级到8.0.19之后,就不报错了 2、column-stat…...

ElasticSearch 7.x

前言 elastic表示可伸缩,search表示查询。所以es的核心即为查询。通常情况下,我们的数据可以分为三类:结构化数据、非结构化数据、半结构化数据。 结构化数据:一般会用特定的结构来组织和管理数据,表现为二维表结构。…...

MVC乱码问题

RequestMapping(value "insert",produces {"text/html;charsetutf-8"}) //前端响应回去加响应头,解决乱码问题,这个还跟JSP响应头还不一样,这是响应的字符串,纯文本,那个前端的是out.Writer()对象&#xff…...

1004. 最大连续1的个数 III

题目描述&#xff1a; 主要思路&#xff1a; 刚看到这个问题首先想到的是二分答案&#xff0c;二分长度&#xff0c;然后利用滑动窗口判断是否可以达成。 class Solution { public:bool find(int x,vector<int> nums, int k){int now0;for(int i0,j0;i<nums.size();…...

【机器学习】西瓜书学习心得及课后习题参考答案—第3章线性模型

过了一遍第三章&#xff0c;大致理解了内容&#xff0c;认识了线性回归模型&#xff0c;对数几率回归模型&#xff0c;线性判别分析方法&#xff0c;以及多分类学习&#xff0c;其中有很多数学推理过程以参考他人现有思想为主&#xff0c;没有亲手去推。 术语学习 线性模型 l…...

面试官问我:一个 TCP 连接可以发多少个 HTTP 请求?我竟然回答不上来...

一道经典的面试题是从 URL 在浏览器被被输入到页面展现的过程中发生了什么&#xff0c;大多数回答都是说请求响应之后 DOM 怎么被构建&#xff0c;被绘制出来。但是你有没有想过&#xff0c;收到的 HTML 如果包含几十个图片标签&#xff0c;这些图片是以什么方式、什么顺序、建…...