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

本文详细介绍了使用STM32单片机结合FREERTOS实现串口双缓冲接收和DMA发送的过程。在双缓冲接收中,通过两个接收缓存交替保存串口数据,避免信息丢失。然而,在串口DMA发送时,作者遇到一个问题:连续发送字节串会导致数据覆盖。尽管尝试了各种方法,但未能解决这个问题,最终选择采用阻塞式发送。文章提供了关键代码,并展示了测试结果,显示接收功能正常,但DMA发送存在问题。

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

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

根据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发送和阻塞式发送-硬件开发文档类资源-优快云下载

<think>首先,用户的问题是关于在STM32L471芯片上,使用HAL库和FreeRTOS环境,通过USART1和DMA同时进行日志发送DMA发送)和指令接收DMA + 空闲中断)。问题在于,当同时使用时,日志功能被卡死。 用户提供了几个引用: - 引用[1]:讨论使用DMA串口发送的问题,作者提到在STM32CubeMX中修改例程时遇到的问题,并计划写一篇关于使用DMA串口Normal模式发送的博文。 - 引用[2]:列出了HAL_UART的各种函数,包括DMA发送接收等。 - 引用[3]:显示了USART1的初始化代码。 用户的上一个问题不是直接给出的,所以我需要基于当前查询和引用来回应。用户说“参考站内引用”,但这不是真正的需求;我应该参考用户上一次的问题和我的回答。但在这个对话中,没有显示上一次问题,所以我基于当前查询。 关键点:用户想解决log发送卡死的问题,当同时使用DMA发送log和DMA接收指令时。 可能的原因: - **资源冲突**:USART1同时用于发送接收DMA通道可能共享或冲突。 - **中断优先级**:在FreeRTOS环境中,中断处理不当可能导致任务阻塞。 - **DMA配置错误**:发送接收DMA可能有竞争条件或配置错误。 - **空闲中断处理**:空闲中断用于接收,可能干扰发送。 - **FreeRTOS调度**:任务优先级或同步问题。 解决方案步骤: 1. **分离DMA通道**:确保发送接收使用不同的DMA通道,避免硬件冲突。 2. **中断优先级**:设置中断优先级正确,避免嵌套中断或抢占。 3. **DMA发送模式**:使用DMA Normal模式或Circular模式;Normal模式需要每次启动,Circular模式持续运行。 4. **空闲中断处理**:在空闲中断中,正确处理接收数据,避免长时间占用中断。 5. **FreeRTOS集成**:使用FreeRTOS特性,如semaphores或queues,来协调发送接收。 6. **错误处理**:添加错误检测和恢复机制。 从引用[1]中,作者提到在DMA串口发送时,有一个问题需要注意细节,可能与缓冲区或启动序列有关。 引用[2]提供了HAL_UART函数,如HAL_UART_Transmit_DMA用于DMA发送。 引用[3]显示USART初始化代码。 用户芯片是STM32L471,USART1。 在HAL库中,使用DMA发送接收时: - 对于发送:使用HAL_UART_Transmit_DMA() - 对于接收:使用HAL_UART_Receive_DMA() 或结合空闲中断。 空闲中断:设置空闲检测中断来触发当接收线空闲,表示一帧数据结束。 卡死问题可能由于: - **发送阻塞**:如果DMA发送未正确启动或完成。 - **接收中断占用CPU**:空闲中断处理不当,导致高优先级中断阻塞其他任务。 - **DMA竞争**:共享DMA资源导致冲突。 解决方案思路: 1. **检查DMA配置**:确保发送接收使用不同的DMA Streams或Channels。在STM32中,每个USART有特定的DMA请求映射。 - 对于USART1_TX,通常是DMA1_Channel4(STM32L4系列)。 - 对于USART1_RX,通常是DMA1_Channel5。 - 确认在CubeMX中配置正确。 2. **中断优先级**: - 设置空闲中断优先级低于或等于系统tick中断,以避免影响FreeRTOS调度。 - 在FreeRTOS中,使用configMAX_SYSCALL_INTERRUPT_PRIORITY来管理中断优先级。 3. **使用DMA Circular模式用于接收**:对于接收数据,使用循环模式来连续接收数据,空闲中断触发处理。 4. **发送流程**:对于log发送,使用DMA Normal模式;每次发送前检查DMA状态是否空闲。 5. **互斥**:由于USART发送器和接收器共享同一个物理接口,需要软件互斥。使用FreeRTOS信号量或互斥锁来保护串口访问。 - 例如,在发送log前获取互斥锁,发送后释放;接收中断中快速处理数据。 6. **空闲中断处理**: - 在空闲中断中,禁用中断,读取数据,然后重新启用接收。 - 使用HAL_UART_DMAStop() 和 HAL_UART_Receive_DMA() 来重新启动接收。 7. **添加超时和错误处理**:在发送函数中添加超时逻辑,防止无限等待。 8. **优化log发送**:使用队列缓冲log消息,避免频繁DMA启动。 具体步骤: - **初始化**:在CubeMX中配置USART1,启用DMA for TX and RX,设置空闲中断。 - **代码**: - 定义全局变量或结构来管理发送接收状态。 - 创建FreeRTOS任务:一个用于log发送一个用于指令处理。 - 在log发送任务中,使用互斥锁保护USART发送。 - 在空闲中断处理函数中,快速拷贝数据到缓冲区,然后唤醒任务处理。 参考用户引用[1],作者提到“问题的答案很早就写在里面了”,可能指在DMA发送时,需要在发送前停止接收或其他操作。 常见问题:如果在DMA发送过程中,空闲中断发生可能导致冲突。 解决方案:在启动DMA发送前暂停接收DMA发送完成后恢复接收。 使用HAL函数: - HAL_UART_DMAPause(&huart1); // 在发送前暂停接收 - HAL_UART_Transmit_DMA(...); // 发送log - 等待发送完成(使用callback或查询) - HAL_UART_DMAResume(&huart1); // 恢复接收 在HAL中,有HAL_UART_DMAPause() 和 HAL_UART_DMAResume() 用于管理DMA。 在接收端,使用空闲中断: - 启用空闲中断:__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); - 在中断处理函数中检测空闲中断。 在FreeRTOS环境中,保持中断处理简短。 回答结构: 1. 确认问题。 2. 分析可能原因。 3. 提供解决方案步骤。 4. 给出代码示例。 5. 建议调试方法。 6. 生成相关问题(根据系统指令)。 系统指令要求: - 行内数学:如无必要,可不使用。 - 独立公式:可能不需要。 - 中文回答。 - 生成相关问题。 - 引用末尾添加标识,如[^1]。 在回答中,引用用户提供的引用。 相关问题: 1. 如何配置STM32L4的USART DMA优先级? 2. FreeRTOS中如何处理串口中断? 3. DMA串口发送接收的最佳实践? </think>### STM32L471 HAL FreeRTOS USART1 DMA日志发送接收卡死问题解决方案 在STM32L471平台上,使用HAL库和FreeRTOS,通过USART1同时进行DMA日志发送DMA+空闲中断接收指令时,日志功能卡死是一个常见问题。主要原因包括:USART发送器和接收器共享硬件资源导致冲突、DMA通道竞争、空闲中断优先级设置不当、FreeRTOS任务调度阻塞等。以下基于您的引用[^1][^2][^3]和我之前的经验,提供逐步解决方案。问题核心在于**资源冲突和中断管理**,引用[^1]提到“千里之堤,溃于蝼蚁”,强调细节处理的重要性。 #### 问题分析 - **卡死原因**: - **资源冲突**:USART1的发送(TX)和接收(RX)共享同一串口,DMA传输未正确同步,导致硬件锁死[^1]。 - **中断优先级**:空闲中断(用于接收)优先级过高,抢占FreeRTOS调度,导致日志发送任务阻塞[^2]。 - **DMA配置问题**:发送(HAL_UART_Transmit_DMA)和接收(HAL_UART_Receive_DMA)同时使用同一DMA控制器时,未处理竞争条件(如发送过程中接收中断触发)。 - **FreeRTOS影响**:任务未正确使用互斥机制,发送任务可能被接收中断打断并无限等待。 - **引用关键点**:[^1]指出问题与DMA发送启动序列相关;[^2]提供了HAL_UART函数,强调DMA暂停/恢复的重要性;[^3]显示USART初始化代码,但需适配STM32L471。 #### 逐步解决方案 以下步骤基于STM32CubeMX和HAL库实现,确保代码可移植。**核心原则**:分离DMA资源、优化中断优先级、添加FreeRTOS同步机制。 1. **检查硬件配置(STM32CubeMX)**: - 确保USART1的TX和RX使用**独立DMA通道**(STM32L471中,USART1_TX常用DMA1 Channel 4,USART1_RX用DMA1 Channel 5)。 - 在CubeMX中启用USART1全局中断和空闲中断(UART_IT_IDLE)。 - 设置DMA优先级: - TX DMA优先级:Medium - RX DMA优先级:Low(避免抢占发送) - 参考[^3]初始化代码,但调整BaudRate为115200,Mode为UART_MODE_TX_RX_DMA。 - 生成代码后,验证CubeMX配置是否匹配引用[^3]。 2. **优化中断优先级**: - 空闲中断优先级必须**低于或等于** FreeRTOS的系统tick中断优先级(通常为5-15),防止中断抢占导致任务阻塞。 - 在`FreeRTOSConfig.h`中设置: ```c #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 // FreeRTOS可管理中断的最高优先级 ``` - 在USART初始化代码中添加中断优先级设置: ```c /* 在USART1初始化后添加 */ HAL_NVIC_SetPriority(USART1_IRQn, 6, 0); // 优先级6(低于FreeRTOS tick) HAL_NVIC_EnableIRQ(USART1_IRQn); ``` 3. **修改DMA发送接收逻辑**: - **发送日志(log输出)**: - 使用**DMA Normal模式**发送(非循环模式),每次发送前检查DMA状态。 - 在FreeRTOS任务中,发送前暂停接收DMA,防止冲突: ```c void vLogTask(void *pvParameters) { while(1) { // 获取信号量(确保串口空闲) if (xSemaphoreTake(xUARTSemaphore, portMAX_DELAY) == pdTRUE) { HAL_UART_DMAPause(&huart1); // 暂停接收DMA[^2] if (HAL_UART_Transmit_DMA(&huart1, logBuffer, logSize) != HAL_OK) { // 错误处理,如重试或记录错误 } // 等待发送完成(使用回调或查询) while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY) { vTaskDelay(pdMS_TO_TICKS(1)); // 释放CPU给其他任务 } HAL_UART_DMAResume(&huart1); // 恢复接收DMA[^2] xSemaphoreGive(xUARTSemaphore); // 释放信号量 } vTaskDelay(pdMS_TO_TICKS(10)); // 减少任务频率 } } ``` - **接收指令(空闲中断)**: - 初始化接收DMA为**循环模式**(Circular),持续接收数据。 - 在空闲中断中快速处理数据,避免长时间占用中断: ```c void USART1_IRQHandler(void) { if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart1); // 禁用中断并处理数据 HAL_UART_DMAStop(&huart1); // 停止DMA接收[^2] size_t receivedSize = LOG_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx); // 拷贝数据到安全缓冲区 memcpy(cmdBuffer, rxBuffer, receivedSize); // 重启接收 HAL_UART_Receive_DMA(&huart1, rxBuffer, LOG_BUFFER_SIZE); BaseType_t xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR(xCmdSemaphore, &xHigherPriorityTaskWoken); // 唤醒指令处理任务 portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } ``` 4. **添加FreeRTOS同步机制**: - 创建信号量(Semaphore)来协调串口访问: ```c SemaphoreHandle_t xUARTSemaphore; // 用于串口互斥 SemaphoreHandle_t xCmdSemaphore; // 用于指令通知 void MX_FREERTOS_Init(void) { xUARTSemaphore = xSemaphoreCreateMutex(); // 互斥信号量 xCmdSemaphore = xSemaphoreCreateBinary(); // 二进制信号量用于指令 xTaskCreate(vLogTask, "LogTask", 128, NULL, 3, NULL); // 优先级3 xTaskCreate(vCmdTask, "CmdTask", 128, NULL, 4, NULL); // 优先级4(高于日志任务) } ``` - 指令处理任务示例: ```c void vCmdTask(void *pvParameters) { while(1) { if (xSemaphoreTake(xCmdSemaphore, portMAX_DELAY) == pdTRUE) { // 处理接收到的指令cmdBuffer } } } ``` 5. **错误处理和优化**: - **添加超时**:在`HAL_UART_Transmit_DMA`调用后,使用超时逻辑(如引用[^2]的Timeout参数)。 - **DMA状态检查**:发送前验证DMA是否空闲:`if (HAL_DMA_GetState(huart1.hdmatx) == HAL_DMA_STATE_READY)`。 - **缓冲区管理**:使用双缓冲(ping-pong buffer)避免数据覆盖。 - **初始化序列**:引用[^1]强调启动顺序,确保接收DMA发送前启动: ```c // 启动接收DMA在主初始化中 HAL_UART_Receive_DMA(&huart1, rxBuffer, LOG_BUFFER_SIZE); __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 启用空闲中断 ``` #### 调试建议 - **测试步骤**: 1. 最小化测试:先禁用接收中断,只测试日志发送是否正常。 2. 逐步启用:添加空闲中断后,用逻辑分析仪检查USART信号。 3. 监控任务:使用FreeRTOS的`uxTaskGetStackHighWaterMark`检查任务栈溢出。 - **常见陷阱**: - 未清除空闲中断标志(导致重复中断)。 - DMA缓冲区未对齐(STM32L471要求字对齐)。 - FreeRTOS任务优先级倒置(指令任务优先级应高于日志任务)。 #### 完整代码摘要 在STM32CubeMX生成代码基础上,修改如下: - `usart.c`:启用DMA和空闲中断。 - `stm32l4xx_it.c`:实现`USART1_IRQHandler`。 - `freertos.c`:添加任务和信号量初始化。 - 确保所有HAL_UART函数引用自[^2]。 通过以上步骤,您能解决日志卡死问题。引用[^1]提到“这个问题困扰了我很久”,正确同步DMA和中断是关键[^1]。如果仍有问题,提供更多代码细节以进一步分析。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

st01lsp

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

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

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

打赏作者

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

抵扣说明:

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

余额充值