NUCLEO-G0B1RE STM32G0B1RET6的学习(3)——SPI从DMA HAL库到应用层回调函数CallBack的定义

三篇初级学习有关Nucleo的板的文章,以下预留跳转其他文章的位置:

NUCLEO-G0B1RE STM32G0B1RET6的学习(1)------STM32CubeIDE的安装、新建工程和配置硬件SPI-CSDN博客

NUCLEO-G0B1RE STM32G0B1RET6的学习(2)------与W25Q32的SPI通信:读取ID、擦除、写和读/解决SPI通信使用模式3的首帧异常的问题-CSDN博客

NUCLEO-G0B1RE STM32G0B1RET6的学习(3)------SPI从DMA HAL库到应用层回调函数CallBack的定义-CSDN博客

本篇介绍SPI从DMA HAL库到应用层回调函数的定义,Los geht's~

目录

分析HAL库的回调函数调用

应用层回调函数CallBack的定义


分析HAL库的回调函数调用

本文所述SPI是有使用DMA的情况下,具体设置IOC可看前两篇博文

在传输完成后,会触发DMA中断,会调用就是以下函数:

void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)

回调的是hdma->XferCpltCallback(hdma);

通过查询功能,搜索XferCpltCallback,可以看到在以下函数定义中进行注册,

HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)

HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)

HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)

总结来说,我们调用HAL_SPI_Transmit_DMA函数,或调用HAL_SPI_Receive_DMA函数,或调用HAL_SPI_TransmitReceive_DMA函数来传输数据,就会把相应的回调函数SPI_DMATransmitCplt、SPI_DMAReceiveCplt、SPI_DMATransmitReceiveCplt的函数指针赋值给XferCpltCallback函数指针,然后在传输完成调用HAL_DMA_IRQHandler中断函数时,调用XferCpltCallback,通过函数指针,实际执行的是相应的回调函数SPI_DMATransmitCplt、SPI_DMAReceiveCplt、SPI_DMATransmitReceiveCplt(具体调用哪个回调函数,看一开始使用哪个传输数据的HAL库函数)

就以我在上两篇博文为例,我使用的是HAL_SPI_TransmitReceive_DMA来进行数据传输

那么对应的是SPI_DMATransmitReceiveCplt函数,在通过搜索该函数,查看其定义:

最终调用的是HAL_SPI_TxRxCallback(hspi);

关系如下:

|-----------------------------------------------------------------|-----------------------------|
| 调用关系(定义中调用) | 注册点(函数指针赋值) |
| HAL_DMA_IRQHandler | |
| ↓ | |
| hdma->XferCpltCallback(只是函数指针) 实际调用 SPI_DMATransmitReceiveCplt | HAL_SPI_TransmitReceive_DMA |
| ↓ | |
| HAL_SPI_TxRxCpltCallback | |

在传输完成后,通过DMA中断的调用,最终运行HAL_SPI_TxRxCallback回调函数定义的程序

所以,通过对使用哪个传输数据的HAL库函数对应的回调函数进行重写就可以做到在传输完成后,运行某一段想要运行的代码,比如SPI用户自定义的状态变化,就可以做超时判断等等,完善代码,不必在while中死等

就以我在上两篇博文为例,我使用的是HAL_SPI_TransmitReceive_DMA来进行数据传输,最终在spi.c中,重写HAL_SPI_TxRxCallback来达到回调的效果,在传输完成后,就会运行HAL_SPI_TxRxCallback函数中定义的程序

应用层回调函数CallBack的定义

spi.c可以说是属于驱动层,在实际使用中,spi调通后,不会动spi.c这一层的程序(这一层一般是初始化、基础的发送接收程序编写等等),会偏向在main.c或其他.c,这一应用层来编写用户自定义的程序,不直接调用HAL库的函数,比如用户的回调函数、发送和接收具体的温度、湿度、压力等等数据,所以会希望main.c中可以定义用户的回调函数。

那么就可以类似HAL库定义和调用回调函数的方法,来写应用层定义和调用回调函数。

关系如下:

|--------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
| 调用关系(定义中调用) | 注册点(函数指针赋值) |
| HAL_SPI_TxRxCpltCallback(spi.c中定义的函数) | |
| ↓ | |
| SPI_TxRxCallBack (spi.c中定义的函数指针变量) 实际调用 TxRxCallBack(main.c中定义的函数) | MX_SPI1_Init(spi.c中定义的函数) 实际调用 SPI_TxRxCb(spi.c中定义的函数) (如果简化的话,可以直接MX_SPI1_Init中注册,不必定义SPI_TxRxCb这一函数) |

具体代码(只体现回调函数部分的内容,注意程序要写在提示的USER CODE BEGIN和USER CODE END之间):

spi.c:

cpp 复制代码
static void (*SPI_TxRxCallBack)(SPI_HandleTypeDef* hspi) = NULL;
void SPI_TxRxCb(void(*txrxCallBack)(SPI_HandleTypeDef* hspi));

void MX_SPI1_Init(void)
{

...

  /* USER CODE BEGIN SPI1_Init 2 */

  SPI_TxRxCb(TxRxCallBack);
  /* USER CODE END SPI1_Init 2 */

}

/* USER CODE BEGIN 1 */

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if(SPI_TxRxCallBack != NULL)
	{
		SPI_TxRxCallBack(&hspi1);
	}
}

void SPI_TxRxCb(void(*txrxCallBack)(SPI_HandleTypeDef *hspi))
{
	SPI_TxRxCallBack = txrxCallBack;
}

/* USER CODE END 1 */

main.h

cpp 复制代码
void TxRxCallBack(SPI_HandleTypeDef *hspi);

main.c

cpp 复制代码
void TxRxCallBack(SPI_HandleTypeDef *hspi)
{
	//在完成发送生接收后的回调函数,可放应用层调用
	uint8_t a = 0;

	a = 1;
}

最后,在TxRxCallBack函数中增加断点,可以结合逻辑分析仪看,逻辑分析仪只扫描到了一帧数据,断点停在了TxRxCallBack函数中

工程文件(不过还是建议从第一篇博文开始自己搭工程):

https://download.csdn.net/download/dlwlrma_516/92270754

相关推荐
python百炼成钢2 小时前
13.RTC实时时钟
linux·stm32·单片机·嵌入式硬件·实时音视频
小柯博客3 小时前
STM32MP1 没有硬件编解码,如何用 CPU 实现 H.264 编码支持 WebRTC?
c语言·stm32·嵌入式硬件·webrtc·h.264·h264·v4l2
Jerry丶Li5 小时前
二十九、STM32的USART (串口发送)
stm32·单片机·嵌入式硬件
d111111111d6 小时前
STM32外设学习-串口数据包笔记-(数据包的了解)
笔记·stm32·单片机·嵌入式硬件·学习
hemama_15 小时前
STM32F103VET6开发板例程(一)-LED
stm32·单片机·嵌入式硬件
夜月yeyue16 小时前
Linux 内核驱动加载机制
linux·服务器·stm32·嵌入式硬件
炸膛坦客16 小时前
FreeRTOS 学习:(十七)“外部中断”和“内核中断”的差异,引入 FreeRTOS 中断管理
stm32·freertos·实时操作系统
d111111111d19 小时前
STM32中为什么会有APB1和APB2两个外设有什么区别
笔记·stm32·单片机·嵌入式硬件·学习
d111111111d1 天前
STM32外设学习--USART串口外设--学习笔记。
笔记·stm32·单片机·嵌入式硬件·学习