FreeRTOS
FreeRTOS 目录结构
目录结构如下

RTOS的概念
普通单片机程序.
1 2 3 4 5 6 7
| void main() { while (1) { fun_A(); fun_B(); } }
|
RTOS程序.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void task_A() { while (1) { fun_A(); } }
void task_B() { while (1) { fun_B(); } }
void main() { create_task(task_A); create_task(task_B); start_scheduler(); while (1) { sleep() } }
|
创建任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| void task_1(void* arg) { while (1) { printf("task_1\r\n"); } }
void task_2(void* arg) { while (1) { printf("task_2\r\n"); } }
int main(void) { TaskHandle_t xHandlerTask1; TaskHandle_t xHandlerTask2; xTaskCreate(task_1, "task_1", 100, NULL, 1, &xHandlerTask1); xTaskCreate(task_2, "task_2", 100, NULL, 1, &xHandlerTask2);
vTaskStartScheduler(); }
|
创建静态任务
在FreeRTOSConfig.h文件中需要配置下列宏定义.
1
| #define configSUPPORT_STATIC_ALLOCATION 1
|
还需要定义vApplicationGetIdleTaskMemory函数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| TaskHandle_t xHandleTask3;
void Task3Function(void* arg) { while (1) { printf("3"); } }
StackType_t xTask3Stack[100]; StaticTask_t xTask3TCB;
StackType_t xIdleTaskStack[100]; StaticTask_t xIdleTaskTCB;
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, StackType_t ** ppxIdleTaskStackBuffer, uint32_t * pulIdleTaskStackSize ) { *ppxIdleTaskTCBBuffer = &xIdleTaskTCB; *ppxIdleTaskStackBuffer = xIdleTaskStack; *pulIdleTaskStackSize = 100; }
int main(void) { xHandleTask3 = xTaskCreateStatic(task_3, "task_3", 100, NULL, 1, xTask3Stack, &xTask3TCB); }
|
vTaskDelete
删除任务.
1 2 3 4 5 6
| void vTaskDelete(TaskHandle_t xTaskToDelete);
|
任务状态
RTOS中共有四种状态,阻塞(Blocked),暂停(Suspended),就绪(Ready),运行(Running),其转换过程可以参考下图.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| void task_1(void* arg) { TickType_t Start = xTaskGetTickCount(); TickType_t t; int flag = 0; while (1) { t = xTaskGetTickCount(); task1flagrun = 1; task2flagrun = 0; task3flagrun = 0; printf("1"); if (!flag && (t > Start + 10)) { vTaskSuspend(xHandleTask3); flag = 1; } if (t > Start + 20) { vTaskResume(xHandleTask3); } } }
void task_2(void* arg) { while (1) { task1flagrun = 0; task2flagrun = 1; task3flagrun = 0; printf("2"); vTaskDelay(10); } }
void task_3(void* arg) { while (1) { task1flagrun = 0; task2flagrun = 0; task3flagrun = 1; printf("3"); } }
|

vTaskDelay
两种不同的Delay,vTaskDelay和vTaskDelayUntil.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| void task_1(void* arg) { TickType_t Start = xTaskGetTickCount(); int i = 0; int j = 0; while (1) { task1flagrun = 1; task2flagrun = 0; task3flagrun = 0; for (i = 0; i < rand[j]; i++) printf("1"); j++; if (j == 5) j = 0; #if 0 vTaskDelay(20); #else vTaskDelayUntil(&Start, 20); #endif } }
void main() { xTaskCreate(task_1, "task_1", 100, NULL, 2, &xHandleTask1); }
|
vTaskDelay
每次执行结束到下次执行开始之间的时间间隔相同. 间隔即位所填参数.

vTaskDelayUntil
每次执行开始到下次执行开始之间的时间间隔相同.间隔即位所填参数

空闲函数与其钩子函数
空闲函数用于帮助自删的任务函数处理后续工作如内存回收等,同时还可以定义钩子函数,在空闲函数中调用. 钩子函数的出现是便于我们利用空闲函数,但不破坏其本身的功能,所以为了保证其原本的功能能正常执行,即处理自删任务的内存回收,不能使空闲函数进入Suspended和Blocked状态,以及建议钩子函数处理的工作需要优先级低且简单高效.
为了使用钩子函数,首先需要在FreeRTOSConfig.h中定义如下宏定义:
1
| #define configUSE_IDLE_HOOK 1
|
然后实现如下函数:
1 2 3 4 5
| void vApplicationIdleHook(void) { printf("Idle Task Running...\r\n"); }
|
注意不要在钩子函数中执行死循环.
任务调度算法
- 是否允许抢占:
允许抢占的话,高优先级的任务先执行;不允许抢占的话,大家都是平等,谁先执行谁就一致霸占住,除非主动放弃CPU资源.
- 允许抢占时,是否允许时间片轮转:
允许时间片轮转的话,同优先级的任务交替执行;不允许时间片轮转的话,谁先运行谁就一直运行.
- 允许抢占时,允许时间片轮转时,空闲任务是否让步:
即空闲任务中存在一条判断语句,如果定义了该宏定义,执行一次调度.
