STM32使用HAL库中外设初始化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代表常见外设的名称为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 configuration
	GPIO_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;			//PA9
		GPIO_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;	  // 复用为USART1
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	  // 初始化PA9  	
 
		GPIO_Initure.Pin=GPIO_PIN_10;	//PA10
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);  // 初始化PA10	   	
		
#if EN_USART1_RX
		HAL_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 initialization
	UART1_Handler.Instance=USART1;					    // USART1
	UART1_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() 会使能UART1
	
	HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);
// 该函数会开启接收中断,标志位UART_IT_RXNE,并设置接收缓冲以及接收缓冲的最大接收数量
  
}

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

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

STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解,STM32,stm32,单片机,嵌入式硬件

跳转到文件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);
	}
	}

这里插入正点原子的图进行解释:
STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解,STM32,stm32,单片机,嵌入式硬件

一个项目首先是引导程序先运行,汇编函数会引导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()回调函数中。初始化函数会自动调用回调函数.
STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解,STM32,stm32,单片机,嵌入式硬件
STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解,STM32,stm32,单片机,嵌入式硬件

HAL库中断回调机制

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

STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解,STM32,stm32,单片机,嵌入式硬件
HAL_PPP_IRQHandler();公共中断处理函数,它会自动调用中断处理回调函数HAL_PPP_Callback()
用户要写在中断服务处理函数中的逻辑代码要放在回调函数中,公共中断处理函数会帮你清除中断标志,并且自动调用回调函数
STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解,STM32,stm32,单片机,嵌入式硬件
参考原文:《STM32HAL库中外设初始化MSP回调机制及中断回调机制详解》文章来源地址https://www.toymoban.com/news/detail-649622.html

到了这里,关于STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32 cubemx CAN STM32 CAN初始化详解

    接收用到的结构体如下: CAN概念:         全称Controller Area Network,是一种半双工,异步通讯。 物理层:         闭环:允许总线最长40m,最高速1Mbps,规定总线两端各有一个120Ω电阻,闭环        开环:最大传输距离1Km,最高速125Kbps,规定每根线串联一个2.2kΩ的电阻,

    2024年02月13日
    浏览(47)
  • STM32的GPIO初始化配置-学习笔记

            由于刚开始没有学懂GPIO的配置原理,导致后面学习其它外设的时候总是产生阻碍,因为其它外设要使用前,大部分都要配置GPIO的初始化,因此这几天重新学习了一遍GPIO的配置,记录如下。         首先我们要知道芯片上的引脚,并不是只有GPIO的功能,还能复用成

    2024年04月17日
    浏览(44)
  • STM32 串口的初始化(内附详细代码)

    首先我们先要根据原理图来确认我们用的串口接到了那个引脚  我这边的串口1为例,接收端是PA10,发送端是PA9首先我们需要配置PA9和PA10. 把接受端配置成浮空输入,完全靠引脚来判断。把发送端配置成复用推挽模式,并打开GPIOA的时钟和复用时钟多的看代码吧,我把注释都写

    2024年02月13日
    浏览(44)
  • STM32 GPIO设置(GPIO初始化)学习笔记

    GPIO 都知道是 通用输入输出接口 的意思就不详细解释 那么我们就直接进入怎么设置GPIO接口: 这里我的编译软件是keil5,相信大家都应该知道stm32有各种的工作模式上拉、下拉、推挽、开漏等等。如果想要了解具体的工作模式原理这里我推荐大家看:推挽 开漏 高阻 这都是谁

    2024年03月28日
    浏览(49)
  • STM32—TIM定时器初始化结构体详解

      注:高级控制定时器可以用到所有初始化结构体,通用定时器不能使用 TIM_BDTRInitTypeDef 结构体,基本定时器只能使用时基结构体。    时基结构体TIM_TimeBaseInitTypeDef用于定时器基础参数设置,与TIM_TimeBaseInit函数配合使用完成配置。 (1) TIM_Prescaler:定时器预分频器设置,

    2024年02月02日
    浏览(41)
  • STM32单片机同时初始化GPIOA和GPIOB

    要同时初始化STM32F1xx的GPIOA和GPIOB,您可以按照以下步骤进行: 首先,在代码中包含stm32f1xx.h头文件 , 例如: 然后,使能GPIOA和GPIOB的时钟 ,例如: 这将使能GPIOA和GPIOB的时钟,以便进行配置和使用。需要注意的是,STM32F103C8T6使用APB2总线驱动GPIOA和GPIOB。 接下来,设置GPIOA和

    2024年02月14日
    浏览(37)
  • STM32/GD32学习指南-踩坑之(一)外部晶振配置,初始化失败,不起振

    GD32使用外部有源晶振和无源晶振的问题,型号为GD32 F450 一、GD32配置使用外部晶振 1.使用外部无源晶振 找到startup_gd32f450_470.s汇编文件,找到SystemInit()函数跳转进去 在底部找到system_clock_config()函数,再次跳转进去 选中宏定义:__SYSTEM_CLOCK_200M_PLL_IRC16M,跳转,如图 将内部时钟

    2024年02月13日
    浏览(43)
  • STM32CubeMX v6.9.0 BUG:FLASH_LATENCY设置错误导致初始化失败

    今天在调试外设功能时,发现设置了使用外部时钟之后程序运行异常,进行追踪调试并与先前可以正常运行的项目进行对比之后发现这个问题可能是由于新版本的STM32CubeMX配置生成代码时的BUG引起的。 MCU: STM32H750VBT6 STM32CubeIDE: Version: 1.13.0 Build: 17399_20230707_0829 (UTC) STM32CubeMX: v

    2024年02月15日
    浏览(48)
  • 【STM32&RT-Thread零基础入门】 5. 线程创建应用(线程创建、删除、初始化、脱离、启动、睡眠)

    硬件:STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线 本章主要讲线程的工作机制和管理方法,通过实例讲解如何使用多线程完成多任务开发。 RT-Thread用线程控制块来描述和管理一个线程,一个线程对应一个线程控制块。线程控

    2024年02月12日
    浏览(46)
  • 通用输入输出端口GPIO,及其初始化(HAL库)

    我在学习STM32时候呢,是直接先接触的STM32CubeMX软件,更着网上各种教程迷迷糊糊学了一大堆没用的东西,于是先一步步来吧,我总结了很长时间,希望对正在学习相关知识的朋友们有帮助。 可以先去看看STM32CubeMX如何配置:传送门 读完以上我写的文章基本上是蒙的,因为我

    2024年02月09日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包