STM32串口数据收发基础(四):使用USART DMA模式


  在STM32中编写串口通信数据收发有三种方式:轮询模式(阻塞方式),中断模式(非阻塞方式)以及DMA模式

一. 串口通信(DMA模式)


  打开STM32CubeMX,前部分配置流程如串口数据收发基础(三)节里一样。配置好USART1的基本参数,开启定时器中断后,接下来就要开启USART1的DMA。

设置好之后,设置存储路径,选择所用IDE,然后点击GENERATE CODE创建工程,open project打开工程进行全局编译。

二. HAL库中串口收发的重要函数(DMA模式)

  1. DMA模式下发送数据函数:HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

@简介  以DMA模式发送一定数量的数据。
@参数  huart,指向UART_HandleTypeDef结构体的指针,该结构体包含指定UART模块的配置信息。
@参数  pData,待发送数据缓冲区的指针。
@参数  Size,待发送数据的字节数。
@返回值  函数执行状态。

  2. DMA模式下接收数据函数: HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

@简介  在DMA模式下接收一定数量的数据
@参数  huart,指向UART_HandleTypeDef结构体的指针,该结构体包含指定UART模块的配置信息。
@参数  pData,待接收接收数据缓冲区的指针。
@参数  Size,待接收数据的字节数。
@注意  当UART奇偶校验使能(PCE = 1)时,接收的数据包含奇偶校验位。
@返回值  函数执行状态。

  3. DMA模式下接收不定长数据函数: HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

@简介  在DMA模式下接收一定数量的数据,直到接收到预期数量的数据或发生IDLE事件。
@注意  接收数据由这个函数调用发起。DMA服务实现了接收数据的进一步进展,在用户接收缓冲区中自动传输接收到的数据元素,并在接收传输过半/完成时调用回      
      调函数。UART IDLE事件也用于将接收阶段视为结束。在所有情况下,回调执行将指示接收到的数据元素的数量。
@参数  huart 指向UART_HandleTypeDef结构体的指针,该结构体包含指定UART模块的配置信息。
@参数  pData 待接收接收数据缓冲区的指针。
@参数  Size  待接收数据的字节数。
@返回值  函数执行状态。

  4. DMA模式数据传输完成回调函数:HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);

__weak void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
  UNUSED(huart);
  UNUSED(Size);
}

@简介  接收事件回调(使用高级接收服务后调用的Rx事件通知)。
@参数  huart UART handle
@参数  Size,应用程序接收缓冲区中可用数据的数量(表示接收缓冲区中数据可用的位置)
@返回值  空

  5.禁用指定的DMA通道中断函数:__HAL_DMA_DISABLE_IT(HANDLE, INTERRUPT) (CLEAR_BIT((HANDLE)->Instance->CCR , (INTERRUPT)));

#define __HAL_DMA_DISABLE_IT(__HANDLE__, __INTERRUPT__)  (CLEAR_BIT((__HANDLE__)->Instance->CCR , (__INTERRUPT__)))

@简介  禁用指定的DMA通道中断。
@参数  __HANDLE__: DMA处理
@参数  __INTERRUPT__: 指明要启用或者禁用的DMA中断源
该参数可以是下列值的任意组合:
    @arg DMA_IT_TC:  传输完成中断屏蔽
    @arg DMA_IT_HT:  过半传输完成中断屏蔽
    @arg DMA_IT_TE:  传输错误中断屏蔽
@返回值  空

以上提及的函数可在stm32f1xx_hal_uart.cstm32f1xx_hal_dma.h中,在这些文件中找到

三. 实现串口定长收发通信(DMA模式)

在main.c文件中的Private variables下的USER CODE BEGIN PV注释对中写入以下代码

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
unsigned char String_1[] = "hello world!!!\r\n";
unsigned char rx[16];
/* USER CODE END PV */

在main.c文件中的Private function prototypes下的USER CODE BEGIN PFP注释对中写入以下代码

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
// dma模式
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
	if(huart == &huart1){ //  将上位机发来的数据完整的返回回去
		HAL_UART_Transmit_DMA(&huart1,rx,sizeof(rx));
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_4);
		HAL_UART_Receive_DMA(&huart1,rx,sizeof(rx));
	}
}
/* USER CODE END PFP */

在main.c文件中的main函数下USER CODE BEGIN 2 注释对写入以下代码

/* USER CODE BEGIN 2 */
  HAL_UART_Transmit_IT(&huart1,String_1,sizeof(String_1));
  HAL_UART_Receive_DMA(&huart1,rx,sizeof(rx));
/* USER CODE END 2 */

实现的效果是在当开发板通过USB转TTL模块与电脑上的串口调试助手相连后,上位机向开发板发送的数据。开发板接收到上位机发来的数据后,翻转PB4引脚上的LED灯亮灭,随后将接收到的数据原封不动的再返回给上位机。

四. 实现串口不定长收发数据通信(DMA模式)

在main.c文件中的Private function prototypes下的USER CODE BEGIN PFP注释对中写入以下代码

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
// dma模式
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){
	if(huart == &huart1){ //  将上位机发来的数据完整的返回回去
		HAL_UART_Transmit_DMA(&huart1,rx,Size);
		HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx,sizeof(rx));
	}
}
/* USER CODE END PFP */

在main.c文件中的main函数下USER CODE BEGIN 2 注释对写入以下代码

/* USER CODE BEGIN 2 */
  HAL_UART_Transmit_IT(&huart1,String_1,sizeof(String_1));
  HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx,sizeof(rx));
/* USER CODE END 2 */

实现的效果是在当开发板通过USB转TTL模块与电脑上的串口调试助手相连后,上位机向开发板发送的数据。开发板接收到上位机发来的数据后,随后将接收到的数据原封不动的再返回给上位机。
如下图可以发现依次发送不定长的数据给开发板后,开发板都完整的返回了。


但是当我们一次发送过多的数据后,返回的数据竟然不完整了。

这是因为在发送数据的时候,当我们接收的数据量达到设置的最大值的一半的时候
也就是这个
,就会触发HAL_UARTEx_RxEventCallback回调函数。
为了解决这个问题,我们可以使用关闭“DMA传输过半中断”的函数,也就是__HAL_DMA_DISABLE_IT()函数。将其写入到HAL_UARTEx_RxEventCallback回调函数中


注意:如果编译出错为 **error: #20: identifier “hdma_usart1_rx” is undefined **时,说明标识符“hdma_usart1_rx”未定义。可以证明解决。
在usart.h中的USER CODE BEGIN Private defines注释对中添加extern DMA_HandleTypeDef hdma_usart1_rx;

编译,运行,串口调试助手,启动!!!
这个时候可以发现数据终于实现了正常的不定长收发了(只要不要写过我们的先前设定的rx数组长度就好了)

物联沃分享整理
物联沃-IOTWORD物联网 » STM32串口数据收发基础(四):使用USART DMA模式

发表评论