FreeRTOS+LWIP

2019-08-20 19:34

FreeRTOS与LWIP的移植

1 FreeRTOS任务管理

1-1任务函数

任务是由C 语言函数实现的。唯一特别的只是任务的函数原型,其必须返回void,而且带有一个void 指针参数(void ATaskFunction( void *pvParameters );)。每个任务都是在自己权限范围内的一个小程序。其具有程序入口,通常会运行在一个死循环中,也不会退出。

FreeRTOS 任务不允许以任何方式从实现函数中返回——它们绝不能有一 条”return”语句,也不能执行到函数末尾。如果一个任务不再需要,可以显式地将其删除。

一个任务函数可以用来创建若干个任务——创建出的任务均是独立的执行实例,拥有属于自己的栈空间,以及属于自己的自动变量(栈变量),即任务函数本身定义的变量。 例:

void ATaskFunction( void *pvParameters )

{

/* 可以像普通函数一样定义变量。用这个函数创建的每个任务实例都有一个属于自己的

iVarialbleExample变量。但如果iVariableExample被定义为static,这一点则不成立– 这种情况下只存在一个变量,所有的任务实例将会共享这个变量。*/ int iVariableExample = 0;

/* 任务通常实现在一个死循环中。*/ for( ;; ) {

/* 完成任务功能的代码将放在这里。*/ }

/* 如果任务的具体实现会跳出上面的死循环,则此任务必须在函数运行完之前删除。传入NULL参数表示删除

的是当前任务*/ vTaskDelete( NULL ); }

1-2创建任务

创建任务使用FreeRTOS 的API 函数xTaskCreate()。 接下来描述用到的数据类型和命名约定。

xTaskCreate() API 函数原型如下:

portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,

const signed portCHAR * const pcName, unsigned portSHORT usStackDepth, void *pvParameters,

unsigned portBASE_TYPE uxPriority,

xTaskHandle *pxCreatedTask );

参数名 pdTASK_CODE pcName usStackDepth 表1 xTaskCreate()参数与返回值

含义描述 任务只是永不退出的C 函数,实现常通常是一个死循环。参数pvTaskCode 只一个指向任务的实现函数的指针(效果上仅仅是函数名)。 具有描述性的任务名。这个参数不会被FreeRTOS 使用。其只是单纯地用于辅助调试。识别一个具有可读性的名字总是比通过句柄来识别容易得多。 当任务创建时,内核会分为每个任务分配属于任务自己的唯一状态。usStackDepth值用于告诉内核为它分配多大的栈空间。 这个值指定的是栈空间可以保存多少个字(word),而不是多少个字节(byte)。比如说,如果是32 位宽的栈空间,传入的usStackDepth值为100,则将会分配400字节的栈空间(100 * 4bytes)。栈深度乘以栈宽度的结果千万不能超过一个size_t类型变量所能表达的最大值。 应用程序通过定义常量configMINIMAL_STACK_SIZE 来决定空闲任务任用的栈空间大小。在FreeRTOS 为微控制器架构提供的Demo 应用程序中,赋予此常量的值是对所有任务的最小建议值。如果你的任务会使用大量栈空间,那么你应当赋予一个更大的值。没有任何简单的方法可以决定一个任务到底需要多大的栈空间。计算出来虽然是可能的,但大多数用户会先简单地赋予一个自认为合理的值,然后利用FreeRTOS提供的特性来确证分配的空间既不欠缺也不浪费。 pvParameters uxPriority 任务函数接受一个指向void 的指针(void*)。pvParameters的值即是传递到任务中的值。这篇文档中的一些范例程序将会示范这个参数可以如何使用。 指定任务执行的优先级。优先级的取值范围可以从最低优先级0到最高优先级(configMAX_PRIORITIES–1)。 pxCreatedTask 用于传出任务的句柄。这个句柄将在API调用中对该创建出来的任务进行引用,比如改变任务优先级,或者删除任务。如果应用程序中不会用到这个任务的句柄,则pxCreatedTask 可以被设为NULL。 返回值 有两个可能的返回值: 1. pdTRUE表明任务创建成功。 2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY由于内存堆空间不足,FreeRTOS无法分配足够的空间来保存任务结构数据和任务栈,因此无法创建任务。 1-3任务优先级

xTaskCreate()API函数的参数uxPriority为创建的任务赋予了一个初始优先级。优先级可以在调度器启动后调用vTaskPrioritySet()API函数进行修改。任意数量的任务可以共享同一个优先级——以保证最大设计弹性。低优先级号表

示任务的优先级低,优先级号0表示最低优先级。调度器保证总是在所有可运行的任务中选择具有最高优先级的任务,并使其进入运行态。如果被选中的优先级上具有不止一个任务,调度器会让这些任务轮流执行。两个测试任务被创建在同一个优先级上,并且一直是可运行的。所以每个任务都执行一个“时间片”,任务在时间片起始时刻进入运行态,在时间片结束时刻又退出运行态。

调度器总是在可运行的任务中,选择具有最高优级的任务,并使其进入运行态。要能够选择下一个运行的任务,调度器需要在每个时间片的结束时刻运行自己本身。一个称为心跳(tick,有些地方被称为时钟滴答,本文中一律称为时钟心跳)中断的周期性中断用于此目的。时间片的长度通过心跳中断的频率进行设定,心跳中断频率由FreeRTOSConfig.h 中的编译时配置常configTICK_RATE_HZ 进行配置。比如说,如果configTICK_RATE_HZ设为100(HZ),则时间片长度为10ms。

API 函数vTaskPriofitySet()可以用于在调度器启动后改变任何任务的优先级。

表2 vTaskPrioritySet() 参数 参数 含义描述 被修改优先级的任务句柄(即目标任务)——参考xTaskCreate() API函数的参数pxTask pxCreatedTask 以了解如何得到任务句柄方面的信息。任务可以通过传入NULL 值来修改自己的优先级。 uxNewPriority 目标任务将被设置到哪个优先级上。如果设置的值超过了最大可用优先级(configMAX_PRIORITIES – 1),则会被自动封顶为最大值。常量configMAX_PRIORITIES 是FreeRTOSConfig.h 头文件中设置的一个编译时选项。 1-4调度算法 优先级抢占式调度

?

? ? ?

每个任务都赋予了一个优先级。

每个任务都可以存在于一个或多个状态。

在任何时候都只有一个任务可以处于运行状态。

调度器总是在所有处于就绪态的任务中选择具有最高优先级的任务来执行。 这种方法被称为“固定优先级抢占式调度”,所谓“固定优先级”是指每个任务都被赋予了一个优先级,这个优先级不能被内核本身改变(只能被任务修改)。”抢占式”是指当任务进入就绪态或是优先级被改变时,如果处于运行态的任务优先级更低,则该任务总是抢占当前运行的任务。

任务可以在阻塞状态等待一个事件,当事件发生时其将自动回到就绪态。时间事件发生在某个特定的时刻,比如阻塞超时。时间事件通常用于周期性或超时行为。任务或中断服务例程往队列发送消息或发送任务一种信号量,都将触发同步事件。同步事件通常用于触发同步行为,比如某个外围的数据到达了。

下图为某个应用程序的执行流程展现了抢占式调度的行为方式。

图1执行流程中的主要抢占点

1. 空闲任务

空闲任务具有最低优先级,所以每当有更高优先级任务处于就绪态是,空闲任务就会被抢占——如图中t3, t5 和t9 时刻。 2. 任务3

任务3 是一个事件驱动任务。其工作在一个相对较低的优先级,但优先级高于空闲任务。其大部份时间都在阻塞态等待其关心的事件。每当事件发生时其就 从阻塞态转移到就绪态。FreeRTOS 中所有的任务间通信机制(队列,信号量等) 都可以通过这种方式用于发送事件以及让任务解除阻塞。事件在t3,t5 以及t9 至t12 之间的某个时刻发生。发生在t3 和t5 时刻的事件可以立即被处理,因为这些时刻任务3 在所有可运行任务中优先级最高。发生在t9 至t12 之间某个时刻的事件不会得到立即处理,需要一直等到t12 时刻。因为具有更高优先级的任务1 和任务2 尚在运行中,只有到了t12 时刻,这两个任务进入阻塞态,使得任务3 成为具有最高优先级的就绪态任务。 3. 任务2

任务2 是一个周期性任务,其优先级高于任务3 并低于任务1。根据周期间隔,任务2 期望在t1,t6 和t9 时刻执行。在t6 时刻任务3 处于运行态,但是任务2 相对具有更高的优先级,所以会抢占任务3,并立即得到执行。任务2 完成处理后,在t7 时刻返回阻塞态。同时,任务3 得以重新进入运行态,继续完成处理。任务3 在t8 时刻进入阻塞状态。 4. 任务1

任务1 也是一个事件驱动任务。任务1 在所有任务中具有最高优先级,因此可以抢占系统中的任何其它任务。在图中看到,任务1 的事件只是发生在在t10时刻,此时任务1 抢占了任务2。只有当任务1 在t11 时刻再次进入阻塞态之后,任务2 才得以机会继续完成处理。

选择任务优先级

单调速率调度(Rate Monotonic Scheduling, RMS)是一种常用的优先级分配

技术。其根据任务周期性执行的速率来分配一个唯一的优先级。具有最高周期执行频率的任务赋予高最优先级;具有最低周期执行频率的任务赋予最低优先级。这种优先级分配方式被证明了可以最大化整个应用程序的可调度性

(schedulability),但是运行时间不定以及并非所有任务都具有周期性,会使得对这种方式的全面计算变得相当复杂。

协作式调度

采用一个纯粹的协作式调度器,只可能在运行态任务进入阻塞态或是运行态任务显式调用taskYIELD()时,才会进行上下文切换。任务永远不会被抢占,而具有相同优先级的任务也不会自动共享处理器时间。协作式调度的这作工作方式虽然比较简单,但可能会导致系统响应不够快。

实现混合调度方案也是可行的,这需要在中断服务例程中显式地进行上下文切换,从而允许同步事件产生抢占行为,但时间事件却不行。这样做的结果是得到了一个没有时间片机制的抢占式系统。或许这正是所期望的,因为获得了效率,并且这也是一种常用的调度器配置。

2 FreeRTOS队列管理

2-1队列的特性

1、数据存储

队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称为队列的“深度”。在队列创建时需要设定其深度和每个单元的大小。

通常情况下,队列被作为FIFO(先进先出)使用,即数据由队列尾写入,从队列首读出。当然,由队列首写入也是可能的。

往队列写入数据是通过字节拷贝把数据复制存储到队列中;从队列读出数据使得把队列中的数据拷贝删除。 2、可被多任务存取

所有任务都可以向同一队列写入和读出。 3、读队列时阻塞

当某个任务试图读一个队列时,其可以指定一个阻塞超时时间。在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务例程往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。

由于队列可以被多个任务读取,所以对单个队列而言,也可能有多个任务处于阻塞状态以等待队列数据有效。这种情况下,一旦队列数据有效,只会有一个任务会被解除阻塞,这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同,那么被解除阻塞的任务将是等待最久的任务。 4、写队列时阻塞


FreeRTOS+LWIP.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:中国公务员制度第2阶段测试题

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: