zoukankan      html  css  js  c++  java
  • FreeRTOS学习笔记——任务基础知识

    1、什么是多任务系统?

    1.1 前后台系统

    前后台系统如图所示:

     图1 前后台系统

    前后台系统实时性差,所有的任务(应用程序)都是排队轮流执行的。不管任务多紧急,没轮到的时候只能等着。

    1.2 多任务系统

    多任务系统是把一个大问题分成多个小问题,逐步把小问题解决了,大问题也就随着解决了,这些小问题可以单独的作为一个小任务来处理。这些小任务是并发处理的,但不是同一时刻一起执行多个任务,而是每个任务执行的时间都很短,导致看起来就像同一时刻执行了很多个任务一样。究竟那个任务先运行,那个任务后运行,完成这个功能的东西在FreeRTOS中叫做任务调度器。FreeRTOS是一个抢占式的实时多任务系统,那么其任务调度器也是抢占式的,运行过程如图所示:

    图2 抢占式多任务系统

    2、FreeRTOS

    2.1 任务的特性

    在使用RTOS的时候一个实时应用可以作为一个独立的任务,每个任务都有自己的运行环境,不依赖其他任务或者RTOS调度器。任何一个时间点都只能有一个任务运行,具体运行哪一个任务由任务调度器来决定,RTOS的调度器因此就会重复的开启、关闭每个任务。RTOS调度器的职责是确保当一个任务开始执行的时候其上下文环境(寄存器值,堆栈内容等)和任务上一次退出的时候保持一致。为了做到这一点,每个任务都必须有个堆栈,当任务切换的时候将上下文环境保存在堆栈中,这样当任务再次执行的时候就可以从堆栈中取出上下文环境,任务恢复运行。

      任务特性:

    • 简单
    • 没有使用限制
    • 支持抢占
    • 支持优先级
    • 每个任务都拥有堆栈导致了RAM使用量增大
    • 如果使用了抢占的话必须考虑重入的问题

    3、任务的状态

    FreeRTOS中的任务永远处于下面几个状态中的某一个:

    • 运行态

    当一个任务正在运行时,那么就说这个任务处于运行态,处于运行态的任务就是当前正在使用处理器的任务。如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处于运行态。

    • 就绪态

    处于就绪态的任务是那些已经准备就绪(这些任务没有被阻塞或者挂起),可以运行的任务,但是处于就绪态的任务还没有运行,因为有一个同优先级或者更高优先级的任务正在运行!

    • 阻塞态

    如果一个任务当前正在等待某个外部事件的话就说它处于阻塞态,比如说如果某个任务调用了函数vTaskDelay()的话就会进入阻塞态,直到延时周期完成。任务在等待队列、信号量、事件组、通知或互斥信号量的时候也会进入阻塞态。任务进入阻塞态会有一个超时时间,当超过这个超时时间任务就会退出阻塞态,即使所等待的事件还没有来临!

    • 挂起态

    像阻塞态一样,任务进入挂起态以后也不能被调度器调用进入运行态,但是进入挂起态的任务没有超时时间。任务进入和退出挂起态通过调用函数vTaskSuspend()和xTaskResume()。

     图3 任务状态间的切换

    4、任务优先级

    每个任务可以分配一个从0~(configMAX_PRIORITIES-1)的优先级,如果所使用的平台支持类似计算前导零这样的指令(可以通过该指令选择下一个要运行的任务,Cortex-M处理器支持该指令),并且宏configUSE_PORT_OPTIMISED_TASK_SELECTION也设置为1,那么宏configMAX_PRIORITIES不能超过32,!其他情况下configMAX_PRIORITIES可以设置为任意值,但是考虑到RAM的消耗,宏configMAX_PRIORITIES最好设置为一个满足应用的最小值。

    优先级数字越小表示任务的优先级越低,0的优先级最低,configMAX_PRIORITIES-1的优先级最高。空闲任务的优先级最低为,0。

    FreeRTOS调度器确保处于就绪态或者运行态的高优先级的任务获取处理器的使用权,换句话说就是出于就绪态的最高优先级的任务才会运行。当宏configUSE_TIME_SLICING定义在文件FreeRTOS.h中已经定义为1.此时出于就绪态的优先级相同的任务就会使用时间片轮转调度器获取运行时间。

    5、任务实现

    (1)、任务函数本质也是函数,所以肯定有任务名什么的,不过这里我们要注意:任务函数的返回类型一定要为void类型,也就是无返回值,而且任务的参数也是void指针类型的!任务函数名可以根据实际情况定义。 

    (2)、任务的具体执行过程是一个大循环,for(;;)就代表一个循环,作用和 while(1)一样。

    (3)、循环里面就是真正的任务代码了,此任务具体要干的活就在这里实现! 

    (4)、FrecRTOS的延时函数,此处不一定要用延时函数,其他只要能让 FrecRTOS 发生任务切换的API函数都可以,比如请求信号量、队列等,甚至直接调用任务调度器。只不过最常用的就是 FrecRTOS的延时函数。

    (5)、任务函数一般不允许跳出循环,如果一定要跳出循环的话在跳出循环以后一定要调用函数vTaskDelete(NULL)删除此任务!

    FrccRTOS的任务函数和UCOS的任务函数模式基本相同的,不止 FrecRTOS,其他RTOS的任务函数基本也是这种方式的。

    6、任务控制块

    FreeRTOS的每个任务都有一些属性需要存储,FrecRTOS把这些属性集合到一起用一个结构体来表示,这个结构体叫做任务控制块:TCB_t,在使用函数xTaskCreate()创建任务的时候就会自动的给每个任务分配一个任务控制块。在老版本的 FreeRTOS中任务控制块叫做tskTCB,新版本重命名为TCB_t,但是本质上还是 tskTCB,本教程后面提到任务控制块的话均用TCB_t表示,此结构体在文件 tasks.c中有定义,如下:

    typedef struct tskTaskControlBlock       /* The old naming convention is used to prevent breaking kernel aware debuggers. */
    {
        volatile StackType_t * pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */
    
        #if ( portUSING_MPU_WRAPPERS == 1 )
            xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
        #endif
    
        ListItem_t xStateListItem;                  /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
        ListItem_t xEventListItem;                  /*< Used to reference a task from an event list. */
        UBaseType_t uxPriority;                     /*< The priority of the task.  0 is the lowest priority. */
        StackType_t * pxStack;                      /*< Points to the start of the stack. */
        char pcTaskName[ configMAX_TASK_NAME_LEN ]; /*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
    
        #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
            StackType_t * pxEndOfStack; /*< Points to the highest valid address for the stack. */
        #endif
    
        #if ( portCRITICAL_NESTING_IN_TCB == 1 )
            UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */
        #endif
    
        #if ( configUSE_TRACE_FACILITY == 1 )
            UBaseType_t uxTCBNumber;  /*< Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */
            UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */
        #endif
    
        #if ( configUSE_MUTEXES == 1 )
            UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */
            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 )
            uint32_t ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */
        #endif
    
        #if ( configUSE_NEWLIB_REENTRANT == 1 )
            /* Allocate a Newlib reent structure that is specific to this task.
             * Note Newlib support has been included by popular demand, but is not
             * used by the FreeRTOS maintainers themselves.  FreeRTOS is not
             * responsible for resulting newlib operation.  User must be familiar with
             * newlib and must provide system-wide implementations of the necessary
             * stubs. Be warned that (at the time of writing) the current newlib design
             * implements a system-wide malloc() that must be provided with locks.
             *
             * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
             * for additional information. */
            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
    
        /* See the comments in FreeRTOS.h with the definition of
         * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
        #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */
            uint8_t ucStaticallyAllocated;                     /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
        #endif
    
        #if ( INCLUDE_xTaskAbortDelay == 1 )
            uint8_t ucDelayAborted;
        #endif
    
        #if ( configUSE_POSIX_ERRNO == 1 )
            int iTaskErrno;
        #endif
    } tskTCB;
    

      

     

    7、任务堆栈

    FrecRTOS之所以能正确的恢复一个任务的运行就是因为有任务堆栈在保驾护航,任务调度器在进行任务切换的时候会将当前任务的现场(CPU寄存器值等)保存在此任务的任务堆栈中,等到此任务下次运行的时候就会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着从上次中断的地方开始运行。

    创建任务的时候需要给任务指定堆栈,如果使用的函数 xTaskCreate()创建任务(动态方法)的话那么任务堆栈就会由函数xTaskCreate()自动创建,后面分析xTaskCreate()的时候会讲解。如果使用函数 xTaskCrcatcStatic()创建任务(静态方法)的话就需要程序员自行定义任务堆栈,然后堆栈首地址作为函数的参数puxStackBuffer 传递给函数,如下:

    TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                                        const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                        const uint32_t ulStackDepth,
                                        void * const pvParameters,
                                        UBaseType_t uxPriority,
                                        StackType_t * const puxStackBuffer,
                                        StaticTask_t * const pxTaskBuffer )
    

      (1)、任务堆栈,需要用户定义,然后将堆栈首地址传递给这个参数。堆栈大小:

     我们不管是使用函数xTaskCrcatc()还是xTaskCrcatcStatic()创建任务都需要指定任务堆栈大小。任务堆栈的数据类型为StackType_t,StackType_t本质上是uint32_t,在portmacro.h中有定义,如下:

    #define portSTACK_TYPE    uint32_t
    #define portBASE_TYPE     long
    
    typedef portSTACK_TYPE   StackType_t;

        typedef long BaseType_t;
        typedef unsigned long UBaseType_t;

      可以看出 StackType_t类型的变量为4个字节,那么任务的实际堆栈大小就应该是我们所定义的4倍

    参考正点原子:《STM32F429FreeRTOS开发手册》

  • 相关阅读:
    Designing With Web Standard(一)
    再听姜育恒
    终于找到Effective C Sharp电子版了
    继续下一个题目
    想做就做,要做得漂亮
    空悲还是空杯
    整理,中庸
    分布式系统设计随想
    日志log4
    log4net更换目录
  • 原文地址:https://www.cnblogs.com/young-dalong/p/15366837.html
Copyright © 2011-2022 走看看