【STM32 HAL】UART串口通讯

HAL库中UART的三种收发方式

(一)阻塞收发

  • 特点:简单粗暴,占满单片机资源进行收发
  • 简介:
    • 发送:发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志
    • 接收:接收指定长度的数据。如果超时没接收完成,则不再接收数据到指定缓冲区,返回超时标志(HAL_TIMEOUT)
  • 发送函数:HAL_UART_Transmit()
  • 接收函数:HAL_UART_Receive()
  • CubeMX配置 :
    【STM32 HAL】UART串口通讯_第1张图片

(二)中断收发

  • 特点:利用中断收发,不会占满资源
  • 简介:
    • 发送:把发送缓冲区指针指向要发送的数据,设置发送长度,发送计数器初值,然后使能串口发送中断,触发串口中断。
      再然后,串口中断函数处理,直到数据发送完成,而后关闭中断,不再发送数据,串口发送完成回调函数。
    • 接收:把接收缓冲区指针指向要存放接收数据的数组,设置接收长度,接收计数器初值,然后使能串口接收中断。接收到数据时,会触发串口中断。
      再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,不再触发接收中断,调用串口接收完成回调函数。
  • 发送函数: HAL_UART_Transmit_IT()
  • 接收函数: HAL_UART_Receive_IT()
  • CubeMX配置:
    【STM32 HAL】UART串口通讯_第2张图片

(三)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配置
    【STM32 HAL】UART串口通讯_第3张图片

使用示例

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 uart\n");
  }

}

回调函数

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

更多高级操作请查阅HAL库官方文档
或阅读stm32f1xx_hal_uart.c中的相关介绍

你可能感兴趣的:(STM32,HAL,单片机,嵌入式,stm32)