FreeRTOS概念与任务概念

操作系统允许多个任务同时运行,其任务调度器的责任就是:决定某一时刻究竟运行那个任务。
有的OS调度方式:基于时间的任务调度,如Unix。但是,RTOS的任务调度器被设计为可预测的,这是实时系统所必须的,实时就要求os必须对某一个事件做出实时的响应。所以实时系统的任务调度方法是非常重要的,如给任务分配优先级。

  • 实时:硬实时和软实时。
    硬实时:规定时间内必须完成,不允许超时。
    软实时:没有那么严格。

  • 任务
    把功能划分为多个任务,每个任务负责实现其中一部分,每个任务都是一个简单的程序,通常是死循环。

  • RTOS核心内容
    实时内核:可剥夺型内核,内核负责管理任务,决定运行哪个任务,如某个任务告诉任务调度器表明自己准备好,可以被调度执行。

为什么选择freeRTOS:

  1. 开源、免费
  2. 很多半导体厂商的sdk使用freeRTOS
  3. 文件少,用C和汇编来写,学习简单,可读性极强。

一、FreeRTOS 系统配置和风格

FreeRTOS的系统配置文件为:FreeRTOSConfig.h,可以完成FreeRTOS的裁剪和配置。

1.1 变量风格

  1. 非stdint类型的变量使用前缀x,比如基本的Type_t和TickType_t类型,这些类型在移植层定义,定义成符合处理器架构的最高效类型;
  2. size_t类型的变量使用前缀x
  3. 枚举类型变量使用前缀e
  4. 指针类型变量在类型基础上附加前缀p

1.2 函数

  1. 在文件作用域范围的函数前缀为prv
  2. API函数的前缀为它们的返回类型,当返回为空时,前缀为v
  3. API函数名字起始部分为该函数所在的文件名。比如vTaskDelete函数定义在tasks.c,并且该函数返回空。

1.3 宏

  1. INCLUDE_ 开始的宏表示使能FreeRTOS中相应的API函数,FreeRTOS中的裁剪和配置就是通过这种用条件编译的方法来实现的。条件编译的好处就是节省空间,不需要的功能就不用编译,减少占用ROM和RAM大小。
  2. 宏的名字起始部分为该宏定义所在的文件名的一部分。比如configUSE_PREEMPTION定义在FreeRTOSConfig.h文件中。

1.4 misc

  1. 注释:注释单行不超过80列,特殊情况除外。不使用C++风格的双斜线(//)注释,使用 /*.. */
  2. 源码包中,RTOS核心代码位于三个源文件中,分别是tasks.c、queue.c和list.c

1.5 移植FreeRTOS

  1. 将tasks.c、queue.c和list.c这三个内核代码加入工程,将port.c和heap_1.c这两个与处理器相关代码加入工程。
  2. FreeRTOS内核是高度可定制的,使用配置文件FreeRTOSConfig.h进行定制。每个FreeRTOS应用都必须包含这个头文件,用户根据实际应用来裁剪FreeRTOS内核。这个配置文件是针对用户程序的,而非内核,因此配置文件一般放在应用程序目录下,不要放在RTOS内核源码目录下。配置文件具体的参数可以参考:FreeRTOS系列第6篇---FreeRTOS内核配置说明。 如: configUSE_PREEMPTION: 为1时RTOS使用抢占式调度器,为0时RTOS使用协作式调度器(时间片)。

二、 FreeRTOS任务基础知识

FreeRTOS的核心就是任务管理,必须掌握任务的创建、删除、挂起和恢复等操作

2.1 多任务系统

未使用系统时,都是在main中使用while(1)做一个大循环来完成所有的处理,即应用程序是一个无限的循环,循环中调用相应的函数完成所需的处理。中断服务函数作为前台程序,大循环while(1)作为后台程序。

前后台系统

缺点:实时性差,所有任务都是排队等着轮流执行,紧急的任务不能及时执行,并且任务管理不方便。

多任务系统:把一个大问题(应用)“分而治之”,把大问题划分成很多个小问题(小任务),逐步把小问题解决掉。这些小任务是并发处理的。那么哪个任务先执行呢,可以通过任务调度器来完成。FreeRTOS是一个抢占式的实时多任务系统。
从图中可以看出,中断返回后会进行一次任务调度。永远运行的是就绪态优先级最高的任务。

抢占式多任务系统

2.2 FreeRTOS任务与协程

FreeRTOS可以使用任务或协程(Co-Routine),但是任务和协程使用不同的API函数,不能互传数据。

FreeRTOS的一个实时应用可以作为一个独立的任务,每个任务都有自己的运行环境,不依赖于系统中其他的任务。FreeRTOS会重复的开启、关闭每个任务,职责是确保当一个任务开始执行时,上下文环境(寄存器值、堆栈内容)和任务上一次退出时相同。因此,每个任务都有堆栈。

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

协程:为了资源很少的MCU而做的。如今使用较少了。

2.2.1 任务状态

运行态、就绪态(可以运行了)、阻塞态(等待某个外部事件)、挂起态
运行态:当前运行的
就绪态:当前已经准备好,告诉调度器可以被调度运行了
阻塞态:某个任务等待某个事件,必须等待事件发生才能被调度
挂起态:暂时不运行。


2.2.2 任务优先级

每个任务都可以分配从0~(configMAX_PRIORITIES-1)的优先级,数字越大,优先级越高,数字越低表示任务的优先级越低,0的优先级最低。
空闲任务是启动RTOS调度器时由内核自动创建的任务,这样可以确保至少有一个任务在运行。空闲任务具有最低任务优先级。
空闲任务钩子是一个函数,每一个空闲任务周期被调用一次。 通常,使用这个空闲钩子函数设置CPU进入低功耗模式。
FreeRTOS调度器确保:处于就绪态或运行态的高优先级任务获取处理器使用权,即就绪态的最高优先级的任务才会运行。

2.2.3 任务实现

任务实现:即任务的具体工作内容。
FreeRTOS中,可以使用xTaskCreate()或xTaskCreateStatic()来创建任务,参数是pxTaskCode,就是这个任务的任务函数(就是完成本任务工作的函数)
模板:

void vATaskFunction(void *pvParameters)
{
  for(;;){ 
        Specific code to implement the task; //任务应用程序
        vTaskDelay();//用来引起任务调度的函数,这最好要有。
}
/* 不能从任务函数中返回或者退出,如果使用则调用configASSERT(),如果一定要从任务函数中退出的话,一定要调用函数 vTaskDelete(NULL);来删除此任务。*/
  vTaskDelete(NULL);
}

example:
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",                    //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    //创建LED0任务
    xTaskCreate((TaskFunction_t )led0_task,         
                (const char*    )"led0_task",       
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,              
                (UBaseType_t    )LED0_TASK_PRIO,    
                (TaskHandle_t*  )&LED0Task_Handler);   
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler);         
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}

//LED0任务函数 
void led0_task(void *pvParameters)
{
    while(1)
    {
        LED0=~LED0;
        vTaskDelay(500);
    }
}   
//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}
  1. 任务函数的返回值一定是void类型。
  2. 任务函数一般不允许跳出循环,如果想要退出,就是一定要调用vTaskDelete(NULL)删除此任务

2.2.4 任务控制块

描述任务属性的数据结构叫做任务控制块。
FreeRTOS的每个任务都有一些属性需要存储,可以集合到一个结构体来表示。TCB_t。在使用创建任务时自动为任务分配一个任务控制块。
如:
栈顶、列表、任务优先级、任务名字。
PS:很大,如果有条件编译,先不看,没有条件编译的是核心,也是重点理解的。

任务控制块很大,很占内存,一般在创建任务(vTaskCreate())的时候,如果使用的是动态内存申请,那么函数为任务控制块自动调用创建,静态的话需要自己手动填写。

2.2.5 任务堆栈

用来保存任务现场(CPU寄存器的值)。
FreeRTOS能够恢复上一个任务的运行是因为有,任务堆栈。任务调度器切换任务时,将当前任务的现场(CPU寄存器值)保存在此任务堆栈中,等下次任务运行时,用堆栈中保存的值来恢复线程,然后从上次中断的地方开始运行。

创建任务时,需要给任务指定堆栈。xTaskCreate()动态创建时任务堆栈自动创建,xTaskCreateStatic()静态创建任务时,需要程序员自己定义任务堆栈,然后将首地址作为函数的参数(puxStackBuffer)传递给函数。
PS: 要根据任务动态的修改任务堆栈的大小。如任务堆栈分配小了,很容易卡死。

你可能感兴趣的:(FreeRTOS概念与任务概念)