FreeRTOS基础

1. FreeRTOS特性

具有抢占式或者协作式任务调度的开源实时操作系统内核

功能可裁剪,资源占用小;

灵活的任务优先级分配;

有互斥锁、信号量、消息队列等功能;

具有低功耗模式;

支持中断嵌套;

运行过程可追踪;

2. 任务管理

2.1 任务的含义

在FreeRTOS中,线程(Thread)和任务的概念是相同的,每个任务就是一个线程,有着自己的一个程序。任务的函数模型示例如下所示,通常情况下包含一个永远不会退出的循环体。这个任务函数不能有返回值(即使用return语句),不然会导致异常。如果不需要这个任务的话,必须要用语句显示地删除这个任务(比如调用vTaskDelete()这个函数)。每一个创建的任务有自己的栈区以及优先级。

void TaskFunction(void *pvParameters)
{ 
  for(;;)
  {
  }
  vTaskDelete(NULL);  
}

2.2 任务的状态

FreeRTOS的任务有4种状态,分别是就绪态、运行态、阻塞态和挂起态。

就绪态:任务准备就绪,可以运行但当前未被调度器选中执行的状态;

运行态:任务被内核调度执行的时候处于此状态;

阻塞态:任务等待某个事件或信号的时候处于此状态;

挂起态:任务被vTaskSuspend()挂起禁止运行的时候处于此状态。

2.3 任务的创建

FreeRTOS中任务由xTaskCreat()这个函数创建,函数原型如下:

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 )
{...}

其中,pxTaskCode是一个函数指针,指向执行任务的函数;

pcName是任务的描述名称,方便调试,不需要可以设为Null;

usStackDepth表示任务的栈空间大小,单位为word,需要根据任务占用需求设置栈空间的大小;

pvParameters用于传递给任务的参数,不需要可以设为Null;

uxPriority表示任务的优先级,范围由0到(configMAX_PRIORITIES -1),数值越大,等级越高;

pxCreatedTask为任务的句柄,通过句柄可以对任务进行设置,不用可以设为Null。

函数的返回值有pdPass和pdFail两个,pdPass表示创建成功;pdFail表示创建失败。

2.4 任务的调度

调度器(scheduler)是一段决定哪个任务应该执行的算法代码。

任务创建完成后,通过vTaskStartSchedule()函数开启调度器。

FreeRTOS中基于时间片对任务进行调度,时间片即把一段时间等分成很多个时间段,调度器会在每个时间片结束的时候通过周期嘀嗒定时器中断(tick interrupt)执行一次,调度器根据用户设置的是抢占式还是协作式模式选择哪个任务在下一个时间片会运行,即执行任务切换。也可调用API函数如taskYIELD()执行任务切换。上述两种任务切换内在都是通过置PendsV中断标志位触发一次PendsV中断来实现的。

时间片大小通过configTICK_RATE_HZ设置,如configTICK_RATE_HZ为1000,则时间片为1ms;

configUSE_PREEMPTION为1时执行抢占式任务调度,为0时执行协作时任务调度。在抢占式模式下,在每次调度器运行时,高优先级的任务会被切换优先执行,当前处于运行状态的低优先级的任务则会立刻进入就绪状态等待运行;在协作式模式下,高优先级任务不会抢占当前正在运行状态的低优先级任务。

configUSE_PREEMPTION为1且configUSE_TIME_SLICING为1时,调度器会让相同优先级的任务轮流占用时间片进行运行,即当多个任务具有相同的优先级时,每个任务只运行一个时间片,然后让出CPU使用权,让另一个任务运行,从而实现相同优先级任务的调度。

总结一下:

①configUSE_PREEMPTION为1且configUSE_TIME_SLICING为1时,抢占式时间片调度,每个时间片结束时,调度器从处于就绪状态的所有任务中选择优先级最高的在下一个时间片执行,优先级相同的任务则交替执行。

②configUSE_PREEMPTION为1且configUSE_TIME_SLICING为0时,抢占式无时间片调度,每个时间片结束时,调度器从处于就绪状态的的所有任务中选择优先级最高的在下一个时间片执行,如果两个任务优先级相同,则当前运行的任务会一直运行,直到它进入阻塞或者挂起状态,另一个相同优先级的任务才会运行。

③configUSE_PREEMPTION为0,configUSE_TIME_SLICING为任意值时,协作式任务调度,这种调度模式下,当前执行任务会一直执行,高优先级任务不会抢占低优先级任务,当前任务进入阻塞状态时,调度器从处于就绪状态的任务中选择优先级最高的进行执行。

2.5 空闲任务

空闲任务是调用vTaskStartScheduler()时自动创建的一个任务。

空闲任务具有最低的优先级0,当用户创建的任务都在阻塞状态或挂起状态时,空闲任务便得以执行。最低的优先级确保空闲任务不会抢占用户任务,同时空闲任务可以清理内核的资源。空闲任务的运行时间可以反映系统的可用计算资源,可以用于计算CPUload。

空闲任务可以绑定一个钩子函数(Task Hook),当空闲任务运行时钩子函数也会被调用,钩子函的数原型是vApplicationIdleHook( void )。钩子函数里可以添加测量空闲任务运行时间的代码或者把系统放入低功耗模式的代码。

3. 内存管理

首先需要理解堆和栈的不同之处。

堆(heap):一般由程序员分配和释放;

栈(stack):由编译器自动分配和释放,如存放函数的参数值,局部变量的值等。

FreeRTOS预制五种动态内存管理算法,分别通过heap_1、heap_2、heap_3、heap_4和heap_5实现,用户可以根据自己的需求选择这几种算法中的一种或者可以定制实现方法。

上述五种动态内存管理算法,除heap_3以外,均通过堆ucHeap[ configTOTAL_HEAP_SIZE ]来管理内存,堆的大小由configTOTAL_HEAP_SIZE设定。其中,heap_4适用于通用的应用,用户多选择此算法。

当用户调用API函数xTaskCreate()创建任务时,FreeRTOS会在堆中开辟出一块空间,分别用于存放任务控制信息的TCB块和存放任务相关参数值、局部变量值的Stack栈区

4. 资源管理

资源可体现为一个个用于存储数据的全局变量,一个个队列上的数据等需要在任务之间共享的数据,或者对IO口的操作资源等。

资源管理主要包括数据同步资源保护

4.1 数据同步与信号量

数据同步旨在通知任务新数据的到来并提高CPU资源利用率。举例来说,有两个任务,一个是数据的产生者(服务端),一个是数据的使用者(客户端),客户端任务等待服务端生产的数据进行处理,服务端任务和客户端任务轮流执行,如果客户端任务执行时数据还没有产生,那么客户端任务的执行就是无意义的,白白浪费CPU的资源。

FreeRTO中引入信号量(Semaphore),通过信号量的同步机制使客户端任务在数据还未产生的时候进入阻塞状态,进而可以让出CPU资源供其他任务使用。

FreeRTOS中信号量分为二值信号量(binary semaphore)和计数信号量(counting semaphore)。

对信号量的操作主要包括创建信号量(creat a semaphore)、获取信号量(taking a semaphore)和释放信号量(giving a semaphore)。

二值信号量中信号量的数目最大为1,相当于只有一个队列项的队列,二值信号量只关心这个特殊队列的状态,即要么为空,要么为满,最多只能通知解锁一个任务;计数信号量中信号量的数目可以为多个,能够通知解锁多个任务,接收到信号量的任务则根据情况可以进入就绪状态。

4.2 信号量常用API

二值信号量创建

vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )

SemaphoreHandle_t xSemaphoreCreateBinary( void )

计数信号量创建

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )

其中,uxMaxCount表示计数信号量的信号量最大值,uxInitialCount表示计数信号量的信号量初始值。

信号量获取

xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xBlockTime)

其中,xSemaphore表示信号量的句柄,xBlockTime表示如果信号量不可用的话任务处于阻塞状态的最长时间,设置为portMAX_DELAY时任务会一直处于阻塞状态指导信号量可用,设置为0时如果信号量不可用会直接返回。

信号量释放

xSemaphoreGive( SemaphoreHandle_t xSemaphore )

4.3 资源保护与互斥量

资源保护是指保护资源不被多个任务同时操作。例如一个任务在使用串口发送数据的过程中,被高优先级的任务抢占执行使用串口发送数据,那么最终实际串口发送出去的数据将是无意义的乱码。又例如不同任务操作同一个全局变量,可能会产生意外的效果。

上述信号量除了可以同步数据,在一定程度上也可以起到资源保护的作用。但使用信号量保护资源可能会产生优先级倒置的负面效果。

如有3个不同优先级的任务Task_L/Task_M/Task_H,低优先级任务Task_L和高优先级任务Task_H通过信号量共享资源。当Task_L占用资源,锁定了信号量,Task_H运行后获取信号量失败进入阻塞状态,但在Task_H等待Task_L释放信号量的过程中,中等优先级任务Task_M抢占了Task_L,进而延迟了信号量的释放时间,导致Task_H阻塞更长时间。这种现象称为优先级倒置。

为了实现资源保护,FreeRTOS引入了互斥量(Mutex)。

互斥量可以理解为二值信号量的一种变形,两者间的区别主要表现在:

互斥量用于保护资源时必须要被返还,而信号量用于数据同步时不需要返还。

FreeRTOS赋予互斥量优先级继承特性,即当互斥量被一个低优先级任务持有时,如果此时有高优先级任务尝试获取互斥量,那么这个高优先级任务会进入阻塞状态,不过同时会将低优先级任务的优先级提升至与自己相同的优先级。

优先级继承不能完全消除优先级倒置现象,只能尽可能降低优先级倒置产生的影响。

4.4 互斥量常用API

互斥量创建:

SemaphoreHandle_t xSemaphoreCreateMutex( void )

互斥量的获取和释放所使用API和信号量是相同的。互斥量使用如下:

SemaphoreHandle_t xMutex;//定义
...
Mutex_test = xSemaphoreCreateMutex(); //创建
...
xSemaphoreTake( Mutex_test , portMAX_DELAY );
...//要保护的资源
xSemaphoreGive( Mutex_test );

4.5 关键区

关键区(Critical Section)是资源保护的另一种方法。使用方法如下:

taskENTER_CRITICAL()会关闭所有中断(包括调度器执行所在的周期嘀嗒定时器中断);

taskEXIT_CRITICAL()会再次打开中断;

taskENTER_CRITICAL();
...//要保护的资源
taskEXIT_CRITICAL();

5. FreeRTOSConfig配置文件

FreeRTOSConfig.h是一个用户级别的配置文件,不属于内核文件。

每个用户可以创建自己的FreeRTOSConfig.h,从而实现不同的功能配置。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值