搞懂收发模式(Transmit、Receive、IT、DMA、ToIdle、Abort、Callback)

搞懂收发模式(Transmit、Receive、IT、DMA、ToIdle、Abort)


截取了一段<<stm32f1xx_hal_uart.h>>里面的代码

/** @addtogroup UART_Exported_Functions_Group2 IO operation functions
  * @{
  */

/* IO operation functions *******************************************************/
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart);

HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, 
                                           uint16_t *RxLen, uint32_t Timeout);
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

HAL_UART_RxEventTypeTypeDef HAL_UARTEx_GetRxEventType(UART_HandleTypeDef *huart);

/* Transfer Abort functions */
HAL_StatusTypeDef HAL_UART_Abort(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortTransmit(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortReceive(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_Abort_IT(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortTransmit_IT(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortReceive_IT(UART_HandleTypeDef *huart);

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);
void HAL_UART_AbortCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart);

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);

大致可以分成几类:
1、阻塞模式(普通模式)
2、非阻塞模式( IT )
3、直接内存访问模式( DMA )
4、扩展接收模式( ToIdle )
5、中止收发( Abort )
6、中断服务函数的回调函数(Callback)
接下来一一讲解,这里以串口为例,其他外设触类旁通


1、阻塞模式(阻塞轮询)

在这里插入图片描述
阻塞式函数
用于进行UART通信的,会等待操作完成,直到数据传输或接收指定数量的字节。

  • HAL_UART_Transmit:使用阻塞轮询方式通过UART发送数据,函数会一直等待直到所有数据发送完成或超时。
  • HAL_UART_Receive:使用阻塞轮询方式通过UART接收数据,函数会一直等待直到所有数据接收完成或超时。

缺点:阻塞模式可能会导致CPU资源的浪费
优点:就是避免数据大量涌入,导致服务器崩溃

在这里插入图片描述


2、非阻塞模式( IT )

在这里插入图片描述

基于中断的非阻塞式函数
启动传输或接收过程后立即返回,当操作完成时会触发中断,并在相应的回调函数中处理完成事件。

  • HAL_UART_Transmit_IT:通过中断非阻塞方式发送数据,不阻塞调用线程,当数据发送完成后,会触发相应的中断服务例程。
  • HAL_UART_Receive_IT:通过中断非阻塞方式接收数据,不阻塞调用线程,当数据接收完成后,会触发相应的中断服务例程。

中断非阻塞方式,可以在等待I/O操作完成的同时,执行其他任务,提高了效率


3、直接内存访问模式( DMA )


通过/控制DMA(直接内存访问)进行UART传输和接收的函数

  • HAL_UART_Transmit_DMA:启动一个DMA传输,用于通过UART发送数据。
  • HAL_UART_Receive_DMA:启动一个DMA传输,用于通过UART接收数据。
  • HAL_UART_DMAPause:暂停当前正在进行的DMA传输。
  • HAL_UART_DMAResume:恢复之前暂停的DMA传输。
  • HAL_UART_DMAStop:停止当前正在进行的DMA传输。

这些函数允许用户更灵活地控制UART的DMA传输过程
可以在需要时 暂停恢复停止 传输,而 不需要等待传输自然完成
利用DMA控制器在后台处理数据传输,不需要CPU的持续干预,可以在传输或接收过程中执行其他任务。


4、扩展接收模式( ToIdle )

在这里插入图片描述

UART扩展功能,提供了额外的接收模式
用于在 接收到指定数量的数据发生IDLE事件 时停止接收 。

  • HAL_UARTEx_ReceiveToIdle:阻塞接收模式,当接收到指定数量的数据或发生IDLE事件时,停止接收。
  • HAL_UARTEx_ReceiveToIdle_DMA:DMA接收模式,当接收到指定数量的数据或发生IDLE事件时,DMA传输结束。
  • HAL_UARTEx_ReceiveToIdle_IT:中断接收模式,当接收到指定数量的数据或发生IDLE事件时,触发中断并调用中断服务函数

这些函数适用于需要在特定条件下停止接收数据的情况
例如,当UART线路空闲时或者接收到一定数量的数据后,可以停止接收,
这在处理特定长度的数据包或者在数据接收完成后立即停止接收时非常有用。

PS:IDLE事件
通常指接收缓冲区中的数据已接收完毕,没有数据可以接收,此时UART线路变为空闲


5、中止收发( Abort )

用于中止当前的UART传输或接收操作
使用这些函数可以确保UART操作能够被及时终止,释放相关资源,并且可以防止数据丢失或乱序。

  • HAL_UART_Abort:中止UART的所有传输和接收操作。
  • HAL_UART_AbortTransmit:中止UART的发送操作。
  • HAL_UART_AbortReceive:中止UART的接收操作。

用于中止UART的传输或接收操作。
是阻塞函数,程序会被停止,直到传输或接收操作完全中止。
在调用函数时,UART的中断会被禁用,并且UART的状态会被重置。

  • HAL_UART_Abort_IT:通过中断方式中止UART的传输和接收操作。
  • HAL_UART_AbortTransmit_IT:通过中断方式中止UART的发送操作。
  • HAL_UART_AbortReceive_IT:通过中断方式中止UART的接收操作。

同样用于中止UART的传输或接收操作。
是非阻塞函数,它会立即返回,而实际的终止操作会在UART中断服务例程(ISR)中异步执行。
调用此函数后,UART的中断不会被立即禁用,而是在中断中处理中止操作。

应用场景:
1.当需要提前结束一个正在执行的传输或接收操作时。
2.当检测到错误或异常情况,需要立即停止UART通信时。
3.在传输或接收过程中,如果系统需要资源回收或者有更高优先级的任务需要处理时。


6、中断服务函数的回调函数(Callback)

在这里插入图片描述

HAL_UART_IRQHandler 是UART中断服务例程,用于处理UART中断事件。

这些是UART操作完成时的回调函数,用于在操作完成后执行用户定义的代码。
这些回调函数都是用__weak修饰(弱定义),程序员可以重写这些函数
HAL_UART_IRQHandler 会自动调用这些函数,并清除部分标志位(建议主动清标志位)

HAL_UART_TxCpltCallback				//发送完成回调函数。当UART发送完成时,会被调用。
HAL_UART_TxHalfCpltCallback			//发送一半回调函数。当UART发送一半数据时,会被调用。
HAL_UART_RxCpltCallback				//接收完成回调函数。当UART接收完成时,会被调用。
HAL_UART_RxHalfCpltCallback			//接收一半回调函数。当UART接收一半数据时,会被调用。
HAL_UART_ErrorCallback				//出现错误回调函数。当UART操作中发生错误时,会被调用。
HAL_UART_AbortCpltCallback			//中止完成回调函数。当UART操作被中止时,会被调用。
HAL_UART_AbortTransmitCpltCallback	//发送中止回调函数。当中止发送时,会被调用。
HAL_UART_AbortReceiveCpltCallback	//接收中止回调函数。当中止接收时,会被调用。
HAL_UARTEx_RxEventCallback			//扩展接收事件回调函数。当特定的接收事件发生时,此函数会被调用
									//例如接收到指定数量的数据或UART线路变为空闲。

PS:阻塞、非阻塞、轮询和中断的关系


阻塞(Blocking)

在这里插入图片描述

阻塞:程序在等待某个操作完成时暂时停止执行,直到该操作完成或发生超时。

在阻塞操作期间,进程的执行流程被中断,直到等待的事件完成或发生超时
阻塞可能导致CPU资源浪费,进程在等待期间不做任何有用的工作
阻塞操作完成后,进程才会从挂起状态恢复,继续执行后续指令。

非阻塞(Non-blocking)

在这里插入图片描述

非阻塞:程序在等待某任务完成期间,能继续执行其他任务

在非阻塞操作中,程序不会停止执行,而是会立即返回,可能返回操作的结果或状态。
非阻塞操作通常需要通过回调函数、状态标志或事件来通知操作完成。

如果数据还没有准备好,它会立即返回一个错误或者特定的值,而不是等待。
进程可以利用这个返回值来决定是否需要重试操作,或者执行其他任务


轮询(Polling)

在这里插入图片描述

轮询是一种 检查条件是否满足 的技术
阻塞轮询: 会导致进程在等待事件时挂起,不执行其他任务
非阻塞轮询: 允许进程在等待事件时继续执行其他任务,提高了CPU的利用率

轮询是否阻塞通常可以 通过代码设置 ,轮询可以用于实现 阻塞非阻塞 I/O操作
在许多操作系统中和编程环境中,可以通过特定的API或设置来控制I/O操作是阻塞还是非阻塞。

轮询的定义

轮询是一种编程技术,它涉及程序定期检查某个条件或资源的状态,以确定是否可以进行下一步操作
在轮询过程中,程序会周期性地查询一个或多个状态标志或硬件寄存器,直到期望的事件发生

阻塞轮询
  • 是指程序在检查条件时,如果条件不满足,程序会持续检查,直到条件满足 / 超时为止
  • 在这个过程中,程序的执行被暂停,在条件成立之前 / 超时,不执行其他任务,程序是阻塞的
  • 阻塞轮询会占用CPU资源,涉及到重复的检查操作,在条件长时间不改变时可能导致效率低下
非阻塞轮询
  • 是指程序在检查条件时,如果条件不满足,程序会立即返回并执行其他任务,稍后再回来检查条件
  • 在这个过程中,程序不会因为条件未满足而被暂停,会继续执行其他操作,并在适当的时机再次检查条件
  • 非阻塞轮询不会长时间占用CPU资源,条件未满足时程序执行其他任务,提高了CPU的利用率和程序的效率

中断(Interrupt)

中断机制是一种硬件或软件机制
在特定事件发生时,能够暂停当前程序的执行,并转而跳转到一个预定义的处理程序(中断服务例程ISR)。
中断通常用于处理异步事件,如硬件信号或计时器溢出。
通过中断,可以实现非阻塞操作,使得CPU在处理I/O操作等事件的同时,主程序能够继续执行其他任务。
虽然中断是实现非阻塞I/O的常见方式,但非阻塞操作也可以通过轮询等其他方法来完成。


四者关系

  • 阻塞与轮询
    轮询通常是阻塞的,但阻塞操作不一定是通过轮询实现的
    阻塞通常是指程序在等待I/O操作完成时挂起,而轮询是一种不断检查条件的方法。
    轮询可以是阻塞的,如果它在检查条件时暂停其他操作。

  • 非阻塞与轮询
    非阻塞轮询是一种特殊的轮询方式 ,它允许程序在条件不满足时继续执行其他任务,而不是等待。
    轮询是一种实现非阻塞I/O的方式,通过不断地检查某个条件是否满足来实现对I/O操作的监控。
    非阻塞I/O通常与轮询一起使用,进程会不断地尝试I/O操作,直到数据准备好。

  • 中断与轮询
    轮询和中断是两种不同的处理异步事件的方法。
    轮询需要程序主动检查
    中断则是事件驱动的,由硬件或软件条件触发。

  • 中断与非阻塞
    非阻塞操作可以允许程序在等待某个条件时继续执行,而中断是处理异步事件的一种机制
    中断是一种实现非阻塞I/O的机制,因为它允许CPU在事件发生时立即响应,而无需程序主动检查。
    非阻塞操作不一定是通过中断实现的,但中断是实现非阻塞操作的一种常见方式。


虽然本文只是讲解串口的这些函数
但是其他外设的函数也是一样的,取名都异曲同工
要学会触类旁通,比如

_IT结尾:非阻塞
_DMA结尾:直接内存
_ToIdle结尾:拓展函数
_Abort结尾:中止函数
_Callback结尾:回调函数

当然,还有一些拓展知识,比如IDLE,以及阻塞,非阻塞,轮询和中断的关系
这么多干货,你是怎么忍住不 点赞 + 收藏 的啊,赶紧动起手 (。・ω・。)ノ♡


┈┈┈┈▕▔╲┈┈┈┈┈┈┈ ┈┈┈┈▕▔╲┈┈┈┈┈┈┈ ┈┈┈┈▕▔╲┈┈┈┈┈┈┈┈
┈┈┈┈┈▏▕┈┈┈┈┈┈┈ ┈┈┈┈┈▏▕┈┈┈┈┈┈┈ ┈┈┈┈┈▏▕┈┈┈┈┈┈┈ ┈
┈┈┈┈┈▏ ▕▂▂▂▂▂┈┈┈┈┈┈┈▏ ▕▂▂▂▂▂┈┈┈┈┈┈┈▏ ▕▂▂▂▂▂┈┈┈
▂▂▂▂╱┈┈▕▂▂▂▂▏┈ ▂▂▂▂╱┈┈▕▂▂▂▂▏┈ ▂▂▂▂╱┈┈▕▂▂▂▂▏┈┈
▉▉▉┈┈┈┈▕▂▂▂▂▏ ┈ ▉▉▉┈┈┈┈▕▂▂▂▂▏ ┈ ▉▉▉┈┈┈┈▕▂▂▂▂▏ ┈
▉▉▉┈┈┈┈▕▂▂▂▂▏ ┈ ▉▉▉┈┈┈┈▕▂▂▂▂▏ ┈ ▉▉▉┈┈┈┈▕▂▂▂▂▏ ┈
▔▔▔▔╲▂▂▕▂▂▂▂▏┈ ▔▔▔▔╲▂▂▕▂▂▂▂▏┈ ▔▔▔▔╲▂▂▕▂▂▂▂▏┈┈

如果对你有帮助,就点赞收藏把!(。・ω・。)ノ♡

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值