在FreeRTOS开发中,低优先级任务(数据打印任务)被抢占。导致打印任务出现+1延时。

一、问题描述
理想情况:按下一次按键打印一个当前数据
实际情况:第一次按下按键,程序没有打印数据,当我按下第二次按键,才会打印第一次按下的数据。第三次按键,打印第二次数据···

二、具体任务描述
我在程序中定义了两个任务:
(1)按键按下一次,进入按键计数任务(StartButtonTask),按键数目+1,‘按键按下标志位’置1。

void StartButtonTask(void *argument)
{
  /* USER CODE BEGIN StartButtonTask */
  /* Infinite loop */
  for(;;)
  {
    if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(Key_GPIO_Port,Key_Pin))
    {
      osDelay(10);
      if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(Key_GPIO_Port,Key_Pin)){
        buttonCount++;
        buttonPressed = 1;
      }
      while(GPIO_PIN_RESET == HAL_GPIO_ReadPin(Key_GPIO_Port,Key_Pin)){
      osDelay(10);
      /*这里在while中加延时,是不想让时间片在阻塞式等待中浪费,我们只需要每10ms确定按钮是否按下就够了,
      其余时间让操作系统忙别的任务*/
      };
    }
  }

(2)在数据打印任务(StartDataTask)中,查询到‘按键按下标志位’为1,进入按键打印程序,同时清除‘按键按下标志位’。

void StartDataTask(void *argument)
{
  /* USER CODE BEGIN StartDataTask */
  
  /* Infinite loop */
  for(;;)
  {
    osDelay(10);
    if(1 == buttonPressed){

      /*这里一定要注意FreeRTOS抢占式调度机制,
      如果数据处理任务(StartDataTask)的优先级比按键任务(StartButtonTask)低,就会导致数据处理任务一直抢不到cpu,
      直到按键任务进入阻塞态,数据处理任务才能抢到cpu,打印数据,也就是需要按下第n+1次,才能打印第n次结果。*/
			osDelay(1000);//模拟数据处理过程,花费1s。
      
      dataCount ++;//数据计数器加一
			
      /************************************************************************/
      /*打印中文,要先用sprintf将中文打印进字符串
      在嵌入式/RTOS 开发中,用 sprintf通常是为了组合多条信息 + 方便调试/日志系统。
      统一在一个变量里面方便管理*/
      sprintf(msg,"按键:%d 次,数据:%d 次",(int)buttonCount,(int)dataCount);
      printf("%s",msg);
      /*直接打印中文,不推荐,不方便后续的调试/日志系统*/
      // printf("按键:%d 次,数据:%d 次",(int)buttonCount,(int)dataCount);

      /*最直接,打印英文,这个不容易产生因编码问题而导致的报错
      由于不利于日志生成,同样不推荐*/
      //printf("button:%d , data:%d",(int)buttonCount,(int)dataCount);
			/***************************************************************************/
			
			buttonPressed = 0;//打印后标志位清0
			
			
    }
  }
  /* USER CODE END StartDataTask */
}

任务配置结构体:

  osThreadId_t ButtonTaskHandle;
  const osThreadAttr_t ButtonTask_attributes = {
    .name = "ButtonTask",
    .stack_size = 128 * 4,
    .priority = (osPriority_t) osPriorityNormal,
  };
  /* Definitions for DataTask */
  osThreadId_t DataTaskHandle;
  const osThreadAttr_t DataTask_attributes = {
    .name = "DataTask",
    .stack_size = 128 * 4,
    .priority = (osPriority_t) osPriorityLow,
  };

三、问题分析
由于配置时,按键任务(ButtonTask)优先级比数据打印任务(DataTask)优先级高,导致进入任务进入阻塞态osDelay(1000)后,按键任务结束了延时,于是一直抢占cpu资源。直到第二次按键按下,按键任务进入阻塞态osDelay(10),此时数据打印任务才有机会执行osDelay(1000)后的打印程序

sprintf(msg,"按键:%d 次,数据:%d 次",(int)buttonCount,(int)dataCount);
printf("%s",msg);

四、问题解决
(1)修改任务优先级:

osThreadId_t ButtonTaskHandle;
const osThreadAttr_t ButtonTask_attributes = {
  .name = "ButtonTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for DataTask */
osThreadId_t DataTaskHandle;
const osThreadAttr_t DataTask_attributes = {
  .name = "DataTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

(2)在按键任务加入延时调用

void StartButtonTask(void *argument)
{
  /* USER CODE BEGIN StartButtonTask */
  /* Infinite loop */
  for(;;)
  {
    osDelay(10);
    if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(Key_GPIO_Port,Key_Pin))
    {
      osDelay(10);
      if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(Key_GPIO_Port,Key_Pin)){
        buttonCount++;
        buttonPressed = 1;
      }
      while(GPIO_PIN_RESET == HAL_GPIO_ReadPin(Key_GPIO_Port,Key_Pin)){
      osDelay(10);
      /*这里在while中加延时,是不想让时间片在阻塞式等待中浪费,我们只需要每10ms确定按钮是否按下就够了,
      其余时间让操作系统忙别的任务*/
      };
    }
  }
posted @ 2026-02-24 21:24  alanala  阅读(0)  评论(0)    收藏  举报