一、HAL_UART_Transmit和HAL_UART_Transmit_IT的区别
1. HAL_UART_Transmit_IT
(非阻塞模式):
HAL_UART_Transmit_IT
是非阻塞的传输函数,也就是说,当你调用 HAL_UART_Transmit_IT
时,它不会等到数据完全发送完就返回控制权。它会启动一个中断,通知你数据发送完成,允许你继续执行其他任务。这种方式是为了让你在发送数据的过程中,能够继续处理其他操作,不会因为等待数据发送完成而阻塞。
由于数据传输是异步的,当你调用 HAL_UART_Transmit_IT
时,它会立即返回,而数据的发送是由硬件自动完成的。为了保证数据完全发送完毕,你需要通过检查标志位(如 UART_FLAG_TC
)或等待 HAL_UART_TxCpltCallback
被调用来确认数据传输已经完成。
如下图,需要通过标志位判断是否发送完成:
2. HAL_UART_Transmit
(阻塞模式):
HAL_UART_Transmit
是阻塞的传输函数。也就是说,当你调用 HAL_UART_Transmit
时,函数会阻塞,直到所有数据都被完全发送完毕。它会等待硬件完成数据的发送,并且只有在发送完成后才会返回。你不需要手动检查 UART_FLAG_TC
,因为函数本身会等到发送完成才会退出。
3. 为什么 HAL_UART_Transmit_IT
需要判断发送完成,而 HAL_UART_Transmit
不用?
-
非阻塞 vs 阻塞:
HAL_UART_Transmit_IT
是非阻塞的,它只启动发送过程并立即返回,你需要通过中断回调或者标志位来确定发送是否完成。由于它没有等待数据发送完毕,因此你必须在发送后检查是否已完成。 -
阻塞模式:
HAL_UART_Transmit
是阻塞的,函数内部会等待直到数据完全发送完毕,所以你不需要手动检查发送是否完成。
4、示例
错误示例:HAL_UART_Transmit_IT使用时没有标志位判断中断,导致数据被截断,不能接收电脑发送的abc。
数据被截断: 由于 HAL_UART_Transmit_IT
是非阻塞的,它启动了发送后立刻返回。这意味着在你启动 str2
的发送后,可能很快就进入了接收部分,而 UART 的发送缓冲区可能还没有完全发送 str2
,此时如果你再次启动发送操作,就会中断之前的发送,导致数据没有完全传输。
正确操作:
或者
正确结果:
二、为什么串口发送不需要断,而接收需要中断
-
发送数据:
- 串口数据发送是由软件驱动的过程。一般来说,数据发送到串口的寄存器中后,硬件会负责将其逐个字节发送出去。发送过程中,CPU 主要负责将数据写入寄存器或缓冲区,而并不需要实时监控每一个发送过程。
- 因为发送的速度通常比较稳定且有足够的时间来处理下一个字节,CPU 可以通过简单的轮询(Polling)方式来确保数据的发送。
- 串口发送过程中,硬件会根据串口设置(如波特率)自动管理发送队列。当发送缓冲区空闲时,CPU 只需要继续写入数据,或者使用中断通知“数据发送完成”。
-
接收数据:
- 接收数据是一个实时性的过程,硬件通过中断来通知 CPU 什么时候有新的数据可以读取。如果不使用中断,CPU 就需要通过轮询的方式不断检查串口接收缓冲区的状态。这对于实时性要求较高的应用来说会增加负担,降低效率。
- 当接收到一个字节数据时,中断可以直接通知 CPU 进行处理。这使得接收数据更加及时和高效,避免了轮询带来的延迟和 CPU 资源浪费。
接收中断:
如下图:
我们最开始会开启中断,注意:要使用中断启用函数(HAL_UART_Receive_IT)才能真正启用中断,原因我在前几篇的文章有提到过。
这里启用中断后,UART一当检测到接受信号会进入到中断服务函数(HAL_UART_IRQHandler)
接着会进入到回调函数(HAL_UART_RxCpltCallback),执行我们想要的操作了,比如把数据放储存起来。
注意:如果你接收后没有操作,数据虽然接收了,但没有储存,会丢失。
我这里的回调函数是把数据放入到g_RecvChar中,如果我们要检查数据,就去这里面查找。
接收数据的读取:你也可以不读取,反正已经按照你的要求放入容器中了
如上图,我这UART1GetChar(&c)就是去读取被放入容器里面的数据。