关键词不能为空

当前您在: 主页 > 英语 >

FreeRTOS学习

作者:高考题库网
来源:https://www.bjmy2z.cn/gaokao
2021-02-01 23:42
tags:

-

2021年2月1日发(作者:一加仑)


FREE RToS


操作系统学习笔记


--


主要函数记录



1.2


任务函数



原型:


void ATaskFunction( void *pvParameters );


一般来说每个任务都会运行在自己的死循环中。 每个任务不允许从函数实现中返回,绝对不能



return< /p>


语句,当一个任务不再需要时可以将其删除。




一个任务函数可以用来创建若干个任务——创建出的任务均是 独立的执行实例,拥有属于自己


的栈空间,以及属于自己的自动变量

(


栈变量


)


,即任务函数本身定义 的变量。




1.3


任务删除函数



vTaskDelete( NULL );


传入


NULL


参数表示删除当前任务




FreeRTOS


的调度器是能让任务切入切出的唯一实体。




1.4


创建任务



创建任务使用


FreeRTOS



API


函数


xTaskCreate()




函数原型



portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,


const signed portCHAR * const pcName,


unsigned portSHORT usStackDepth,


void *pvParameters,


unsigned portBASE_TYPE uxPriority,


xTaskHandle *pxCreatedTask );



参数



pv TaskCode


参数:


一个指向任务的实现

< br>函数的指针


(


效果上仅仅是函数名


)





pcName


具有描述性的任务名


- -------


应用程序可以通过定义常量


config_MA X_TASK_NAME_LEN



来定义任务名的最大长度— —包括’


0


’结束符。如果传入的字符串长度超过了这个最大值 ,


字符串将会自动被截断。




usStackDepth


参数值用于告诉内核为它分配多大 的栈空间



---


这个值指定的是栈空 间可以保


存多少个字


(word)


,< /p>


而不是多少个字节


(byte)



-----


栈深度乘以栈宽度的结果千万不能超过一

< br>个


size_t


类型变量所能表达的最大值。




pvParameters


任务函数接受一个指向


void


的指 针


(void*)



pvParame ters


的值即



是传递到任务中的值。




uxPriority


指定任务执行的优先级


。优先级的取值范围可以从最低优先级


0


到最高优先级


(configMAX_PRIORITIES



1)



- ---


如果


uxPriority


的值超过了


(configMAX_PRIORITIES




1)


,将 会导致实际赋给任务的优先级被自动封顶到最大合法值。




pxCreatedTask


用于传出任务的句柄


。这个句柄将在


API


调用中对该创建出来的 任务进行引


用,比如改变任务优先级,或者删除任务。


如果应用 程序中不会用到这个任务的句柄,则


pxCreatedTask

可以被设为


NULL



-----



般应用程序中不会再次改变任务的优先级,或者删

< p>
除任务(除非任务死掉,删除后重新建立任务)




返回值



有两个可能的返回值:



1. pdTRUE-------


表明任务创建成功。



2.


errCOULD_NOT_ALLOCATE_REQ UIRED_MEMORY----


由于内存堆空间不足,


Fr eeRTOS


无法分配足


够的空间来保存任务结构数据和任务 栈,因此无法创建任务。




int main( void )


{


/*


创建第一个任务。需要说明的是一个实用的应用程序中应当检测函数


xTaskCre ate()



返回值,以确保任务创建成功。

< br> */


xTaskCreate( vTask1,


/*


指向任务函数的指针


*/



Task 1


/*


任务的文本名字,只会在调试中用到


*/


1000,


/*


栈深度





大多数小型微控制器会使用的值会比此值小得多


*/


NULL,


/*


没有任务参数


*/


1,


/*


此任务运行在优先级


1



. */


NULL );


/*


不会用到任务句柄


*/


xTaskCreate( vTask2,


/*


启动调度器,任务开始执行


*/


vTaskStartScheduler();


/*


如果一切正常,


main()


函数不应该会 执行到这里。但如果执行到这里,很可能是内存堆


空间不足导致空闲任务无法创建。第五 章有讲述更多关于内存管理方面的信息


*/


for( ; );


}



1.5


任务优先级



任务优先级修改函数


vTaskPrioritySet()
















FreeRTOSConfig.h



设定的编译时配置常量


configMAX_PRIORITIES


的值,即是最多 可具有的优先级数目



FreeRTOS

本身并没有限定这个常量的最大值,


但这个值


越大,则内核 花销的内存空间就越多。


所以总是建议将此常量设为能够用到的最小值。




低优先级号表示任务的优先级低,优先级号


0


表示最低优先级。有效的优先级号范围从


0



(configMAX_PRIORITES



1)





如果被选中的优先级上具有不止一个任务,调度器会让这些任 务轮流执行




时间片的长度通过心跳 中断的频率进行设定,


心跳中断频率由


FreeRTOSCon fig.h


中的编译时


配置常量


co nfigTICK_RATE_HZ


进行配置


。比如说,如果


configTICK_RATE_HZ


设为


100(HZ)



则时间片长度为

10ms



常量


portTICK_RATE_MS

用于将以心跳为单位的时间值转化为以毫秒为单位的时间值


。有效精


度依赖于系统心跳频率。




1.6


扩充“非运行态”




阻塞状态


----

< br>如果一个任务正在等待某个事件,则称这个任务处于”阻塞态


(blocked)


”。阻塞


态是非运行态的一个子状




互斥信号量,因为其主要用于实现互斥访问

< br>)


和互斥量都可以用来实现同步事件。




挂起状态


----

< br>“挂起


(suspended)



也是非运行状态的子状态。


处于挂起状态的任务对调度器而


言 是不可见的


。让一个任务进入挂起状态的唯一办法就是调用


vT askSuspend() API


函数;而


把一个挂起状态 的任务唤醒的唯一途径就是调用


vTaskResume()



vTaskResumeFromISR()


API


函数。


大多数应用程序中都不 会用到挂起状态。




就绪状态


----


如果任务处于非运行状态,


但既没有 阻塞也没有挂起,


则这个任务处于就


(ready



准备或就绪


)


状态。


处于就绪态的任务能够被运行


,但只是”准备


(ready)


”运行,而当前尚未


运行。

< br>



void vTaskDelay( portTickType xTicksToDelay )


xTicksToDelay


延迟多少个心跳周期。


调用该延迟函数的任务将进入阻塞态


,经延迟指定的心

< br>跳周期数后,再转移到就绪态。


--------


常数< /p>


portTICK_RATE_MS


可以用来将以毫秒为单位 的


时间值转换为以心跳周期为单位的时间值。




空闲任务是在调度器启动时自动创建的,

以保证至少有一个任务可运行


(


至少有一个任务处于就


绪态


)





vTaskDelayUntil() API


函数原型



void vTaskDelayUntil( portTickType * pxPreviousWakeTime,


portTickType xTimeIncrement );


vTaskDelayUntil()


类似于


vTaskDelay()


。函数


vTaskDelay()


的参数用来指定任务在调用


vTaskDelay()


到切出阻塞态整个过程包含多少个心跳周期。




任务保持在阻塞态的时间量由

< p>
vTaskDelay()


的入口参数指定,但任务离开阻塞态的时刻实际 上


是相对于


vTaskDelay()


被调用那一刻的



vTaskDelayUntil()


的参数就是用来指定任务离开阻


塞态进入就绪态那一刻的精确心跳计数 值。


API


函数


vTaskDela yUntil()


可以用于实现一个固


定执行周期的需求


(


当你需要让你的任务以固定频率周期性执行的时候

< br>)


。由于调用此函数的任


务解除阻塞的时间是绝对时刻,


比起相对于调用时刻的相对时间更精确


(


即比调用


vTaskDelay()


可以实现更精确的周期性


)





pxPreviousWakeTime


此参数命名时假定< /p>


vTaskDelayUntil()


用于实现某个任务以固定频 率周期


性执行。


这种情况下


pxPre viousWakeTime


保存了任务上一次离开阻塞态


(< /p>


被唤醒


)


的时刻。


这个


时刻被用作一个参考点来计算该任务下一次离开阻塞态的时刻。

< br>


pxPreviousWakeTime



指向的变量值会在


API


函数


vTaskDelayUntil()


调用过程中自动更新,



用程序除了该变量第一次初始化外,通常都不要修改它的值。




void vTaskFunction( void *pvParameters )


{


char *pcTaskName;


portTickType xLastWakeTime;


/* The string to print out is passed in via the parameter. Cast this to a


character pointer. */


pcTaskName = ( char * ) pvParameters;


/*


变量


xLastWakeTim e


需要被初始化为当前心跳计数值。


说明一下,


这是该变量唯一一次


被显式赋值。之后,


< p>
xLastWakeTime


将在函数


vTask DelayUntil()


中自动更新。


*/


xLastWakeTime = xTaskGetTickCount();


/* As per most tasks, this task is implemented in an infinite loop. */


for( ; )


{


/* Print out the name of this task. */


vPrintString( pcTaskName );


/*


本任务将精确的以


250< /p>


毫秒为周期执行。同


vTaskDelay()

< br>函数一样,时间值是以心跳周期为单位的,



可以使用常 量


portTICK_RATE_MS


将毫秒转换为心跳周期。 变量


xLastWakeTime


会在



vTaskDelayUntil()


中自动更新,因此不需要 应用程序进行显示更新。


*/


vTaskDelayUntil( &xLastWakeTime, ( 250 / portTICK_RATE_MS ) );


}


}





1.7


空闲任务与空闲任务钩子函数



当调用


vTaskStartScheduler()


时,调度器会自动创建一个空闲任务。空闲任务是一个非常短


小的循环— —空闲任务拥有最低优先级


(


优先级


0 )


以保证其不会妨碍具有更高优先级的应用任


务进入运行




空闲任务钩子函数




通过空闲任务钩子函数


(


或称回调,

< br>hook, or call-back)


,可以直接在空闲任务中添加应用程< /p>


序相关的功能。


空闲任务钩子函数会被空闲任务每循环一次就自动 调用一次。




通常空闲任务钩子函数被用于:




执行低优先级,后台或需要不停处理的功能代码。




测试处系统处理裕量


(


空闲任务只会在所有其它任务都不运行时才有机会执行,所


以测量出


空闲任务占用的处理时间就可以清楚的知道系统有多少富余的处理时间


)





将处理 器配置到低功耗模式——提供一种自动省电方法,


使得在没有任何应用功能需要处理


的时候,系统自动进入省电模式




空闲任务钩子函数必须遵从以下规则



1.


绝不能阻或挂起。


空闲任务只会 在其它任务都不运行时才会被执行


(


除非有应用任务共享空


闲任务优先级


)



以任何方式阻塞空闲任务都可能导致没有任务能够进入运行态!



2.



如果应用程序用到了


vTaskDelete()


AP


函数,则空闲钩子函数必须能够尽快返回。


因为在


任务被删除后,空闲任务负责回收内核资源。


如果空闲任务一直运行在钩子函数中,则无法进


行回收工作。



空闲任务钩子函数的函数名和函数原型。



void vApplicationIdleHook( void );



/* Declare a variable that will be incremented by the hook function. */


unsigned long ulIdleCycleCount = 0UL;


/*


空闲钩子函数必须命名为


vAp plicationIdleHook(),


无参数也无返回值。


*/


void vApplicationIdleHook( void )


{


/* This hook function does nothing but increment a counter. */


ulIdleCycleCount++;


}



FreeRTOSConfig.h


中的配置常量


configUSE_IDLE_HOOK


必须定义为


1



这 样空闲任务钩子函数


才会被调用。




void vTaskFunction( void *pvParameters )


{


char *pcTaskName;


/* The string to print out is passed in via the parameter. Cast this to a


character pointer. */


pcTaskName = ( char * ) pvParameters;


/* As per most tasks, this task is implemented in an infinite loop. */


for( ; )


{


/*


打印输出任务名,以及调用计数器


ulIdleCycleCoun t


的值。


*/


vPrintStringAndNumber( pcTaskName, ulIdleCycleCount );


/* Delay for a period for 250 milliseconds. */


vTaskDelay( 250 / portTICK_RATE_MS );


}


}



1.8


改变任务优先级



vTaskPrioritySet() API


函数



API


函数


vTaskPriofitySet()


可以用于在调度 器启动后改变任何任务的优先级。



void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );




pxTask


被修改优先级的任务 句柄


(


即目标任务


)

< br>——参考


xTaskCreate() API


函数的参数


pxCreatedTask


以了解如何得到任务句柄方面的信息。


任务可以通过传入

NULL


值来修改自己


的优先级。




uxNewPriority



目标任务将被设置到哪个优先级上。如果设置的值超过了最大可用优先级

(configMAX_PRIORITIES



1)


,则会被自动封顶为最大值。




常量


configMAX_PRIORITIES


是在


FreeRTOSConfig.h


头文件中设置的一个编译时选项。




uxTaskPriorityGet() API


函数



uxTaskPriorityGet() API


函数用于查询一个任务的优先级。



unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );



pxTask


被查询任务的句柄< /p>


(


目标任务


)



——参考


xTaskCreate() API


函数的参


pxCreatedTask


以了解如何得到任务句柄方面的信息。


任务可以通过传入


NULL


值来查询自己的优先级。



返回值



被查询任务的当前优先级。




1.9


删除任务



vTaskDelete() API


函数



函数原型:


void vTaskDelete( xTaskHandle pxTaskToDelete );


任务可以使用


API


函数


vTaskDelete()


删除自己或其它任务。需要说明一点,只有 内核为任务


分配的内存空间才会在任务被删除后自动回收。


任务 自己占用的内存或资源需要由应用程序自


己显式地释放。



pxTaskToDelete


被删除任务的句柄

< p>
(


目标任务


)



——



参考


xTaskCreate() API


函数的参数


pxCreatedTask

以了解如何得到任务句柄方面的信息。


任务可以通过传入


NULL


值来删除自己。




1.10


调度算法





简述



优先级抢占式调度






调度器总是在所有处于就绪态的任 务中选择具有最高优先级的任务来执行。



< br>抢占式”是指当任务进入就绪态或是优先级被改变时,如果处于运行态的任务优先级更低,则


该任务总是抢占当前运行的任务。




选择任务优先级



作为一种通用规则, 完成硬实时功能的任务优先级会高于完成软件时功能任务的优先级。




实现混合调度方案也是可行的,这需要在中断服务例程中显式 地进行上下文切换,从而允许同


步事件产生抢占行为,但时间事件却不行。这样做的结果 是得到了一个没有时间片机制的抢占


式系统。或许这正是所期望的,

因为获得了效率,并且这也是一种常用的调度器配置。





2.1


概览



基于


FreeRTOS


的应用程序由一组独立的任务构成——每个任务都是具有独立权限的 小程序。


这些独立的任务之间很可能会通过相互通信以提供有用的系统功能。

< p>
FreeRTOS


中所有的通信与


同步机制都是 基于队列实现的。




2.2


队列的特性



数据存储



队列可以保存有限个具有确 定长度的数据单元。


队列可以保存的最大单元数目被称为队列的


“深


度”。


在队列创建时需要设定其深度和每个单元的大小。< /p>




通常情况下,队列被作为

< p>
FIFO(


先进先出


)


使 用,即数据由队列尾写入,从队列首读出。当然,


由队列首写入也是可能的。

< p>



往队列写入数据是通过字节拷贝把数据复制存 储到队列中;从队列读出数据使得把队列中的数


据拷贝删除。




可被多任务存取


< br>队列是具有自己独立权限的内核对象,并不属于或赋予任何任务。


所有任务都可以 向同一队列


写入和读出。一个队列由多方写入是经常的事,但由多方读出倒是很少遇到。




读队列时阻塞



当某个任务试图读一个 队列时,其可以指定一个阻塞超时时间。


在这段时间中,


如果队 列为空,


该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务例程往其等 待的队列中写


入了数据,该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定 的阻塞时间,即使


队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。


由于队列可以被多个任务读取,


所以对单个队列而言,也可能有多个任务 处于阻塞状态以等待队列数据有效。


这种情况下,一


旦队列数据 有效,


只会有一个任务会被解除阻塞,这个任务就是所有等待任务中优先级最高的


任务。


而如果所有等待任务的优先级相同,那么被解除阻塞的任务将是 等待最久的任务。




写队列时阻塞



同读队列一样,


任务也可以在写队列时指定一个阻塞超时时间。


这个时间是当被写队列 已满时,


任务进入阻塞态以等待队列空间有效的最长时间。


由于 队列可以被多个任务写入,所以对单个


队列而言,也可能有多个任务处于阻塞状态以等待 队列空间有效。


这种情况下,一旦队列空间


有效,


只会有一个任务会被解除阻塞,这个任务就是所有等待任务中优先级最高的任务


。而如


果所有等待任务的优先级相同,那么被解除阻塞的任务将是等待最久的任务。< /p>




2.3


使用队列



xQueueCreate() API


函数




队列在使用前必须先被创建。



队列由声明为


xQueueHandle

< br>的变量进行引用



xQueueCreate()


用于创建一个队列,并返回一



xQueue Handle


句柄以便于对其创建的队列进行引用。


当创建队 列时,


FreeRTOS


从堆空间中


分配内存空间。分配的空间用于存储队列数据结构本身以及队列中包含的数据单元。


如果 内存


堆中没有足够的空间来创建队列,


xQueueCreat e()


将返回


NULL





队列创建函数原型



xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength,


unsigned portBASE_TYPE uxItemSize );


uxQueueLength



队列能够存储的最大单元数目,即队列深度。




uxItemSize


< p>
队列中数据单元的长度,


以字节为单位





返回值


NULL


表示没有足够的堆空间分配给队列而导致创建失败。非


NULL


值表示队列创建成


功。


此返 回值应当保存下来,以作为操作此队列的句柄。




xQueueSendToBack()




xQueueSendToFront() API


函数




xQ ueueSendToBack()


用于将数据发送到队列尾;




xQueueSendToFront()

< br>用于将数据发送到队列首。



xQueueSend()


完全等同于


xQueueSendToBack()

< p>

































xQueueSendToFront()




xQueueSendToBac k()


。系统提供中断安全版本的


xQueueSendToF rontFromISR()



xQueueSendToBa ckFromISR()


用于在中断服务中实现相同的功能。




xQueueSendToFront() API


函数原型



portBASE_TYPE xQueueSendToFront( xQueueHandle xQueue,const void * pvItemToQueue,


portTickType xTicksToWait );



xQueueSendToBack() API


函数原型



portBASE_TYPE xQueueSendToBack( xQueueHandle xQueue,const void * pvItemToQueue,


portTickType xTicksToWait );



xQueue


目标队列的句柄


。这个句柄即是调用


xQueueCreate()


创建该队列时的返回值。




pvItemToQueue


发送数据的指针。


其指向将要复制到目标队列中的数据单元。由于在创建队列


时设置了队列中数 据单元的长度,所以会从该指针指向的空间复制对应长度的数据到队列的存


储区域。




xTicksToWait


阻塞超时时间



如果在发送时队列已满,


这个时间即是任务处于阻塞态等待队列


空间有效的最长等待时间。







xTicksToWait


设为


0




并且队列已满,



< br>xQueueSendToFront()



xQueu eSendToBack()


均会立即返回。


阻塞时间是以系统 心跳周期为单位的,所以绝对时间取决于系统心跳频率。



量< /p>


portTICK_RATE_MS


可以用来把心跳时间单位转换为毫秒时间单位。









xTicksToWait


设置为


portMAX_DELAY




并且在


FreeRTOSConig.h


中设定


INCLUDE_vTaskSuspend



1


,那么阻塞等待将没有超时限制。




返回值



有两个可能的返回值


:


1. pdPASS


返回


pdPASS


只 会有一种情况,那就是数据被成功发送到队列中


。如果设定了阻塞超时时间


(xTicksToWait



0)


,在函数返回之前任务将被转移到阻塞态以等待队列空间有效—在超时到


来前 能够将数据成功写入到队列,函数则会返回


pdPASS





2. errQUEUE_FULL


如果由于队列已满而无法将数据写入,



则将返回


errQUEUE_FULL


。如果设定了阻塞超时 时间



xTicksToWait



0


),在函数返回之前任务将被转移到阻塞态以等待队列空间有 效。


但直到


超时也没有其它任务或是中断服务例程读取队列而腾 出空间,函数则会返回


errQUEUE_FULL





xQueueReceive()



xQueuePeek() API


函数




xQ ueueReceive()


用于从队列中接收


(


读取)数据单元。


接收到的单元同时会从队列中删除。



xQueuePeek()


也是从从队列中接收数据单元,


不同的是并不从队列中删出接收到的单元。


xQueuePee k()


从队列首接收到数据后,


不会修改队列中的数据,也不会 改变数据在队列中的存


储序顺。




切记不要在中断服务例程中调用


xQueueRceive( )



xQueuePeek()


。中断 安全版本



的替代


API


函数


xQueueReceiveFromISR()



xQueueReceive() API


函数原型



portBASE_TYPE xQueueReceive( xQueueHandle xQueue,const void * pvBuffer,


portTickType xTicksToWait );



xQueuePeek() API


函数原型



portBASE_TYPE xQueuePeek( xQueueHandle xQueue,const void * pvBuffer,


portTickType xTicksToWait );



xQueue


被读队列的句柄


。这个句柄即是调用


xQueueCreate()


创建该队列时的返回值。




pvBuffer


接收缓存指针


。其 指向一段内存区域,用于接收从队列中拷贝来的数据。



数据单 元的长度在创建队列时就已经被设定,所以该指针指向的内存区域大小应当足够保存一


个 数据单元。




xTicksToWait


阻塞超时时间


如果在接收时队列为空,


则这个时间是任务处于阻塞状态 以等待


队列数据有效的最长等待时间。




FreeRTOSConig.h


中设定


INCLUDE_vTaskSuspend



1


,那么阻塞等待将没有超时限制。




返回值



有两个可能的返回值


:


1. pdPASS

-


-


-


-


-


-


-


-



本文更新与2021-02-01 23:42,由作者提供,不代表本网站立场,转载请注明出处:https://www.bjmy2z.cn/gaokao/595869.html

FreeRTOS学习的相关文章