这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 【STM32F103ZET6】13:RTOS-任务的创建与系统函数知识分享

共2条 1/1 1 跳转至

【STM32F103ZET6】13:RTOS-任务的创建与系统函数知识分享

高工
2025-11-06 20:23:00     打赏

一:RTOS中的常用的用户函数说明如下:

1.1  osThreadId 函数说明如下所示:

/// Thread ID identifies the thread (pointer to a thread control block).
/// \note CAN BE CHANGED: \b os_thread_cb is implementation specific in every CMSIS-RTOS.
typedef TaskHandle_t osThreadId;

任务的ID,可以使用到其他函数说明中

1.2 任务的创建

osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument);

如在上一篇帖子中,我们创建LED灯闪烁的任务:

任务创建的原型函数如下所示:

/*********************** Thread Management *****************************/
/**
* @brief  Create a thread and add it to Active Threads and set it to state READY.
* @param  thread_def    thread definition referenced with \ref osThread.
* @param  argument      pointer that is passed to the thread function as start argument.
* @retval thread ID for reference by other functions or NULL in case of error.
* @note   MUST REMAIN UNCHANGED: \b osThreadCreate shall be consistent in every CMSIS-RTOS.
*/
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
{
  TaskHandle_t handle;
  
#if( configSUPPORT_STATIC_ALLOCATION == 1 ) &&  ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
  if((thread_def->buffer != NULL) && (thread_def->controlblock != NULL)) {
    handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
              thread_def->buffer, thread_def->controlblock);
  }
  else {
    if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
              &handle) != pdPASS)  {
      return NULL;
    } 
  }
#elif( configSUPPORT_STATIC_ALLOCATION == 1 )

    handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
              thread_def->buffer, thread_def->controlblock);
#else
  if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
                   thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
                   &handle) != pdPASS)  {
    return NULL;
  }     
#endif
  
  return handle;
}

在上个帖子中,应用如下所示:

首先定义任务的句柄如下:

osThreadId LED0Handle;
osThreadId LED1Handle;
  osThreadDef(LED0, StartTask02, osPriorityIdle, 0, 128);
  LED0Handle = osThreadCreate(osThread(LED0), NULL);

1.3  删除任务:

在STM32cube MX 软件配置的时候,已将删除任务的功能打开,我们只需要直接调用就可以了。

/**
* @brief  Terminate execution of a thread and remove it from Active Threads.
* @param   thread_id   thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
* @retval  status code that indicates the execution status of the function.
* @note   MUST REMAIN UNCHANGED: \b osThreadTerminate shall be consistent in every CMSIS-RTOS.
*/
osStatus osThreadTerminate (osThreadId thread_id)
{
#if (INCLUDE_vTaskDelete == 1)
  vTaskDelete(thread_id);
  return osOK;
#else
  return osErrorOS;
#endif
}

1.4 开启任务调度:

    在程序进入主程序之前,我们需要开启任务调度器,从而使rtos的任务正常的工作起来,在我们添加了LED0和LED1的两个任务到项目中,实际上系统还是处于未工作状态,没有实现任务调度,而且基本的空闲任务也没有实现,系统的时基定时器也没有正常工作。这两个任务都是在开启调度函数中实现的。

    这时候,有的人会疑问?RTOS 中为什么要有空闲任务呢?没有这个空闲任务会有什么影响?

    在RTOS中,一旦开启了任务调度,就必须保证系统中的存在一个任务处于工作状态,而且要求空闲的任务不能被挂起与删除,同事要求空闲任务的优先级是最低的,这样项目中的其他任务都可以打断空闲任务的运行。

/**
* @brief  Start the RTOS Kernel with executing the specified thread.
* @param  thread_def    thread definition referenced with \ref osThread.
* @param  argument      pointer that is passed to the thread function as start argument.
* @retval status code that indicates the execution status of the function
* @note   MUST REMAIN UNCHANGED: \b osKernelStart shall be consistent in every CMSIS-RTOS.
*/
osStatus osKernelStart (void)
{
  vTaskStartScheduler();
  
  return osOK;
}

二:其他的API应用函数:

2.1 延时函数:

延时函数,用于阻塞延时使用,在任务中调用该函数时,任务进入阻塞状态,进入阻塞态的额任务将让出CPU资源。

cube Mx软件初始化时候,会自动使能该函数,用户无需配置。

当然也可以关闭:要想使用该函数必须在 Include parameters 中把 vTaskDelay 选择 Disabled 来失能。

osStatus osDelay (uint32_t millisec)
{
#if INCLUDE_vTaskDelay
  TickType_t ticks = millisec / portTICK_PERIOD_MS;
  
  vTaskDelay(ticks ? ticks : 1);          /* Minimum delay = 1 tick */
  
  return osOK;
#else
  (void) millisec;
  
  return osErrorResource;
#endif
}

2.2 osThreadTerminate

osDelayUntil 基于系统的绝对时间进行延时,这意味着它会等待直到系统时钟达到某个特定的计数值。这种方式通常与 osKernelGetTickCount 函数配合使用,后者用于获取系统时钟的当前计数值。

在多线程环境中,即使设置了延时,也可能因为存在更高优先级的线程或中断服务例程(ISR)的回调处理而导致线程的执行被推迟。因此,计算线程被推迟的确切时间可能相当困难。尽管如此,使用 osDelayUntil 可以确保线程与预期的“匹配值”同步,这使得它成为实现长时间运行的周期性任务的推荐选择。

osStatus osDelayUntil (uint32_t *PreviousWakeTime, uint32_t millisec)
{
#if INCLUDE_vTaskDelayUntil
  TickType_t ticks = (millisec / portTICK_PERIOD_MS);
  vTaskDelayUntil((TickType_t *) PreviousWakeTime, ticks ? ticks : 1);
  
  return osOK;
#else
  (void) millisec;
  (void) PreviousWakeTime;
  
  return osErrorResource;
#endif
}

函数程序如下所示:

void vTask_LED( void * pvParameters ) 
{
    /* 用于保存上次时间。调用后系统自动更新 */ 
    static portTickType PreviousWakeTime; 
    /* 设置延时时间,将时间转为节拍数 */ 
    const portTickType TimeIncrement = pdMS_TO_TICKS(1000); 

    /* 获取当前系统时间 */ 
    PreviousWakeTime = osKernelSysTick();

    while (1)
    {
        /* 调用绝对延时函数,任务时间间隔为 1000 个 tick */ 
        osDelayUntil( &PreviousWakeTime,TimeIncrement );
        // 添加用户任务就看可以了。
    }
}

三:任务挂起与恢复

3.1  任务挂起

函数说明:挂起指定的任务,被挂起的任务不会占用CPU的资源,可以将一个线程挂起,使其进入阻塞状态,直到其他线程使用。

osThreadSuspend
osStatus osThreadSuspend (osThreadId thread_id)
{
#if (INCLUDE_vTaskSuspend == 1)
    vTaskSuspend(thread_id);
  
  return osOK;
#else
  return osErrorResource;
#endif
}

应用函数如下所示:

/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为 NULL。
*/
static void KEY_Task(void* parameter)
{
    while (1) 
    {
        if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13) == 0) 
        {
         osThreadSuspend(LED0Handle);/* 挂起 LED 任务 */ 
        }
        osDelay(50);/* 延时50个 tick */
    }
}

3.2 任务的恢复

函数说明:让挂起的任务重新进入就绪状态,恢复的任务会保留挂起前的状态信息,在恢复的时候根据挂起时的状态继续运行。如果被恢复任务在所有就绪态任务中,处于最高优先级列表的第一位,那么系统将进行任务上下文的切换。

被唤醒的线程会立即运行,前提是它的优先级高于当前运行的线程。

osThreadResume
osStatus osThreadResume (osThreadId thread_id)
{
#if (INCLUDE_vTaskSuspend == 1)  
  if(inHandlerMode())
  {
    if (xTaskResumeFromISR(thread_id) == pdTRUE)
    {
      portYIELD_FROM_ISR(pdTRUE);
    }
  }
  else
  {
    vTaskResume(thread_id);
  }
  return osOK;
#else
  return osErrorResource;
#endif
}

参考挂起的任务,可以实现恢复的调用

 osThreadResume(LED0Handle);/* 恢复 LED0 任务! */

3.3 中断服务函数中恢复任务调度

使用 xTaskResumeFromISR()的时候有几个需要注意的地方:

当函数的返回值为 pdTRUE 时:恢复运行的任务的优先级等于或高于正在运行的任务,表明在中断服务函数退出后必 须进行一次上下文切换 , 使用 portYIELD_FROM_ISR() 进行上下文切换。当函数的返回值为 pdFALSE 时:恢复运行的任务的优先级低于当前正在运行的任务,表明在中断服务函数退出后不需 要进行上下文切换。

xTaskResumeFromISR() 通常被认为是一个危险的函数,因为它的调用并非是固定的,中断可能随时来来临。

所以xTaskResumeFromISR()不能用于任务和中断间的同步,如果中断恰巧在任务被挂起之前到达,这就会导致一次中断丢失(任务还没有挂起,调用 xTaskResumeFromISR()函数是没有意义的,只能等下一次中断)。这种情况下,可以使用信号量或者任务通知来同步就可以避免这种情况。





关键词: STM32F103ZET6     函数介绍    

专家
2025-11-07 10:26:54     打赏
2楼

这个是CMSIS_OS 包了一层皮的结果。



共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]