IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32笔记:FREERTOS+USART+IDLE+DMA双缓冲接收,DMA发送存在一个BUG,请教有心人。 -> 正文阅读

[嵌入式]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下载

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-02-07 13:52:33  更:2022-02-07 13:54:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:32:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码