• FreeRTOS 任务与调度器(1)


     前言:

    Task.c和Task.h文件内是FreeRTOS的核心内容,所有任务和调度器相关的API函数都在这个文件中,它包括下图这些内容FreeRTOS文件如下:

    Task.c和Task.h文件内是FreeRTOS的核心内容,所有任务和调度器相关的API函数都在这个文件中,它包括下图这些内容

    在开始介绍函数之前,首先我们先简单了解一下任务状态:

    • FreeRTOS的任务5种状态:

    1. 运行状态:当前正在执行的任务的状态,只可能会一个当前正在执行的任务
    2. 就绪状态:随时可以运行的任务的状态,就绪状态任务随时等待调度器调度
    3. 阻塞状态:任务因为某些原因暂时不能被调度状态,一般情况下正在等待某些事件的发生比如调用了xTaskDelay()在一段时间内任务会被阻塞,在这些事件达成后任务会自动回到就绪状态。
    4. 挂起状态:vTaskSuspend()函数会让任务进入挂起状态,这时候这个任务不会执行。调用xTaskResume()函数才能让这些任务回到就绪状态
    5. 删除状态:一个任务被使用vTaskDelete()函数后被删除,处于删除状态。


    他们之间的状态切换如下示意图:

    这本篇中,主要介绍一下这6个部分:

     

     

    一、创建任务:

    • 顾名思义,这些函数的作用是创建一个任务,创建的任务会进入就绪状态,如果没其他更高优先级的任务运行,则马上进入运行状态
    • 这些函数可以在调度器启动前或启动后调用

    1.1、vTaskCreate()

    1.1.1、函数简介

    几个比较重要的输入参数介绍一下:

    • pvTaskCode:直接指向函数的本体的指针,可以把任务函数名字直接贴过来
    • usStackDepth:任务内申请的局部变量会使用到任务的堆栈空间,(在32位系统中,这个参数的单位是word=4byte),例如这个参数设置为100,那么这个任务将会申请到400byte的空间。
    • uxPriority:任务优先级,使用这个参数来设置任务优先级(0是最低优先级),在FreeRTOSConfig.h 中调整configMAX_PRIORITIES的定义可以设置最高可用的优先级(最高可设置优先级为configMAX_PRIORITIES-1)。高优先级的任务可以抢断低优先级的任务,(主要:记得高优先级的任务不需要用的时候将其阻塞或挂起或删除,否则低优先级的任务可能永远无法得到运行权)
    • pxCreatedTask:句柄的地址,以后使用其他API功能来索引这个任务时会需要用到(注意:这里传入的是句柄的地址!)

    1.1.2、使用简介:

    以下是官方例子:

    1.2、vTaskCreateStatic()

    1.2.1、函数简介

    为了方便我们自己管理内存,有了静态创建任务法,任务堆栈的创建和回收都要由编程者来处理,与vTaskCreate()对比,我们可以发现以下不同之处:

    • puxStackBuffer参数:任务需要用到的堆栈数组的地址,我们只需要创建一StackType_t类型个空的数组,然后把数组指针传进来就好了(注意数组的大小要大于ulStackDepth)
    • pxTaskBuffer参数:存放任务数据结构(TCB)的变量,同样的,我们创建一个StaticTask_t类型的变量,然后把他的指针传进来
    • 还有一处不同,输入参数的句柄取消掉了,但是句柄还是存在的,只是变为了返回参数

    1.2.2、使用简介
    官方例程如下:

    1. 创建一个StaticTask_t 类型的参数,稍后用于存放任务数据结构(TCB)
    2. 创建一个StackType_t类型数组,稍后用于作为任务堆栈
    3. 创建一个句柄,稍后用作vTaskCode任务的句柄
    4. 使用xTaskCreateStatic()创建任务
    5. 使用vTaskSuspend()、并通过传入句柄挂起刚刚创建的任务,目的是展示给我们看这个任务的句柄是可用的

    二、删除任务:

    2.1、参数简介

    2.2、使用简介

    下面是官方例子:

    1. 在当前任务中,用xTaskCreate()创建另一个任务B
    2. 如果任务B创建成功,使用vTaskDelete(任务句柄)删除掉任务B。
    3. 用vTaskDelete(NULL)删除掉当前任务,目的是展示给我们看通过传入NULL可以删除当前任务

     

     

    三、延时函数:

    3.1、vTaskDelay()

    3.1.1、函数简介

    xTaskDelay()

    • 让调用这个函数的任务在一定时间内进入阻塞状态,时间到达后会切换回来这个任务。
    • 如果输入参数为0,那么这个任务不会阻塞,但是会切换

    *这个函数只有一个输入参数,但需要注意一下它是以tick时钟的中断次数为单位的(并不是以毫秒为单位):

    3.1.2、使用简介

    下面是官方的例子
    其中两处vTaskDelay()

    1. 延时20个tick时间片
    2. 延时20ms。(pdMS_TOTICKS()可以把ms时间换成tick为单位)

     3.2、vTaskDelayUntil()

    3.2.1、函数简介

    • 让任务进入阻塞状态等待实际那到达,是精确的绝对时间
    • 周期性任务能够使用vTaskDelayUntil()来达到连续的执行频率

    3.2.2、使用简介

    以下是官方的例子:

    1. 创建一个TickType_t类型的变量,用于记录上一次系统时间
    2. 用pdMS_TO_TICKS()函数把50ms转换为tick为单位,方便等下给vTaskDelayUntil调用
    3. 初始化第一步中的变量,在这一步后,这个变量不用再手动更新(vTaskDelayUntil()会更新它)
    4. 使用vTaskDelayUntil()、传入刚刚的参数,制造50ms固定时间的循环

    3.3、重要对比

    vTaskDelay()和vTaskDelayUntil()的不同之处
    我们可以直接翻译一下官方手册的描述:

    举个例子:
    以下两个任务分别用vTaskDelay()和vTaskDelayUntil()来实现延时功能:
    思考一个问题: 任务A 和任务B都能实现LED闪烁,那么A 和 B任务的LED端口多少毫秒翻转一次 ?
    任务A:

    任务B:

    • 任务A中,LED端口15毫秒翻转一次
    • 任务B中,LED端口10毫秒翻转一次

    *注意:Delay_MS()是一个自定义的函数,用来模拟任务中处理其他东西浪费了5ms。
    两个任务都是 TaskDelay(10毫秒) ,但是任务A中使用vTaskDelay(),在任务B中使用vTaskDelayUntil()。
    在任务A中:vTaskDelay()是从调用的那一刻开始算,那么这个任务本身在Delay_MS中占用了5MS,LED翻转的时间忽略不计,那么加上vTaskDelay()的10MS,就是15MS。
    在任务B中:vTaskDelayUntil()和任务本身执行时间无关,只要任务每次循环执行的总时间少于10ms,那么这个任务就是10ms执行一次了。

    最后提一下xTaskAbortDelay()这个函数,根据描述,他能让正在阻塞状态等待延时的函数马上切出,进入就绪状态。但由于我的库版本比较旧,没有这个函数,所以就不作更多的介绍了。

    四、开启调度器

    4.1、函数简介:

    这个函数作用是开启调度器,调用这个函数后任务就会开始执行。所以在整个程序中只需要调用一次,一般在main函数中调用就可以了。开启成功的话,系统由调度器接管了,main函数中vTaskStartScheduler()后面的代码都不会被执行。

    4.2、使用简介:

    官方的例子:

    1. 创建任务
    2. 开启调度器,开启后程序会跳转到vATask()任务中

    五、任务的挂起和恢复

    5.1、vTaskSuspend() 和 vTaskResume()

    5.1.1、函数简介:

    挂起/解除挂起单个任务:

    • vTaskSuspend的函数是让指定的任务进入挂起状态
    • xTaskResume的函数是让指定的任务从挂起状态换为就绪状态
    • xTaskResumeFromISR()是xTaskResume()适合在中断中调用的版本


    5.1.2、使用简介
    使用很简单,当不需要用某个任务的时候用vTaskResume(句柄) 把那个任务挂起,需要用的时候再打开就行了,下面是官方的例程,实现了这三步:

    1. 使用xTaskCreate()创建任务
    2. 创建成功的话使用vTaskSuspend()把刚刚创建的任务转换为挂起状态(该任务将不会再得到执行)
    3. 使用vTaskResume()让刚刚挂起的任务转为就绪状态

    5.2、vTaskSuspendAll()和vTaskResumeAll()

    5.2.1、函数简介:

    vTaskSuspendAll()挂起调度器 对应 xTaskResumeAll()解除挂起调度器:
    • vTaskSuspendAll()挂起调度器后,只有当前任务在继续执行,不会发生任务切换了。
    • xTaskResumeAll()对应vTaskSuspendAll()恢复调度器。
    这个函数的作用之一在于,可以保证一些不能被分的程序执行,因为挂起调度器保证了不会被高优先级的任务强调(注意调度器挂起后中断还是可以运行的,如果要保证时效,还得把中断关闭)

    注意:vTaskSuspendAll()是可以递归调用的,这意味着调用了多少次vTaskSuspendAll(),就必须有多少此vTaskResumeAll()的调用才能让调度器恢复。这个情况以下的例子中很好地体现了。


    5.2.2、使用简介

    1. 在任务vTask1中第一此调用vTaskSuspendAll(),此时调度器被挂起,不会发生任务切换
    2. 调用另一个用作例子的vDemoFunction()
    3. 第二次调用vTaskSuspendAll(),此时调度器再次被挂起,而且挂起计数增加到2
    4. 第一次调用vTaskResumeAll(),此时调度器挂起计数减少为1,但是调度器仍然处于挂起状态
    5. 第二次调用vTaskResumeAll(),调度器计数为0,调度器恢复运行,后面会发生任务切换了

     

    六、任务切换

    6.1、函数简介

    • 在一个运行的任务中调用taskYIELD(),那么这个任务会被降级为就绪状态,调度器会选择另一个相同优先级的就绪任务执行。(如果没有相同优先级的任务就绪,那么这个任务将不会切换,会继续执行。

     

    6.2、使用简介

    我们来看官方例子:

    1. 在调用taskYIELD()后,vATask这个任务会马上"让步",进入就绪状态等待,等待下次得到调度器调度的时候,会执行taskYIELD()下面的代码

     在下一节中,我们会继续介绍task中的通知和其他内容

  • 相关阅读:
    easy ui 表单ajax和from两种提交数据方法
    easy ui 下拉级联效果 ,下拉框绑定数据select控件
    easy ui 下拉框绑定数据select控件
    easy ui 异步上传文件,跨域
    easy ui 菜单和按钮(Menu and Button)
    HTTP 错误 404.3
    EXTJS4.2 后台管理菜单栏
    HTML 背景图片自适应
    easy ui 表单元素input控件后面加说明(红色)
    EXTJS 4.2 添加滚动条
  • 原文地址:https://www.cnblogs.com/HongYi-Liang/p/9901242.html
Copyright © 2020-2023  润新知