【FreeRTOS】第二章FreeRTOS任务基础知识

目录

一、单任务和多任务系统

二、FreeRTOS 任务状态

三、任务优先级和调度方式     

四、FreeRTOS 任务控制块

五、FreeRTOS 任务栈


一、单任务和多任务系统

1.单任务系统的编程方式,即裸机的编程方式,这种编程方式的框架一般都是在 main()函数中使用一个大循环,在循环中顺序地调用相应的函数以处理相应的事务。
2.多任务系统在处理事务的实时性上比单任务系统要好得多,从宏观上来看,多任务系统的多个任务是可以“同时” 运行的,因此紧急的事务就可以无需等待 CPU 处理完其他事务,在被处理。

二、FreeRTOS 任务状态

        FreeRTOS 中任务存在四种任务状态,分别为运行态、就绪态、阻塞态和挂起态。 FreeRTOS运行时,任务的状态一定是这四种状态中的一种。

        1. 运行态:如果一个任务得到 CPU 的使用权,即任务被实际执行时,那么这个任务处于运行态。如果运行 RTOS 的 MCU 只有一个处理器核心,那么在任务时刻,都只能有一个任务处理运行态。

        2. 就绪态:如果一个任务已经能够被执行(不处于阻塞态后挂起态),但当前还未被执行(具有相同优先级或更高优先级的任务正持有 CPU 使用权),那么这个任务就处于就绪态。
        3. 阻塞态:如果一个任务因延时一段时间或等待外部事件发生,那么这个任务就处理阻塞态。例如任务调用了函数 vTaskDelay(),进行一段时间的延时,那么在延时超时之前,这个任务就处理阻塞态。任务也可以处于阻塞态以等待队列、信号量、事件组、通知或信号量等外部事件。通常情况下,处于阻塞态的任务都有一个阻塞的超时时间,在任务阻塞达到或超过这个超时时间后,即使任务等待的外部事件还没有发生,任务的阻塞态也会被解除。要注意的是,处于阻塞态的任务是无法被运行的。

        4. 挂起态:任务一般通过函数 vTaskSuspend()和函数 vTaskResums()进入和退出挂起态与阻塞态一样,处于挂起态的任务也无法被运行。

三、任务优先级和调度方式     

        任务优先级是决定任务调度器如何分配 CPU 使用权的因素之一。 每一个任务都被分配一个0~(configMAX_PRIORITIES-1)的任务优先级,宏 configMAX_PRIORITIES 在FreeRTOSConfig.h文件中定义。

        1.抢占式调度:抢占式调度主要时针对优先级不同的任务,每个任务都有一个优先级,优先级高的任务可以抢占优先级低的任务,只有当优先级高的任务发生阻塞或者被挂起,低优先级的任务才可以运行。

        2.时间片调度:时间片调度主要针对优先级相同的任务,当多个任务的优先级相同时, 任务调度器会在每一次系统时钟节拍到的时候切换任务,也就是说 CPU 轮流运行优先级相同的任务,每个任务运行的时间就是一个系统时钟节拍。 有关系统时钟节拍的相关内容,在下文讲解 FreeRTOS 系统时钟节拍的时候会具体分析。

四、FreeRTOS 任务控制块

        FreeRTOS 中的每一个已创建任务都包含一个任务控制块,任务控制块是一个结构体变量,FreeRTOS 用任务控制块结构体存储任务的属性。

        任务控制块的定义如以下代码所示:

typedef struct tskTaskControlBlock
{
  /* 指向任务栈栈顶的指针 */
  volatile StackType_t *pxTopOfStack;
#if (portUSING_MPU_WRAPPERS == 1)
  /* MPU 相关设置 */
  xMPU_SETTINGS xMPUSettings;
#endif
  /* 任务状态列表项 */
  ListItem_t xStateListItem;
  /* 任务等待事件列表项 */
  ListItem_t xEventListItem;
  /* 任务的任务优先级 */
  UBaseType_t uxPriority;
  /* 任务栈的起始地址 */
  StackType_t *pxStack;
  /* 任务的任务名 */
  char pcTaskName[configMAX_TASK_NAME_LEN];
#if ((portSTACK_GROWTH > 0) || (configRECORD_STACK_HIGH_ADDRESS == 1))
  /* 指向任务栈栈底的指针 */
  StackType_t *pxEndOfStack;
#endif
#if (portCRITICAL_NESTING_IN_TCB == 1)
  /* 记录任务独自的临界区嵌套次数 */
  UBaseType_t uxCriticalNesting;
#endif
#if (configUSE_TRACE_FACILITY == 1)
  /* 由系统分配(每创建一个任务,值增加一),分配任务的值都不同,用于调试 */
  UBaseType_t uxTCBNumber;
  /* 由函数 vTaskSetTaskNumber()设置,用于调试 */
  UBaseType_t uxTaskNumber;
#endif
#if (configUSE_MUTEXES == 1)
  /* 保存任务原始优先级,用于互斥信号量的优先级翻转 */
  UBaseType_t uxBasePriority;
  /* 记录任务获取的互斥信号量数量 */
  UBaseType_t uxMutexesHeld;
#endif
#if (configUSE_APPLICATION_TASK_TAG == 1)
  /* 用户可自定义任务的钩子函数用于调试 */
  TaskHookFunction_t pxTaskTag;
#endif
#if (configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0)
  /* 保存任务独有的数据 */
  void *pvThreadLocalStoragePointers[configNUM_THREAD_LOCAL_STORAGE_POINTERS];
#endif
#if (configGENERATE_RUN_TIME_STATS == 1)
  /* 记录任务处于运行态的时间 */
  configRUN_TIME_COUNTER_TYPE ulRunTimeCounter;
#endif
#if (configUSE_NEWLIB_REENTRANT == 1)
  /* 用于 Newlib */
  struct _reent xNewLib_reent;
#endif
#if (configUSE_TASK_NOTIFICATIONS == 1)
  /* 任务通知值 */
  volatile uint32_t ulNotifiedValue[configTASK_NOTIFICATION_ARRAY_ENTRIES];
  /* 任务通知状态 */
  volatile uint8_t ucNotifyState[configTASK_NOTIFICATION_ARRAY_ENTRIES];
#endif
#if (tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0)
  /* 任务静态创建标志 */
  uint8_t ucStaticallyAllocated;
#endif
#if (INCLUDE_xTaskAbortDelay == 1)
  /* 任务被中断延时标志 */
  uint8_t ucDelayAborted;
#endif
#if (configUSE_POSIX_ERRNO == 1)
  /* 用于 POSIX */
  int iTaskErrno;
#endif
} tskTCB;
typedef struct tskTaskControlBlock *TaskHandle_t;

        从上面的代码可以看出, FreeRTOS 的任务控制块结构体中包含了很多成员变量,但是,大部分的成员变量都是可以通过 FreeRTOSConfig.h 配置文件中的配置项宏定义进行裁剪的。

五、FreeRTOS 任务栈

        1.静态创建任务:当使用静态方式创建任务时,需要用户自行分配一块内存,作为任务的栈空间。 静态方式创建任务的函数原型如下所示:

TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,
                               const char *const pcName,
                               const uint32_t ulStackDepth,    //为任务栈的大小
                               void *const pvParameters,
                               UBaseType_t uxPriority,
                               StackType_t *const puxStackBuffer, //任务的栈的内存空间
                               StaticTask_t *const pxTaskBuffer)

        2.动态创建任务 :使用动态方式创建任务时,系统则会自动从系统堆中分配一块内存,作为任务的栈空间,动态方式创建任务的函数原型如下所示:

BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,
                       const char *const pcName,
                       const configSTACK_DEPTH_TYPE usStackDepth, //任务栈的大小
                       void *const pvParameters,
                       UBaseType_t uxPriority,
                       TaskHandle_t *const pxCreatedTask)

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Gui林

你的热爱是我更新的动力!

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

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

打赏作者

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

抵扣说明:

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

余额充值