freeRTOS 一步步学习(二)
任务状态
应用程序可以包含多个任务。如果运行应用程序的微控制器只有一个核(core),那么在任意给定时间,实际上只会有一个任务被执行。这就意味着一个任务可以有一个或
两个状态,即运行状态和非运行状态。 FreeRTOS 的调度器是能让任务切入切出的唯一实体。
任务创建
xTaskCreate() API 函数,创建任务使用 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 );
参数名 描述
pvTaskCode 任务只是永不退出的 C 函数,实现常通常是一个死循环。参数pvTaskCode 只一个指向任务的实现函数的指针(效果上仅仅是函数名)。
pcName 具有描述性的任务名。这个参数不会被 FreeRTOS 使用。其只是单纯地用于辅助调试。识别一个具有可读性的名字总是比通过句柄来识别容易得多。
应用程序可以通过定义常量 config_MAX_TASK_NAME_LEN 来定义任务名的最大长度——包括’\0’结束符。如果传入的字符串长度超过了这个最大值,字符串将会自动被截断 。
usStackDepth 当任务创建时,内核会分为每个任务分配属于任务自己的唯一状态。usStackDepth 值用于告诉内核为它分配多大的栈空间。这个值指定的是栈空间可以保存多少个字(word),而不是多少个字节(byte)。比如说,如果是 32位宽的栈空间,传入的 usStackDepth值为 100,则将会分配 400 字节的栈空间(100 * 4bytes)。栈深度乘以栈宽度的结果千万不能超过一个 size_t 类型变量所能表达的最大值 。
应用程序通过定义常量 configMINIMAL_STACK_SIZE 来决定空闲任务任用的栈空间大小。在 FreeRTOS 为微控制器架构提供的Demo 应用程序中,赋予此常量的值是对所有任务的最小建议值。如果你的任务会使用大量栈空间,那么你应当赋予一个更大的值。没有任何简单的方法可以决定一个任务到底需要多大的栈空间。计算出来虽然是可能的,但大多数用户会先简单地赋予一个自认为合理的值,然后利用 FreeRTOS 提供的特性来确证分配的空间既不欠缺也不浪费。FreeRTOS 有函数去查询任务使用了多少栈空间 。
pvParameters 任务函数接受一个指向 void 的指针(void*)。 pvParameters 的值即是传递到任务中的值。这篇文档中的一些范例程序将会示范这个参数可以如何使用 。
uxPriority 指定任务执行的优先级。优先级的取值范围可以从最低优先级 0 到最高优先级(configMAX_PRIORITIES – 1)。configMAX_PRIORITIES 是一个由用户定义的常量。优先级号并没有上限(除了受限于采用的数据类型和系统的有效内存空间),但最好使用实际需要的最小数值以避免内存浪费。如果 uxPriority 的值超过了(configMAX_PRIORITIES – 1),将会导致实际赋给任务的优先级被自动封顶到最大合法值。
pxCreatedTask pxCreatedTask 用于传出任务的句柄。这个句柄将在 API 调用中对该创建出来的任务进行引用,比如改变任务优先级,或者删除任务。如果应用程序中不会用到这个任务的句柄,则 pxCreatedTask 可以被设为 NULL。
返回值 有两个可能的返回值:
1. pdTRUE表明任务创建成功。
2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY由于内存堆空间不足, FreeRTOS 无法分配足够的空间来保存任务结构数据和任务栈,因此无法创建任务 。
示例:
int main( void )
{
/* 创建第一个任务。需要说明的是一个实用的应用程序中应当检测函数xTaskCreate()的返回值,以确保任
务创建成功。*/
xTaskCreate( vTask1, /* 指向任务函数的指针 */
"Task 1", /* 任务的文本名字,只会在调试中用到*/
1000, /* 栈深度– 大多数小型微控制器会使用的值会比此值小得多*/
NULL, /* 没有任务参数*/
1, /* 此任务运行在优先级1上. */
NULL ); /* 不会用到任务句柄*/
/* Create the other task in exactly the same way and at the same priority. */
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
/* 启动调度器,任务开始执行*/
vTaskStartScheduler();
/* 如果一切正常,main()函数不应该会执行到这里。但如果执行到这里,很可能是内存堆空间不足导致空闲
任务无法创建。第五章有讲述更多关于内存管理方面的信息*/
for( ;; );
}
任务优先级
xTaskCreate() API 函数的参数 uxPriority 为创建的任务赋予了一个初始优先级。
应 用 程 序 在文 件 FreeRTOSConfig.h中 设 定 的 编译 时 配 置 常量configMAX_PRIORITIES 的值,即是最多可具有的优先级数目。 FreeRTOS 本身并没有限定这个常量的最大值,但这个值越大,则内核花销的内存空间就越多。所以总是建议将此常量设为能够用到的最小值 。
对于如何为任务指定优先级, FreeRTOS 并没有强加任何限制。任意数量的任务可以共享同一个优先级——以保证最大设计弹性。当然,如果需要的话,你也可以为每个任务指定唯一的优先级(就如同某些调度算法的要求一样),但这不是强制要求的。
低优先级号表示任务的优先级低,优先级号 0 表示最低优先级。有效的优先级号范围从 0 到(configMAX_PRIORITES – 1).
调度器保证总是在所有可运行的任务中选择具有最高优先级的任务,并使其进入运行态。如果被选中的优先级上具有不止一个任务,调度器会让这些任务轮流执行。
时间片的长度通过心跳中断的频率进行设定,心跳中断频率由FreeRTOSConfig.h 中的编译时配置常量 configTICK_RATE_HZ 进行配置。比如说,如果 configTICK_RATE_HZ设为 100(HZ),则时间片长度为 10ms。
FreeRTOS API 函数调用中指定的时间总是以心跳中断为单位(通常的提法为心跳”ticks”)。常量 portTICK_RATE_MS 用于将以心跳为单位的时间值转化为以毫秒为单位的时间值。有效精度依赖于系统心跳频率。