【InterFace】STM32 I2C 死锁问题

本文针对STM32系列I2C模块在特定条件下出现的死锁问题提供了详细的解决方案,包括STM32F207和STM32F103系列的解决方法。同时分享了一种解决I2C总线被锁死(SDA持续为低电平)的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#背景
其实这篇文章在很久之前就写过解决方法了。在经过不断的实践和深究后发现,硬件 I2C 死锁的问题在ST的官方手册中的勘误手册(errata)中早就提供解决方案,只是我没有重视官方的文档,一直在网络寻求帮助。

即使已经有官方的解决方案,但是还有很多人(包括以前的我)在怀疑 STM32 系列的 I2C 有硬件 BUG。这也告诉我们:网上资源虽丰富,但还是得通过“金睛火眼”来辨别。

讲真的,为了解决 I2C 问题,我在网上看了 N 多篇的文章、Blog 和帖子,还没看到几个人说 “STM32 硬件 I2C 没问题”,反而是看到很多类似这样的:“都听说STM32 硬件 I2C 有问题,一试,发现还真是有问题,改用 IO 模拟吧”

不再哔哔~~~

下面我将提供 STM32F207 和 STM32F103 系列的 I2C 死锁(一直为 BUSY 状态或 START 一直置位)问题的解决方案。同时在底部,依旧保存了我以前的解决方案(SDA为 LOW),如果你遇到的是SDA 被置为 LOW 的问题而已,你完全可以采用旧的解决方案。

#I2C 死锁描述
本文所描述的 I2C 死锁问题,表现为:当 I2C 通讯出现异常后,SDA 和 SCL 均为高(即 IDLE 状态),在调用 HAL_I2C_Master_Transmit 或者 HAL_I2C_Master_Receive 一直返回 BUSY 或 TIMEOUT。通过逻辑分析仪查看总线一直为HIGH。
通常这种异常发生:

  • 在 Slave 设备拔除总线后,Master 出现异常
  • 一次通讯被异常中断,导致 Master 出现异常

在 STM32F207 中,上述的问题能通过 MCU 软件复位来解决。但是对于 STM32F103 的 MCU 软件复位并不能完全解决问题,经常是需要断电重启。在正常场景中,我们当然是不希望需要通过软件复位或断电解决啦!那您就得继续往下看了。

通过 Debug,可以看到,在出现异常时,I2C相关寄存器的值,如下面两图所示。

图1:一直为 BUSY 状态时的 I2C 寄存器状态
这里写图片描述

图2:START 位一直被置位时的 I2C 寄存器状态
这里写图片描述

#STM32F207 解决方案
相对于 STM32F103 来说,STM32F207 的解决方案是比较简单的,仅需要对 进行 I2C 外设复位。也许你会说,这算什么解决办法!!!拜托,人家之前并不知道它还有外设复位寄存器位嘛~~~

首先咱们先来看看勘误手册的描述。
这里写图片描述

Example:用于总线复位的函数

static HAL_StatusTypeDef I2CResetBus(void)
{
	__HAL_I2C_DISABLE(&hi2c1);
	/* 1. Set SWRST bit in I2Cx_CR1 register. */
	hi2c1.Instance->CR1 |=  I2C_CR1_SWRST;
	HAL_Delay(2);
	/* 2. Clear SWRST bit in I2Cx_CR1 register. */
	hi2c1.Instance->CR1 &=  ~I2C_CR1_SWRST;
	HAL_Delay(2);
	/* 3. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register */
	MX_I2C1_Init();
	__HAL_I2C_ENABLE(&hi2c1);
	HAL_Delay(2);
#ifdef I2C_TEST	
	printf("I2CResetBus\r\n");
#endif
	hi2c1.ErrorCode = HAL_I2C_ERROR_NONE;
	hi2c1.State = HAL_I2C_STATE_READY;
	hi2c1.PreviousState = I2C_STATE_NONE;
	hi2c1.Mode = HAL_I2C_MODE_NONE;
	return HAL_OK;
}

上面的函数里面有一行 MX_I2C1_Init() 用于给 I2C 进行配置。这里是因为 I2C 进行复位后,寄存器的值均会被修改掉。只能再配置一遍。

#STM32F103解决方案
STM32F103 的方法比较麻烦,首先,咱们先看看勘误手册的描述。

这里写图片描述
这里写图片描述

我对勘误手册的理解是:将管脚配置为普通输出管脚后,实现电平的反转,以达到解除死锁,再将其恢复为 I2C 配置。

Example:解决代码

static void User_I2C2_GeneralPurposeOutput_Init(I2C_HandleTypeDef* i2cHandle)
{

	GPIO_InitTypeDef GPIO_InitStruct;
	if(i2cHandle->Instance==I2C2)
	{
		/*   PB10     ------> I2C2_SCL; PB11     ------> I2C2_SDA */
		GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
		GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	}
}


static void User_I2C2_AlternateFunction_Init(I2C_HandleTypeDef* i2cHandle)
{

	GPIO_InitTypeDef GPIO_InitStruct;
	if(i2cHandle->Instance==I2C2)
	{
		/*   PB10     ------> I2C2_SCL; PB11     ------> I2C2_SDA */
		GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	}
}

HAL_StatusTypeDef I2CResetBus(void)
{
	hi2c2.ErrorCode = HAL_I2C_ERROR_AF;
	/* 1. Disable the I2C peripheral by clearing the PE bit in I2Cx_CR1 register */
	__HAL_I2C_DISABLE(&hi2c2);
	HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);
	
	/* 2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR) */
	User_I2C2_GeneralPurposeOutput_Init(&hi2c2);
	HAL_Delay(1);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10 | GPIO_PIN_11, GPIO_PIN_SET);
	HAL_Delay(1);
	
	/* 3. Check SCL and SDA High level in GPIOx_IDR */
	if ((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10) != GPIO_PIN_SET)||(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11) != GPIO_PIN_SET))
	{
#ifdef I2C_TEST	
		printf("3.PB10=%d, PB11=%d\r\n", HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10), HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11));
#endif
		return HAL_ERROR;
	}
	
	/* 4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR).
	 * 5. Check SDA Low level in GPIOx_IDR.
	 * 6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR)
	 * 7. Check SCL Low level in GPIOx_IDR.
	 * */
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10|GPIO_PIN_11, GPIO_PIN_RESET);
	HAL_Delay(1);
	if ((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10) != GPIO_PIN_RESET)||(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11) != GPIO_PIN_RESET))
	{
#ifdef I2C_TEST	
		printf("4-7.PB10=%d, PB11=%d\r\n", HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10), HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11));
#endif
		return HAL_ERROR;
	}
	
	/*
	 * 8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR).
	 * 9. Check SCL High level in GPIOx_IDR.
	 * 10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to GPIOx_ODR).
	 * 11. Check SDA High level in GPIOx_IDR.
	 */
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10|GPIO_PIN_11, GPIO_PIN_SET);
	HAL_Delay(1);
	if ((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10) != GPIO_PIN_SET)||(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11) != GPIO_PIN_SET))
	{
#ifdef I2C_TEST	
		printf("8-11.PB10=%d, PB11=%d\r\n", HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10), HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11));
#endif
		return HAL_ERROR;
	}
	
	/* 12. Configure the SCL and SDA I/Os as Alternate function Open-Drain. */
	HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);
	User_I2C2_AlternateFunction_Init(&hi2c2);
	
	/* 13. Set SWRST bit in I2Cx_CR1 register. */
	hi2c2.Instance->CR1 |=  I2C_CR1_SWRST;
	HAL_Delay(2);
	/* 14. Clear SWRST bit in I2Cx_CR1 register. */
	hi2c2.Instance->CR1 &=  ~I2C_CR1_SWRST;
	HAL_Delay(2);
	/* 15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register */
	MX_I2C2_Init();
	__HAL_I2C_ENABLE(&hi2c2);
	HAL_Delay(2);
#ifdef I2C_TEST	
	printf("I2CResetBus\r\n");
#endif
	hi2c2.ErrorCode = HAL_I2C_ERROR_NONE;
	hi2c2.State = HAL_I2C_STATE_READY;
	hi2c2.PreviousState = I2C_STATE_NONE;
	hi2c2.Mode = HAL_I2C_MODE_NONE;
	return HAL_OK;
}

#SDA 为 LOW的解决方案

最近在项目中设计了一个 IIC 模拟从机的程序。为了图方便,我随便拿了个 STM32F207 的开发板做 IIC Master,用 STM32CUBE 做了个程序,Master 的 数据发送和接收,都是直接调用 HAL 库的函数。
通过逻辑分析仪测试发现,**每次主机出现错误后,IIC SDA 会被拉低,导致整个 IIC 总线被锁死了。后续的数据传输异常。**现象如下图所示:

这里写图片描述

这里写图片描述

后来我查看了 HAL 库的 IIC 的 HAL_I2C_Master_Transmit 函数。
发现:当出现 TIMEOUT 或 ERROR 时,STM32 Master 并不会产生 STOP 信号,或者,将总线释放(SDA 和 SCL 置高)。这样就会导致,当出现 TIMEOUT 或者 ERROR 后, 下一次进入HAL_I2C_Master_Transmit ,Master 会认为 IIC 总线为 BUSY,而放弃通讯,造成 SDA 被锁死的现象。
然后,我在 HAL_I2C_Master_Transmit 函数做了些改动,如下面的程序所示。
NOTE:如果是用 HAL_I2C_Master_Transmit 生成的程序,做修改时,必须把这段程序复制出来,保存到别的文件中,不然,在使用 STM32CUBE 再修改程序时,原来的修改会被覆盖掉。

/**
  * @brief  Transmits in master mode an amount of data in blocking mode.
  * @param  hi2c Pointer to a I2C_HandleTypeDef structure that contains
  *                the configuration information for the specified I2C.
  * @param  DevAddress Target device address: The device 7 bits address value
  *         in datasheet must be shifted to the left before calling the interface
  * @param  pData Pointer to data buffer
  * @param  Size Amount of data to be sent
  * @param  Timeout Timeout duration
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint32_t tickstart = 0x00U;

  /* Init tickstart for timeout management*/
  tickstart = HAL_GetTick();

  if(hi2c->State == HAL_I2C_STATE_READY)
  {
    /* Wait until BUSY flag is reset */
    // 下面的代码就是用于检测 IIC 总线是否为 BUSY,当 SDA 和 SCL 同时为高,才会被认为是空闲(IDLE),否则,会被认为是 BUSY。
    if(I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG, tickstart) != HAL_OK)
    {
      return HAL_BUSY;
    }

    /* Process Locked */
    __HAL_LOCK(hi2c);

    /* Check if the I2C is already enabled */
    if((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE)
    {
      /* Enable I2C peripheral */
      __HAL_I2C_ENABLE(hi2c);
    }

    /* Disable Pos */
    hi2c->Instance->CR1 &= ~I2C_CR1_POS;

    hi2c->State     = HAL_I2C_STATE_BUSY_TX;
    hi2c->Mode      = HAL_I2C_MODE_MASTER;
    hi2c->ErrorCode = HAL_I2C_ERROR_NONE;

    /* Prepare transfer parameters */
    hi2c->pBuffPtr    = pData;
    hi2c->XferCount   = Size;
    hi2c->XferOptions = I2C_NO_OPTION_FRAME;
    hi2c->XferSize    = hi2c->XferCount;

    /* Send Slave Address */
    if(I2C_MasterRequestWrite(hi2c, DevAddress, Timeout, tickstart) != HAL_OK)
    {
      if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
      {
        /* 此处为我自行添加的部分,当出现错误时,产生 Stop 信号,以释放总线。*/
        hi2c->Instance->CR1 |= I2C_CR1_STOP;
        /* Process Unlocked */
        __HAL_UNLOCK(hi2c);

        return HAL_ERROR;
      }
      else
      {

        /*此处为我自行添加的部分,当出现错误时,产生 Stop 信号,以释放总线。*/
        hi2c->Instance->CR1 |= I2C_CR1_STOP;
         /* Process Unlocked */
        __HAL_UNLOCK(hi2c);
        return HAL_TIMEOUT;
      }
    }

    /* Clear ADDR flag */
    __HAL_I2C_CLEAR_ADDRFLAG(hi2c);

    while(hi2c->XferSize > 0U)
    {
      /* Wait until TXE flag is set */
      if(I2C_WaitOnTXEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
      {
        if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
        {
          /* 此处为我自行添加的部分,当出现错误时,产生 Stop 信号,以释放总线。*/
          hi2c->Instance->CR1 |= I2C_CR1_STOP;
          return HAL_ERROR;
        }
        else
        {
            /* 此处为我自行添加的部分,当出现错误时,产生 Stop 信号,以释放总线。*/
            hi2c->Instance->CR1 |= I2C_CR1_STOP;
          return HAL_TIMEOUT;
        }
      }

      /* Write data to DR */
      hi2c->Instance->DR = (*hi2c->pBuffPtr++);
      hi2c->XferCount--;
      hi2c->XferSize--;

      if((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF) == SET) && (hi2c->XferSize != 0U))
      {
        /* Write data to DR */
        hi2c->Instance->DR = (*hi2c->pBuffPtr++);
        hi2c->XferCount--;
        hi2c->XferSize--;
      }
      
      /* Wait until BTF flag is set */
      if(I2C_WaitOnBTFFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
      {
        if(hi2c->ErrorCode == HAL_I2C_ERROR_AF)
        {
          /* 此处为我自行添加的部分,当出现错误时,产生 Stop 信号,以释放总线。*/
          hi2c->Instance->CR1 |= I2C_CR1_STOP;
          return HAL_ERROR;
        }
        else
        {
            /* 此处为我自行添加的部分,当出现错误时,产生 Stop 信号,以释放总线。*/
            hi2c->Instance->CR1 |= I2C_CR1_STOP;
          return HAL_TIMEOUT;
        }
      }
    }

    /* Generate Stop */
    hi2c->Instance->CR1 |= I2C_CR1_STOP;

    hi2c->State = HAL_I2C_STATE_READY;
    hi2c->Mode = HAL_I2C_MODE_NONE;
    
    /* Process Unlocked */
    __HAL_UNLOCK(hi2c);

    return HAL_OK;
  }
  else
  {
	/* 此处为我自行添加的部分,当出现错误时,产生 Stop 信号,以释放总线。*/
    hi2c->Instance->CR1 |= I2C_CR1_STOP;
    /* Process Unlocked */
    __HAL_UNLOCK(hi2c);
    return HAL_BUSY;
  }
}

下面是修改后的效果
这里写图片描述

希望对您有帮助~

<think>好的,用户问的是怎么监控STM32 FreeRTOS两个线程卡死的问题。我需要详细分析这个问题,并提供有效的解决方案。首先,我得回想一下FreeRTOS中线程(任务)卡死可能的原因,比如死锁、无限循环、优先级反转或者资源未释放等。监控方法可能需要结合FreeRTOS提供的工具和用户自定义的检测机制。 用户可能是在开发嵌入式系统时遇到了任务卡死的情况,导致系统无法正常运行。他们需要具体的监控手段,而不仅仅是理论上的解释。可能需要实际操作步骤,比如如何配置看门狗、使用钩子函数或者分析堆栈使用情况。 首先,我应该考虑FreeRTOS内置的诊断工具,比如任务状态查询、堆栈溢出检测等。然后,外部的硬件看门狗也是一个重要的点,尤其是独立看门狗和窗口看门狗的使用。接着,任务运行时间监控,可以通过钩子函数或者定时器来检查任务是否在规定时间内执行完毕。 另外,用户可能需要知道如何设置断言和调试输出,比如使用configASSERT来捕捉错误。还有,死锁检测可以通过记录资源获取顺序,或者使用互斥锁的优先级继承机制来避免。 还有,用户提到的两个线程,可能需要分别监控它们的运行状态,比如通过任务通知或者信号量来确认它们是否在预期时间内响应。同时,堆栈溢出是一个常见问题,开启FreeRTOS的堆栈检测功能也很重要。 我需要组织这些思路,分点列出,确保每个方法都有具体的实现步骤,比如代码示例或者配置选项。同时,注意用户可能对FreeRTOS的配置不太熟悉,需要给出具体的配置方法,比如修改FreeRTOSConfig.h中的宏定义。 最后,总结一下推荐的组合方案,帮助用户全面监控任务状态,并快速定位卡死原因。可能还需要提醒用户在实际调试中使用IDE的调试工具,比如ST-Link和Keil的RTOS插件,这些工具可以实时查看任务状态和资源使用情况,辅助诊断问题。</think>在STM32+FreeRTOS环境下监控线程(任务)卡死问题,可通过以下多维度方案实现: --- ### **一、基础监控机制** #### 1. **看门狗分层防护** ```c // 独立看门狗(IWDG)配置(应对完全死锁) IWDG_HandleTypeDef hiwdg; hiwdg.Instance = IWDG; hiwdg.Init.Prescaler = IWDG_PRESCALER_256; // 40kHz/256=156Hz hiwdg.Init.Reload = 1560; // 10秒超时 HAL_IWDG_Init(&hiwdg); // 任务级看门狗(需在任务中定期喂狗) typedef struct { TaskHandle_t handle; uint32_t lastFeed; } TaskWatchdog; TaskWatchdog monitoredTasks[2] = { {task1Handle, 0}, {task2Handle, 0} }; void vApplicationTickHook(void) { // 在时钟节拍钩子中检查 for(int i=0; i<2; i++) { if(xTaskGetTickCount() - monitoredTasks[i].lastFeed > pdMS_TO_TICKS(5000)) { // 触发异常处理 __disable_irq(); Error_Handler(); } } } ``` #### 2. **任务状态监控** ```c void MonitorTask(void *pvParameters) { for(;;) { TaskStatus_t taskStatus; // 获取任务1状态 vTaskGetInfo(task1Handle, &taskStatus, pdTRUE, eInvalid); if(taskStatus.eCurrentState == eBlocked && (xTaskGetTickCount() - taskStatus.xBlockTime) > pdMS_TO_TICKS(10000)) { // 超过10秒阻塞报警 Log_Error("Task1 Blocked Too Long!"); } vTaskDelay(pdMS_TO_TICKS(1000)); } } ``` --- ### **二、深度检测手段** #### 1. **堆栈溢出检测** ```c // 在FreeRTOSConfig.h中启用检测 #define configCHECK_FOR_STACK_OVERFLOW 2 // 自定义溢出处理函数 void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { __asm volatile("cpsid i"); // 关中断 printf("Stack Overflow in %s\n", pcTaskName); while(1); } ``` #### 2. **资源死锁检测** ```c // 互斥锁使用追踪 typedef struct { MutexHandle_t mutex; TaskHandle_t owner; uint32_t acquireTime; } MutexTracker; void xMutexLock(MutexHandle_t mutex) { uint32_t entryTime = xTaskGetTickCount(); if(xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE) { // 记录锁信息 AddMutexRecord(mutex, xTaskGetCurrentTaskHandle(), entryTime); } } void CheckDeadlock(void) { for(所有互斥锁) { if(锁持有时间 > 阈值 && 等待链成环) { TriggerDeadlockRecovery(); } } } ``` --- ### **三、调试分析工具链** #### 1. **SEGGER SystemView** - 配置步骤: 1. 在工程中添加`SEGGER_SYSVIEW_FreeRTOS.c` 2. 初始化代码: ```c SEGGER_SYSVIEW_Conf(); SEGGER_SYSVIEW_Start(); ``` - 实时显示任务切换时序 #### 2. **OpenOCD+GCov** ```sh # 生成代码覆盖率数据 arm-none-eabi-gcov -b -c *.gcda # 生成可视化报告 lcov --capture --directory . --output-file coverage.info genhtml coverage.info --output-directory cov_report ``` --- ### **四、异常恢复策略** #### 1. 分级重启机制 | 故障级别 | 处理方式 | 触发条件 | |----------|--------------------------|--------------------------| | Level1 | 任务重启 | 单任务卡死<3次 | | Level2 | 子系统重启 | 关键任务连续失败 | | Level3 | 全系统热重启 | 看门狗超时 | #### 2. 现场保护设计 ```c __attribute__((section(".backup_ram"))) uint32_t crashCounter; void HardFault_Handler(void) { crashCounter++; SCB->CFSR = SCB->CFSR; // 清除故障状态 __NVIC_SystemReset(); } ``` --- ### **五、推荐监控组合方案** 1. **初级方案**(资源消耗<5%) - IWDG + 任务状态查询 + 栈溢出检测 2. **中级方案**(资源消耗10-15%) - 初级方案 + SystemView + 互斥锁监控 3. **高级方案**(资源消耗20-25%) - 中级方案 + Tracealyzer + GCov覆盖率分析 --- ### **六、典型卡死场景处理** #### 场景1:优先级反转 ```c // 配置互斥锁优先级继承 xSemaphoreCreateMutexStatic(&xHighPriorityMutex); xSemaphoreSetPriority(xHighPriorityMutex, configMAX_SYSCALL_INTERRUPT_PRIORITY - 1); ``` #### 场景2:无限等待 ```c // 带超时的队列接收 if(xQueueReceive(xQueue, &data, pdMS_TO_TICKS(1000)) == errQUEUE_EMPTY) { // 处理超时 } ``` #### 场景3:资源泄漏 ```c // 使用MISRA-C内存检测 #pragma GCC optimize ("-fanalyzer") // 启用静态分析 void* p = pvPortMalloc(100); if(p != NULL) { /* 使用内存 */ vPortFree(p); // 必须显式释放 } ``` --- ### **调试技巧** 1. **断点陷阱**: ```c #define TRAP() __asm volatile ("bkpt #0") if(异常条件) TRAP(); ``` 2. **RTT实时日志**: ```c SEGGER_RTT_WriteString(0, "Enter Critical Section\n"); ``` 3. **PC采样分析**: ```sh # 使用OpenOCD获取PC值 openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \ -c "init" -c "reset halt" -c "flash read_bank 0 pc_dump.bin 0x20000000 0x20000" ``` --- 通过组合使用硬件看门狗、软件监控、调试工具和防御性编程,可有效定位和解决FreeRTOS任务卡死问题。建议根据实际资源情况选择合适方案,并在开发阶段启用所有检测功能,量产时根据需求裁剪。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值