这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » NUCLEO-U083RC学习历程9-空闲中断+DMA接收不定长数据

共1条 1/1 1 跳转至

NUCLEO-U083RC学习历程9-空闲中断+DMA接收不定长数据

助工
2024-12-12 09:44:54     打赏

一:串口中断接收的通讯方式:

  在STM32,串口接收数据时的经常使用的方式就是使用STM32的串口中断的方式,即串口每次接收到一个字节时候,就会产生串口中断,将接收到数据放到接收数组内,同时清除标志位。这种接收方式比较简单,之前的学习记录中也尝试过该接收方式,这种接收方式比较浪费CPU的资源。在串口通讯中,经常会遇到接收不定长的数据,这时候没有必要采用上述方式进行数据的接收处理,比如STM32的标准库和HAL中对串口接收中断的处理中,再到中断处理函数,CPU需要执行很多的程序,频繁的中断为CPU的运行增加了不少的负担,也可能会出现接收出错的情况,而且不确定接收数据长度时,也不确定处理函数应该何时进行处理。

  为了解决上述问题,我们尝试使用串口的空闲中断与DMA接收数据,即开始STM32的空闲中断时,告知CPU本次接收数据完成了,可以进行下一步骤的处理;当然串口中断判断的依据,我个人的理解就是:使用串口与DMA接收字节数据时,当串口检测到在1-2个字节通讯时间内,串口没有接收到数据时,就会判定串口空闲了,使用DMA将数据拷贝到其他数据内进行处理即可。

二:使用的硬件平台:

STM32:NUCLEO-U083RC开发板、编程工具使用:KEIL5.38

三:STM32cube软件配置过程:

(基本的cube的使用参考之前的帖子,这里不做过多的介绍)

3.1串口的配置过程:

这里需要设置所调试的串口通讯参数、工作方式、波特率、校验位、停止位等等;为了调试方便,我这里使用的串口2可以直接与调试器的串口连接,调试代码起来很方便;

1.png

3.2 使能串口的中断:

这里需要注意一下,需要使能串口2的中断,否则串口无法接收数据

3.png

3.3 配置DMA的参数:

4.png

3.4 配置IO口的工作模式:

5.png

这样我们就把串口、DMA接收和中断的参数就配置好了,下面就可以进行软件代码的编写。

使用GPIO口初始化:

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

  /*Configure GPIO pin : PA5 */
  GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

4:程序编写的基本思路:

定义一些接收数组、变量:

uint8_t SendBuffer2[256] ;
uint8_t RecBuffer2[256] ;//串口2接收数据长度
uint8_t Usart2_DEAL_RX_Buf[256];//串口2处理接收缓冲
uint8_t usart2_Flag; //串口2接收完整数据包指令

4.1:cube代码自动生成之后,需要手动开启接收中断和空闲中断:

如下所示:定义一下DMA接收数组的位置;

在串口初始化时候,需要使能串口的接收中断和空闲中断,在串口产生中断的时候,cpu可以调用串口中断函数

static void MX_USART2_UART_Init(void)
{
  /* USER CODE BEGIN USART2_Init 0 */
  /* USER CODE END USART2_Init 0 */
  /* USER CODE BEGIN USART2_Init 1 */
  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */
/*使能串口接收中断和空闲中断*/
	__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);    //IDLE interrupt
	//打开DMA接收,指定接收缓存区和接收大小
	HAL_UART_Receive_DMA(&huart2, (uint8_t *)&RecBuffer2, 256);
  /* USER CODE END USART2_Init 2 */
}

4.2编写空闲中断、DMA处理函数:

这里我们产生串口中断时候,

1:首先需要清楚空闲中断标志,以便再次进入该串口空闲中断,

2:停止DMA的接收,防止计算长度时候出错

3:计算一下DMA数据中有效数据

4:将有效数据拷贝至其他的定义的数据内,以便程序处理

5:将接收完一包有效数据的标志位置位,同时需要再次开启DMA接收,

完成代码如下所示:

DMA1中断函数(程序自动生成,)

static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
void DMA1_Channel1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
  /* USER CODE END DMA1_Channel1_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_rx);
  /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
  /* USER CODE END DMA1_Channel1_IRQn 1 */
}
void UsartReceive_IDLE(UART_HandleTypeDef *huart) 
{
	//当触发了串口空闲中断
  if((__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)) 
	{
		 if(huart->Instance == USART2)
		{
			/* 1.清除标志 */
			__HAL_UART_CLEAR_IDLEFLAG(huart); 	//清除空闲标志
			/* 2.读取DMA */
			HAL_UART_DMAStop(huart); 			//先停止DMA,暂停接收		
			/* 3.搬移数据进行其他处理 */
			usart2_rx_len = 256 - (__HAL_DMA_GET_COUNTER(&hdma_usart2_rx)); //接收个数等于接收缓冲区总大小减已经接收的个数
			memcpy(Usart2_DEAL_RX_Buf, RecBuffer2, usart2_rx_len);
			usart2_Flag = 1;			
			/* 4.开启新的一次DMA接收 */
			HAL_UART_Receive_DMA(&huart2, (uint8_t *)&RecBuffer2, 256);
		}
	}
}

4.3 定义空闲中断回调函数:

在串口中添加上面,对空闲中断的处理:

void USART2_LPUART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_LPUART2_IRQn 0 */
  /* USER CODE END USART2_LPUART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_LPUART2_IRQn 1 */
	//串口空闲中断处理
	UsartReceive_IDLE(&huart2);
  /* USER CODE END USART2_LPUART2_IRQn 1 */
}

4.4将串口接收的数据,回传到串口,即数据原路返回

在主函数中,将串口接收到的数据原路返回,

    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
    /* Insert delay 500ms */
    HAL_Delay(500);
	if(usart2_Flag == 1)
	{
		usart2_Flag = 0 ;

		HAL_UART_Transmit(&huart2,Usart2_DEAL_RX_Buf,usart2_rx_len,1000);
	}

基本上,编写好以上代码,我们实现使用串口2空闲中断+DMA接收的接收不定长数据的接收,并实现数据的原路返回;

5:实物验证如下所示:

8.jpg

6.png




关键词: NUCLEO-U083RC     空闲     串口     处理    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]