(一)阻塞收发
- 特点:简单粗暴,占满单片机资源进行收发
- 简介:
- 发送:发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志
- 接收:接收指定长度的数据。如果超时没接收完成,则不再接收数据到指定缓冲区,返回超时标志(HAL_TIMEOUT)
发送函数:HAL_UART_Transmit()
接收函数:HAL_UART_Receive()
CubeMX配置 :
![]()
(二)中断收发
- 特点:利用中断收发,不会占满资源
- 简介:
- 发送:把发送缓冲区指针指向要发送的数据,设置发送长度,发送计数器初值,然后使能串口发送中断,触发串口中断。
再然后,串口中断函数处理,直到数据发送完成,而后关闭中断,不再发送数据,串口发送完成回调函数。
- 接收:把接收缓冲区指针指向要存放接收数据的数组,设置接收长度,接收计数器初值,然后使能串口接收中断。接收到数据时,会触发串口中断。
再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,不再触发接收中断,调用串口接收完成回调函数。
发送函数: HAL_UART_Transmit_IT()
接收函数: HAL_UART_Receive_IT()
CubeMX配置:
![]()
(三)DMA收发
- 特点:不占CPU资源
- 简介:
- 串口DMA发送,以DMA方式发送指定长度的数据。
过程是,把发送缓冲区指针指向要发送的数据,设置发送长度,发送计数器初值,设置 DMA传输完成中断的回调函数,使能 DMA控制器中断,使能 DMA控制器传输 使能UART的DMA传输请求。
然后,UART便会发送数据,直到发送完成,触发DMA中断。
DMA中断处理,如果DMA模式是循环模式,则直接调用 DMA传输完成中断的回调函数。
如果 DMA模式是正常模式,则先关闭DMA传输完成中断,不再触发DMA中断,再 调用 DMA传输完成中断的回调函数。
DMA传输完成中断的回调函数处理过程,如果 DMA模式是循环模式,则 直接调用串口发送完成回调函数。
如果 DMA模式是正常模式,则先关闭 UART的DMA传输请求, 再使能串口传输完成中断,直到传输完成,触发中断。
串口传输完成中断处理,关闭中断,调用串口发送完成回调函数。
- 串口DMA接收,以DMA方式接收指定长度的数据。
过程是,把 接收缓冲区指针 指向 要存放接收数据的数组,设置接收长度,接收计数器初值,设置 DMA传输完成中断的回调函数,使能DMA控制器中断,使能DMA控制器传输,使能UART的DMA传输请求。
然后,UART接收到数据,便会通过DMA把数据存到接收缓冲区,直到接收到指定长度数据,触发DMA中断。
DMA中断处理,如果 DMA模式 是 循环模式,则 直接 调用 DMA传输完成中断的回调函数。
如果 DMA模式是正常模式,则先关闭DMA传输完成中断,不再触发DMA中断,再 调用 DMA传输完成中断的回调函数。
DMA传输完成中断的回调函数处理过程,如果 DMA模式是循环模式,则直接调用串口接收完成回调函数。
如果 DMA模式 是 正常模式,则先关闭 UART的DMA传输请求, 再调用串口接收完成回调函数。
发送函数:HAL_UART_Transmit_DMA()
接收函数:HAL_UART_Receive_DMA()
CubeMX配置
![]()
使用示例
以HAL_UART_Transmit_IT为例
考虑到初始化代码可以用CubeMX生成,为方便理解,省略了初始化代码
int main(void)
{
/*初始化(已省略)*/
uint8_t uartbuff[20] = "test uart";
while (1)
{
HAL_UART_Transmit(&huart1,uartbuff,20,10);
}
}
在串口中使用printf()和scanf()
包含头文件 #include "stdio.h"
重定义 fputc 以使用 printf()
int fputc(int c,FILE * f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&c,1,20);// 此处以阻塞发送为例,也可以使用其他两种方式
return c;
}
重定义fgetc以使用scanf()
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);// 此处以阻塞接收为例,也可以使用其他两种方式
return ch;
}
完成重定义后,就可以在代码中使用printf()和scanf()了,使用方法同寻常c语言编程相同,输入和输出通过串口实现,可以使用串口调试助手进行查看和操作。
回头看整个流程
分析一下main.c文件,从头往下看,为方便阅读,删去了CubeMX生成的注释
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
void SystemClock_Config(void);
/*此处重定义fputc和fgetc*/
int fputc(int c,FILE * f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&c,1,20);
return c;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
int main(void)
{
/****************这部分代码可以使用CubeMX生成******************************/
/* 复位所有外设,初始化Flash接口和Systick */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 初始化所有已配置的外设,可以看到这里使用的是MX_XX_Init(),也就是调用使用CubeMX配置生成的初始化设置 */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/****************这部分代码可以使用CubeMX生成******************************/
/* 进入while循环,执行用户逻辑 */
while (1)
{
printf("test uartn");
}
}
回调函数
UART的中断收发和DMA收发提供了回调函数,当达到相应的条件后,会触发回调函数,用户可以在回调函数中编写自己需要实现的功能
例如:当使用HAL_UART_Transmit_IT()完成发送后,会触发HAL_UART_TxCpltCallback
回调函数,用户可以在HAL_UART_TxCpltCallback中编写代码实现自己需要的功能。
值得注意的是,回调函数是使用弱定义关键字weak实现的,用户需要重定义实现回调函数。
__weak void HAL_UART_TxCpltCallback(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_TxCpltCallback could be implemented in the user file
*/
}
常见回调函数:
HAL_UART_TxCpltCallback
HAL_UART_RxCpltCallback
HAL_UART_TxHalfCpltCallback
HAL_UART_RxHalfCpltCallback
HAL_UART_ErrorCallback