STM32 HAL库之串口接收不定长字符

背景

在项目开发过程中,经常会使用MCU的串口与外界进行通信,例如两个单片机之间TTL电平型串口通信,单片机与成熟电路模块之间的串口通信等等.... 如何高效的使用串口是开发人员必须关注的问题。

STM32的HAL库为我们提供了三种串口通信机制:

(1)查询方式。

查询方式主要采用查询串口状态寄存器接收和发送相关标志,知道数据发送或接收成功。发送一般可采用这种方式,接收采用这种方式效率比较低。

复制代码
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

(2)中断方式

复制代码
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

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

(3)DMA方式

DMA的方式主要采用以下两个函数。但该函数内部机制都是打开DMA通道中断,采用的是DMA中断的方式。但在使用的过程中,可关闭DMA中断,直接采用DMA接收和放送,降低了CPU负载。

复制代码
HAL_StatusTypeDef HAL_UART_Transmit_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的方式发送,尽量不采用中断方式(包括DMA中断方式)。串口接收数据具有随机性,一般采用中断方式和DMA中断接收,前者一般单字节触发接收,后者一般定长字符触发DMA中断接收。但对于接收字符长度和时间不确定问题,一般可采用串口空闲中断+DMA接收机制。

空闲中断的概念

接收数据后RXNE置位,且之后间隔一个字节的时钟未收到数据则触发IDLE中断。

(1)按照配置工具正常配置,打开串口中断和DMA接收

(2)关闭DMA中断

复制代码
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel5_IRQn interrupt configuration */
//  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
//  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);

}

(3)串口配置空闲中断,使能DMA接收。

复制代码
#define  UART_REV_SIZE   50
uint8_t  gRevBuff[UART_REV_SIZE] = {0};
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
	 HAL_UART_Receive_DMA(&huart1, gRevBuff, UART_REV_SIZE);
  /* USER CODE END USART1_Init 2 */
}

(4)串口中断处理

在串口中断添加如下处理。

复制代码
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	uint16_t  revLen = 0;
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)==1)
	{
		HAL_UART_DMAStop(&huart1);
		__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_IDLE);
		revLen = UART_REV_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx);
		HAL_UART_Receive_DMA(&huart1, gRevBuff, UART_REV_SIZE);
	}
  /* USER CODE END USART1_IRQn 1 */
}

结论

在线断点调试,串口中断不会频繁进入,有效降低了CPU负载。

相关推荐
La Pulga2 小时前
【FreeRTOS源码学习笔记】任务切换
单片机·mcu·学习·嵌入式·freertos·实时操作系统
女神下凡5 小时前
芯参谋(85):Linux_PPI_NAND_驱动开发指南
linux·运维·arm开发·驱动开发·单片机·嵌入式硬件·设计规范
Zw-awa6 小时前
从 CH340 到 HC-05 和 HC-06:让小车脱离电脑
stm32
硅基手札7 小时前
【Linux 内核专栏 19】20 · 嵌入式子系统 — GPIO·pinctrl·clock·regulator
linux·运维·单片机·linux内核·嵌入式开发·调试定位
何粥学9 小时前
Vivado 2024.2 开发记录(AC820 Zynq-7020)——软件使用,点亮led灯
单片机·嵌入式硬件·zynq
Sakura_j_h_10 小时前
51 单片机驱动四位数码管:动态扫描、限流电阻与消影的取舍
单片机·嵌入式硬件·51单片机·嵌入式·数码管·动态扫描·限流电阻
HANDY0920011 小时前
从“不照人”到“剂量达标”:扇叶遮挡式UV-C空气消毒的工程边界与可验证评估框架
开发语言·人工智能·嵌入式硬件·智能家居
飞凌嵌入式14 小时前
千次静电放电试炼,守住飞凌嵌入式硬件可靠底线
嵌入式硬件
蹊黎1 天前
Linux 下用 C 语言实现进度条
linux·c语言·单片机
Zw-awa1 天前
从 CPU 搬运到自动搬运:DMA、空闲中断和两块缓冲
stm32