stm32 HAL库实现UART的不定长数据DMA接收

本工程使用的是STM32F407系列,想要实现DMA的不定长数据接收,在串口数据量不大和工程体量不大的情况下,可以用串口轮询和串口中断来接收数据,而工程量变大而串口数据量变大时,实现UART的DMA发送接收就显得十分必要了,因为串口中断每收到一个byte的数据就会发生中断,这样会非常的消耗单片机的资源。而DMA接受一帧数据才会发生中断,可以极大的节省单片机的资源。

STM32中的代码使用HAL库,底层驱动使用STM32CUBE来生成,下面是重点。

使能USART3接口,到DMA setting中设置UART的DMA通道是什么,注意DMA Request Setting中的Mode是Circular,这样每一次DMA中断完成下一次数据到来前会自动打开DMA请求。同时UART通信接口的传输方式为异步通信。然后就是一些基本操作,这里不再细讲。
在这里插入图片描述
初始化代码中我将数据buffer的大小size设置成了1024个byte,同时初始化接收数据大小和BUFFER

/*USART初始化部分*/
#define DATA_MAX 1024
u16_t UART3_RX_NUM = 0;
u8_t UART3_RX_BUFFER[DATA_MAX] = {0};

然后在USER CODE 2下添加如下代码,用来打开UART空闲中断,因为UART进入空闲状态时,表示一帧数据接收结束

  /* USER CODE BEGIN 2 */
  __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);  //打开中断,接收完一组数据后产生中断
  HAL_UART_Receive_DMA(&huart3,UART3_RX_BUFFER,DATA_MAX);
  /* USER CODE END 2 */

中断部分我借鉴了rtthread中的部分UART DMA模式的代码,rtthread代码如下
我们可以总结出以下几点,即:
1.判断标志位是否置位
2.失能DMA中断
3.清除空闲中断标志位
4.得到接收的数据大小
5.使能DMA中断
6.进入串口中断

#ifdef RT_SERIAL_USING_DMA
	/*UART的空闲中断是否置位需要判断两个寄存器的值,这里不清楚为何要多一个其中一个是状态寄存器*/
    else if ((uart->uart_dma_flag) && (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_IDLE) != RESET)
             && (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_IDLE) != RESET))
    {
        level = rt_hw_interrupt_disable();//失能硬件中断
        
        recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle));
        
        recv_len = recv_total_index - uart->dma_rx.last_index;
        
        uart->dma_rx.last_index = recv_total_index;
        rt_hw_interrupt_enable(level);//使能硬件中断

        if (recv_len)
        {
            rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8));
        }
        __HAL_UART_CLEAR_IDLEFLAG(&uart->handle);
    }

用于复制
这是我在KEIL上修改的

void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */
	uint8_t temp1,temp2 = 0;
	temp1 = __HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE);
	temp2 = __HAL_UART_GET_IT_SOURCE(&huart3,UART_IT_IDLE);
	
	if((temp1 != RESET)&&(temp2 != RESET))
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart3);//清除中断标志位
		__HAL_DMA_DISABLE(&hdma_usart3_rx);//失能DMA_UART3_RX
		UART3_RX_NUM = (DATA_MAX) - hdma_usart3_rx.Instance->NDTR;//获取DMA搬运的数据
		__HAL_DMA_SET_COUNTER(&hdma_usart3_rx,DATA_MAX);//设置DMA_UART3_RX接收大小为DATA_MAX,即重新等待接收。
		__HAL_DMA_ENABLE(&hdma_usart3_rx);//使能DMA_UART_RX

	}
  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */

  /* USER CODE END USART3_IRQn 1 */
}

最后就是在main.c下面写好回调函数
功能是收到数据后转发给UART1

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)  //接收完成
{
	HAL_StatusTypeDef ret = 0;
	if(UART3_RX_NUM)
	{
		UART3_RX_BUFFER[UART3_RX_NUM] = '\0';
		//ret = HAL_UART_Transmit_DMA(&huart1,UART3_RX_BUFFER,(UART3_RX_NUM+1));
		ret = HAL_UART_Transmit(&huart1,UART3_RX_BUFFER,(UART3_RX_NUM),0xff);
		if(ret == HAL_OK)
		{
			UART3_RX_NUM = 0;
			memset(UART3_RX_BUFFER,0,sizeof(UART3_RX_BUFFER));
		}
	}
}

这样子整个工程就完成啦

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值