导言

本帖记录一些有关freertos内核的一些冷门的知识以及一些代码解读(不间断更新)

大部分文案借鉴《USING THE FREERTOS REAL TIME KERNEL A Practical Guide》由Richard Barry所著

笔记

软实时和硬实时

软实时:计算机对每个输入的响应应当限定在一个恰当的时间范围——但是如果响应时间超出了限定范围,并不会让人觉得这台电脑无法使用。

硬实时:硬实时功能必须在给定的时间限制之内完成——如果无法做到即意味着整个系统的绝对失败

线程和任务

在 FreeRTOS 中,每个执行线程都被称为”任务”。

xTaskCreate的一些注意事项

应用程序可以通过定义常量 config_MAX_TASK_NAME_LEN 来定
义任务名的最大长度——包括’\0’结束符。如果传入的字符串长度超
过了这个最大值,字符串将会自动被截断。

usStackDepth 值用于告诉内核为它分配多大的栈空间。
这个值指定的是栈空间可以保存多少个字(word),而不是多少个字
节(byte)。

好用但不常用的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 定义将要通过任务参数传递的字符串。定义为const,且不是在栈空间上,以保证任务执行时也有效。 */
static const char *pcTextForTask1 = “Task 1 is running\r\n”;
static const char *pcTextForTask2 = “Task 2 is running\t\n”;
int main( void )
{
/* 第一个任务创建在优先级1上。优先级是倒数第二个参数。 */
xTaskCreate( vTaskFunction, "Task 1", 1000, (void*)pcTextForTask1, 1, NULL );
/* 第二个任务创建在优先级2上。 */
xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 2, NULL );
/* Start the scheduler so the tasks start executing. */
vTaskStartScheduler();
return 0;
}
奇怪的术语“饿死”

任务2的优先级比任务1高,并且总是可运行,因此任务2是唯一一个一直处于运行态的任务。而任务1不可能进入运行态,所以不可能输出字符串。这种情况我们称为任务1的执行时间被任务2”饿死(starved)”了。

这种”不停处理”类型的任务限制了其有用性,因为它们只可能被创建在最低优先级上。如何它们运行在其它任何优先级上,那么比它们优先级更低的任务将永远没有执行的机会。

解法:采用事件驱动任务

定时事件和同步事件
  1. 定时(时间相关)事件——这类事件可以是延迟到期或是绝对时间到点。比如说某个任务可以进入阻塞态以延迟 10ms。
  2. 同步事件——源于其它任务或中断的事件。比如说,某个任务可以进入阻塞态以等待队列中有数据到来。同步事件囊括了所有板级范围内的事件类型。
一些更加清晰的状态转移图片
用vTaskDelay代替空循环

常数 portTICK_RATE_MS 可以用来将以毫秒为单位的时间值转
换为以心跳周期为单位的时间值。

vTaskDelay( 250 / portTICK_RATE_MS );

vTaskDelayUntil 高级用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
/* 变量xLastWakeTime需要被初始化为当前心跳计数值。说明一下,这是该变量唯一一次被显式赋值。之后,
xLastWakeTime将在函数vTaskDelayUntil()中自动更新。 */
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毫秒为周期执行。同vTaskDelay()函数一样,时间值是以心跳周期为单位的,
可以使用常量portTICK_RATE_MS将毫秒转换为心跳周期。变量xLastWakeTime会在
vTaskDelayUntil()中自动更新,因此不需要应用程序进行显示更新。 */
vTaskDelayUntil( &xLastWakeTime, ( 250 / portTICK_RATE_MS ) );
}
}
空闲任务的钩子函数
1
2
3
4
5
6
7
8
/* Declare a variable that will be incremented by the hook function. */
unsigned long ulIdleCycleCount = 0UL;
/* 空闲钩子函数必须命名为vApplicationIdleHook(),无参数也无返回值。 */
void vApplicationIdleHook( void )
{
/* This hook function does nothing but increment a counter. */
ulIdleCycleCount++;
}
  • 前提是将configUSE_IDLE_HOOK 必须定义为 1
任务修改的一些常用函数
1
2
3
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );//修改函数
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );//查询函数
void vTaskDelete( xTaskHandle pxTaskToDelete );//任务删除