FreeRTOS-00-说明+基础知识

1 说明

本文仅作为学习FreeRTOS的记录文档,作为初学者肯定很多理解不对甚至错误的地方,望网友指正。

1.1 简介

FreeRTOS是一个RTOS(实时操作系统)系统,支持抢占式、合作式和时间片调度。适用于微处理器或小型微处理器的实时应用。

本文档使用的FreeRTOS版本:FreeRTOS Kernel V10.4.1

参考文档:《FreeRTOS_Reference_Manual_V10.0.0.pdf》、《FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf》、《STM32F4 FreeRTOS开发手册_V1.1.pdf》

参考视频:正点原子FreeRTOS手把手教学-基于STM32_哔哩哔哩_bilibili

1.2 测试工程说明

测试环境为在Linux进行仿真跑FreeRTOS,关于如何实现在Linux下的FreeRTOS仿真说明如下:

网上在linux下跑FreeRTOS的教程很多,这里我直接check了一份别人已经创建了的代码。

代码路径:freertos-simulator: FreeRTOS simulator on linux platform (gitee.com)

或者去官网,按照官网说明创建也可以。

官网:FreeRTOS simulator for Posix/Linux

编译之前需要先安装pcap开发包,不同的系统安装方式也不同,官网也给出了安装方式。

To install on ubuntu run

$ sudo apt-get install libpcap-dev

To install on rpm based system run

$ sudo yum install libpcap-devel

or

$ sudo dnf install libpcap-devel

To install on MacOS run

$ brew install libpcap

编译方式:

进入simulator目录,直接执行make即可。

运行方式:

$ ./build/freertos-simulator 

image-20210609091429229

2 FreeRTOS任务基础知识

主要介绍任务管理。从以下几个方面进行介绍:多任务系统、任务状态、任务优先级、任务实现、任务控制块、任务堆栈。

2.1 多任务系统

FreeRTOS是一个抢占式的实时多任务系统。高优先级的任务可以打断低优先级的任务而取得CPU的使用权。

image-20210704172819679

2.2 任务的特性

任务是独立的,每个任务都有自己的运行环境,不依赖于系统中的其他任务或者RTOS调度器。任何一个时间点只能有一个任务运行,具体运行哪个任务由调度器决定。RTOS调度器的职责是确保当一个任务开始执行的时候其上下文环境(寄存器、堆栈内容等)和任务上一次退出的时候相同。所以每个任务都有堆栈,当任务切换的时候将上下文环境存放在堆栈中,这样当任务再次执行的时候可以从堆栈中取出上下文环境,任务恢复运行。

任务有以下特性:简单、没有使用限制、支持抢占、支持优先级、任务必须有堆栈、使用抢占必须考虑重入的问题。

2.3 任务状态

FreeRTOS中的任务永远处在下面几种状态中的某一种。

1)运行态:当一个任务运行时,则该任务处于运行态,处于运行态的任务就是当前使用处理器的任务。单核处理器在任何时刻只有一个任务处于运行态。

2)就绪态:任务已经准备就绪可以运行,但还没有运行,因为有一个同优先级或者更高优先级的任务正在运行。

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

4)挂起态:任务进入挂起态后不能被调度器调用进入运行态,但是进入挂起态的任务没有超时时间。

任务状态之间的转换如下图所示:

image-20210717155218266

2.4 任务优先级

每个任务都可以分配一个从0~configMAX_PRIORITIES-1(定义在FreeRTOSConfig.h)的优先级。优先级数字越低表示任务的优先级越低,0的优先级最低,configMAX_PRIORITIES-1的优先级最高。空闲任务的优先级最低,为0。

FreeRTOS调度器确保处于就绪态或运行态最高优先级的任务获取处理器使用权。当宏configUSE_TIME_SLICING(默认在FreeRTOS.h中定义)定义为1的时候多个任务可以共用一个优先级,数量不限。此时处于就绪态的优先级相同的任务就会使用时间片轮转调度器获取运行时间。

2.5 任务控制块

FreeRTOS的每个任务都有一些属性需要存储,这些属性集合在一个结构体来表示,这个结构体就叫任务控制块:TCB_t,这个结构体定义在task.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;

2.6 任务堆栈

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

创建任务的时候需要给任务指定堆栈,如果使用的函数 xTaskCreate()创建任务(动态方法)的话那么任务堆栈就会由函数 xTaskCreate()自动创建,后面分析 xTaskCreate()的时候会讲解 。

堆栈大小:任务堆栈的数据类型为 StackType_t, StackType_t 本质上是 unsigned long,在 portmacro.h 中有定义。

#define portSTACK_TYPE	unsigned long
typedef portSTACK_TYPE StackType_t;

可以看出 StackType_t 类型的变量为 4 个字节,那么任务的实际堆栈大小就应该是我们所定义的 4 倍(32位处理器)或8倍(64位处理器) 。

posted @ 2021-07-17 17:58  zhengcixi  阅读(641)  评论(0编辑  收藏  举报
回到顶部