STM32笔记:FREERTOS+USART+IDLE+DMA双缓冲接收,DMA发送存在一个BUG,请教有心人。

如题,本文演示STM32+FREERTOS实现串口双缓冲接收。双缓冲接收指的是,为串口设置两个接收缓存区,可以以字节串为单位,交替保存串口收到的信息。它的好处是,在T时间内收到两条字节串时,不会错过其中一条(T时间指的是单片机在接收信息完成,产生IDLE中断后,到处理完信息的时间),另一个好处是,避免了在没关闭DMA情况下,T时间内被另一条字节串部分覆盖而产生的错误。当然,单缓冲足够应付多数场合,只需要注意在接收完毕后,马上关闭串口DMA接收,等处理完信息再开启串口DMA接收!

关于串口接收的IDLE中断模式,是STM32单片机的一个亮点,简单讲,就是每当有一串字节发过来的时候,接收字节的空隙不会产生中断,在接收完这串字节后(单片机进行判断),会产生一个IDLE中断。这个功能加上DMA,可以大大地减少单片机CPU在串口接收过程中的负担。具体在CSDN有诸多大神的详细演示。

根据DMA的特性,串口发送当然也采用DMA最好,而且采用串口DMA发送中断来确认一条字节串发送完毕,才允许下一次的发送(单缓冲模式)。具体的流程是:

1.使能串口DMA发送完成中断

2.待发送的字节串写入缓冲区,禁止下一次发送

3.启动串口DMA发送

4.发送完毕产生串口DMA发送中断,允许下一次发送。

但是经过反复的测试,发现存在一个问题:利用该功能,连续发送字节串,会导致字节串后部分被覆盖,也就是说,串口DMA发送完成中断产生的时候,DMA发送并没有完全结束!!!

对于这个问题,目前束手无策,只能采用阻塞式发送(浪费CPU资源)

请有心人讨论,指点!感谢!

开发环境:

IDE:STM32CubeIDE 1.8

固件库:STM32Cube_FW_F1_V1.8.4

硬件:Waveshare Open107V,STM32F107VC, 晶振25MHz,工作频率72MHz

关键代码:

//初始化时,启用接收IDLE中断
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);

//启用串口中断,以下是stm32f1xx_it.c中对IDLE中断的响应
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 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))//确认是IDLE中断
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志
		IsrProcRxIdle();//在freertos中处理(双缓冲)
	}
  /* USER CODE END USART1_IRQn 1 */
}

//main.c和main.h中的宏和全局变量定义

typedef enum {false = 0, true = 1} bool;

#define BUF_TOP						254
#define BUF_SIZE					(BUF_TOP + 1)
#define BUF_FLAG					(BUF_TOP + 1)
#define BUF_STATUS_FREE				0
#define BUF_STATUS_RX				(BUF_TOP + 1)

#define BUF_ARRAY_SIZE				2

uint8_t rxBuf10[BUF_SIZE + 1];
uint8_t rxBuf11[BUF_SIZE + 1];
//rxBufxx[BUF_FLAG]用来作为状态变量BUF_STATUS_RX或BUF_STATUS_FREE,其他值则为接收到的字符长度。

//在freertos.c中实现DMA双缓冲接收

//Called in USART1_IRQHandler
void IsrProcRxIdle(void)
{
	BaseType_t pxHigherPriorityTaskWoken;
	uint16_t len;
	HAL_UART_DMAStop(&huart1);//停止uart dma
    //获取接收的字符数
	len = BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
    //双缓冲处理
	if(rxBuf10[BUF_FLAG] == BUF_STATUS_RX)
	{
		rxBuf10[BUF_FLAG] = len;
		if(rxBuf11[BUF_FLAG] == BUF_STATUS_FREE)
		{
			rxBuf11[BUF_FLAG] = BUF_STATUS_RX;
			HAL_UART_Receive_DMA(&huart1, rxBuf11, BUF_SIZE);
		}
	}
	else if(rxBuf11[BUF_FLAG] == BUF_STATUS_RX)
	{
		rxBuf11[BUF_FLAG] = len;
		if(rxBuf10[BUF_FLAG] == BUF_STATUS_FREE)
		{
			rxBuf10[BUF_FLAG] = BUF_STATUS_RX;
			HAL_UART_Receive_DMA(&huart1, rxBuf10, BUF_SIZE);
		}
	}
	xSemaphoreGiveFromISR(myBsRx1Handle, &pxHigherPriorityTaskWoken);
	portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
}


//在freertos.c中串口接收的处理任务:

/* USER CODE END Header_taskCallBackRx1 */
void taskCallBackRx1(void const * argument)
{
  /* USER CODE BEGIN taskCallBackRx1 */
	uint8_t * pBufRx;
	char pStrBuf[50];
	char * strx;
	uint8_t i, bn = 9;
	uint16_t len;
	BaseType_t xReturn;
  /* Infinite loop */
  for(;;)
  {
	xReturn = xSemaphoreTake(myBsRx1Handle, 1);//看看有没有接收到字节串
	if(pdPASS == xReturn)
	{
		//HAL_UART_Transmit_DMA(&huart1, (uint8_t *)rxBuf10, strlen(rxBuf10));//test OK!
		//BSP_LED2_Toggle();	//Test OK!

		pBufRx = rxBuf10;
		while(pBufRx != NULL)
		{
			if(pBufRx == rxBuf10)
			{
				bn = 0;
			}
			else if(pBufRx == rxBuf11) bn = 1;	//for test, check the current buffer

			if(pBufRx[BUF_FLAG] != BUF_STATUS_FREE && pBufRx[BUF_FLAG] != BUF_STATUS_RX)
			{
                  //处理接收到的指令
				  strx = strstr((char*)pBufRx, (char*)"SET");
				  if(strx)
				  {
					  switch(pBufRx[3])
					  {
					  case 'D':	//收到SETD指令,用串口DMA连续发送5条字节串
						  printf("\n\n");
						  for(i = 0; i < 5; i++)
						  {
							  memset(pStrBuf, 0, 50);
							  sprintf(pStrBuf, "Receive cache:%d, response with DMA...%d.\n", bn, i);
							  len = strlen(pStrBuf);
							  Usart1SendString_DMA((uint8_t *)pStrBuf, len);
						  }
						  break;
					  case 'N':	//收到SETD指令,用串口DMA连续发送5条字节串
						  printf("\n\n");
						  for(i = 0; i < 5; i++)
						  {
							  memset(pStrBuf, 0, 50);
							  sprintf(pStrBuf, "Receive cache:%d, response in blocking mode...%d.\n", bn, i);
							  len = strlen(pStrBuf);
							  Usart1SendString((uint8_t *)pStrBuf, len);
						  }
						  break;
					  default:
						  ;
					  }
				  }
                //双缓冲接收处理。
				//两个缓冲区都有数据需要处理,处理完信息后,将第一个设为接收缓冲区使能串口DMA接收
				if(rxBuf10[BUF_FLAG] != BUF_STATUS_RX && rxBuf10[BUF_FLAG] != BUF_STATUS_FREE &&
				   rxBuf11[BUF_FLAG] != BUF_STATUS_RX && rxBuf11[BUF_FLAG] != BUF_STATUS_FREE)
				{
					rxBuf10[BUF_FLAG] = BUF_STATUS_FREE;
					HAL_UART_Receive_DMA(&huart1, rxBuf10, BUF_SIZE);
					//BSP_LED4_Toggle();
				}
				else
				{
					//BSP_LED3_Toggle();					//Test OK!
					pBufRx[BUF_FLAG] = BUF_STATUS_FREE;
				}
			}//end of if(pBuf[BUF_FLAG] != ...

			if(rxBuf10 == pBufRx) pBufRx = rxBuf11;
			else pBufRx = NULL;
		}//end of while
		/**/

	}//end of if(pdPASS = xReturn)
    osDelay(1);
  }
  /* USER CODE END taskCallBackRx1 */
}

串口发送代码:

//阻塞式发送
void Usart1SendString(uint8_t * buf, uint16_t len)
{
	xSemaphoreTake(myMutexTx1Handle, portMAX_DELAY);
	while(*buf && len)
	{
		HAL_UART_Transmit(&huart1, (uint8_t *)buf, 1, HAL_MAX_DELAY);//Send in blocking mode
        buf++;
        len--;
	}
	xSemaphoreGive(myMutexTx1Handle);
}

//DMA发送
bool Usart1SendString_DMA(uint8_t * buf, uint16_t len)
{
	bool res = false;
	xSemaphoreTake(myMutexTx1Handle, portMAX_DELAY);
	res = Usart1Send(buf, len);
	xSemaphoreGive(myMutexTx1Handle);
	return res;
}


bool Usart1Send(uint8_t * buf, uint16_t len)
{
	int16_t i, n;
	bool bResult = false;
	if(buf == NULL || len == 0 || len > BUF_SIZE) return false;

    //确保串口DMA发送空闲
	n = 1000;
	do
	{
		osDelay(2);
	}
	while(HAL_DMA_STATE_READY != HAL_DMA_GetState(&hdma_usart1_tx) && n--) ;
	if(n <= 0)
	{
		HAL_UART_DMAStop(&huart1);
	}
    //确保串口DMA发送空闲 发送状态是空闲的!
	n = 1000;
	do
	{
		osDelay(1);
	}
	while(txBuf10[BUF_FLAG] != BUF_STATUS_FREE && n--);
	if(n <= 0) return false;
    //满足条件后发送,就这样,连续发送两条,第二条都会覆盖第一条的一部分!!!
	if(txBuf10[BUF_FLAG] == BUF_STATUS_FREE)
	{
		for( i = 0; i < len; i++)
		{
			txBuf10[i] = *(buf + i);
		}
		txBuf10[BUF_FLAG] = len;
		bResult = true;
		//BSP_LED3_Toggle();	//test OK!
		HAL_UART_Transmit_DMA(&huart1, (uint8_t*)txBuf10, len);
	}
	return bResult;
}
//串口DMA发送中断的处理 stm32f1xx_it.c中

void DMA1_Channel4_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel4_IRQn 0 */

  /* USER CODE END DMA1_Channel4_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart1_tx);
  /* USER CODE BEGIN DMA1_Channel4_IRQn 1 */
//确认是DMA完全发送结束中断,注意有一个发送一半中断
  if(__HAL_DMA_GET_IT_SOURCE(&hdma_usart1_tx, DMA_IT_TC))
  {
	huart1.gState = HAL_UART_STATE_READY;
	hdma_usart1_tx.State = HAL_DMA_STATE_READY;
	__HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx, DMA_FLAG_TC4);
	__HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx, DMA_FLAG_HT4);
	__HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx, DMA_FLAG_TE4);
	__HAL_UNLOCK(&hdma_usart1_tx);
	txBuf10[BUF_FLAG] = BUF_STATUS_FREE;//设置为可发送状态
  }
  /* USER CODE END DMA1_Channel4_IRQn 1 */
}

测试结果:


21:23:55 You Send : SETN
Receive cache:0, response in blocking mode...0.
Receive cache:0, response in blocking mode...1.
Receive cache:0, response in blocking mode...2.
Receive cache:0, response in blocking mode...3.
Receive cache:0, response in blocking mode...4.

21:23:56 You Send : SETN
Receive cache:1, response in blocking mode...0.
Receive cache:1, response in blocking mode...1.
Receive cache:1, response in blocking mode...2.
Receive cache:1, response in blocking mode...3.
Receive cache:1, response in blocking mode...4.

21:24:03 You Send : SETD
Receive cache:0, response with DMA..Receive cache:0, response with DMA.Receive cache:0, response with DMAReceive cache:0, response with DMA.Receive cache:0, response with DMA...4.

21:24:04 You Send : SETD
Receive cache:1, response with DMA..Receive cache:1, response with DMA.Receive cache:1, response with DMAReceive cache:1, response with DMA.Receive cache:1, response with DMA...4.
 

从结果看,接收双缓冲用上了,串口DMA发送拉胯。。。,阻塞式发送优秀!!

实在找不出原因了。。。囧

完整代码:

FREERTOS+USART+IDLE+DMA双缓冲接收,DMA发送和阻塞式发送-硬件开发文档类资源-CSDN下载

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: STM32是一款由ST(意法半导体)公司开发的高性能32位单片机系列。它集成了丰富的外设,如串口、定时器、PWM、ADC等,同时支持多种通信接口如SPI、I2C和CAN等。STM32 HAL(Hardware Abstraction Layer)是ST公司为STM32系列开发的一套硬件抽象层,它提供了一套统一的编程接口,简化了在不同STM32芯片之间的移植工作。 FreeRTOS一个流行的实时操作系统(RTOS),它在STM32上得到广泛的应用。它提供了多任务调度、信号量、消息队列等功能,可以帮助开发者实现复杂的任务并行处理。在STM32中使用FreeRTOS,可以充分利用STM32的多核处理能力和丰富的外设资源。 MQTT是一种轻量的消息传输协议,广泛应用于物联网领域。它通过发布和订阅模式实现消息的传输,具有简单、开销小、可靠性高的特点。在STM32中使用MQTT,可以实现与各种设备的通信,如传感器、控制器等。 综上所述,STM32 HAL是ST公司为STM32系列开发的硬件抽象层,可以方便地在不同芯片之间移植。FreeRTOS一个实时操作系统,能够帮助开发者实现并行处理和任务调度。MQTT是一种轻量的消息传输协议,可以用于STM32与其他设备之间的通信。通过结合使用这三种技术,可以开发出高性能、可靠的物联网应用。 ### 回答2: STM32 HAL是ST公司提供的一套基于硬件抽象层的开发库,用于简化嵌入式系统的开发。HAL库提供了一系列功能丰富的函数接口,包括GPIO、UART、SPI、I2C等外设的控制接口,可以方便地对STM32单片机进行配置和控制。 FreeRTOS是一款广泛使用的开源实时操作系统(RTOS),适用于嵌入式系统的开发。FreeRTOS提供了任务管理、调度器、队列、信号量等功能,可以用于多任务的并发执行。它具有轻量、可移植、可靠等特点,广泛应用于各种嵌入式系统中。 MQTT(Message Queuing Telemetry Transport)是一种基于发布-订阅模式的轻量级通信协议,常用于物联网(IoT)应用中的设备间通信。MQTT协议使用简单、开销小,适用于带宽有限的场景。它通过客户端和代理服务器之间的消息传递实现通信,支持可靠传输和压缩技术,可以满足物联网应用对低功耗、低带宽的要求。 结合起来,使用STM32 HAL库和FreeRTOS可以实现在STM32单片机上运行MQTT协议。HAL库提供了对待控制的硬件外设的支持,可以与MQTT库进行配合,实现对设备的配置和控制。FreeRTOS提供了任务管理和调度功能,可以用于处理MQTT消息的异步接收和处理,以及与其他任务的并行执行。通过这些组件的结合使用,可以开发出功能强大、稳定可靠的物联网设备。 ### 回答3: STM32 HAL是指STM32微控制器的硬件抽象层(Hardware Abstraction Layer)。它提供了一个统一的接口,以便开发人员能够简化对STM32微控制器的底层硬件操作。通过使用HAL,开发人员可以更方便地编写可移植且易于维护的代码。 FreeRTOS一个开源的嵌入式实时操作系统(RTOS)。它提供了任务调度、时间管理、内存管理、通信和同步机制等功能,使开发人员能够更方便地编写多任务并发的嵌入式应用程序。在STM32项目中,FreeRTOS通常与STM32 HAL一起使用,以实现高效的任务调度和资源管理。 MQTT是一种基于发布/订阅模式的轻量级消息传输协议。它被广泛应用于物联网等场景中,以实现设备之间的消息通信。MQTT具有低延迟、低能耗和网络带宽占用小等特点,非常适合在资源有限的嵌入式系统中使用。在STM32 HAL和FreeRTOS的基础上,使用MQTT可以实现STM32微控制器与其他设备之间的可靠、高效的通信。 总结来说,STM32 HAL提供了对STM32微控制器硬件的抽象接口,简化了底层编程;FreeRTOS一个实时操作系统,提供了任务调度和资源管理;而MQTT是一种轻量级的消息传输协议,用于在嵌入式系统中实现设备之间的通信。这三个技术共同使用可以实现高效、可靠的嵌入式应用程序开发。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

st01lsp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值