任务的调度
前面说过,想要实现多任务的调度,要满足 ①就绪列表中有任务,②开启了任务调度器。
我们创建了新任务,任务就位于就绪列表中,之后开启任务调度器,就能开始任务的切换。
开启任务调度器的函数
函数 | vTaskStartScheduler ( ) |
功能 | 启动任务调度器,启动后 FreeRTOS 开始对就绪列表中的任务进行调度 |
挂起、恢复任务调度器的函数
针对一些对时序有严格时间要求的外设,例如 I2C、SPI、DHT11,这类代码运行过程是不能被打断的(这类代码也成为临界区代码),否则模块极有可能无法正常工作,为防止可以先挂起任务调度器,等代码完整执行完后再恢复任务调度器
vTaskSuspendAll ( ) | 挂起任务调度器,停止任务调度,进行执行当前任务程序 |
xTaskResumeAll ( ) | 恢复任务调度器,任务调度又开始 |
注意事项
挂起和恢复必须成对使用,如下。
vTaskSuspendAll(); ...... //执行过程中要求不能被打断的代码 xTaskResumeAll();
与进入临界区(下面有介绍)不一样,挂起任务调度器并未关闭中断;
它仅仅是防止了任务之间的资源争夺,中断照样可以直接响应;
挂起调度器这种方式适用于位于任务与任务之间的临界区;
任务调度器挂起恢复对比进入临界区的好处是既不用去延时中断的响应,又可以做到临界区的安全;
临界区
临界区,也称临界段代码,是指那些必须完整运行,运行过程中不能被打断(途中不能进入中断,不能切换到其他任务)的代码段
为什么需要对临界区进行保护?
一些外设如 I2C、SPI、DHT11 这些,都需要严格按照时序进行初始化,程序执行时不能中途被打断,不然会导致模块无法正常工作。打断当前程序的运行的手段有两种:中断、任务调度(任务调度也是借助滴答定时器中断来实现),所以想要保证程序能够完整运行,最简单粗暴的方法就是关闭中断。中断在 FreeRTOS 中都有最高优先级。所以保护临界区其实就是通过关闭中断,来保证程序一直运行,不被打断。
临界段代码保护函数:
任务级:在任务函数中调用的
taskENTER_CRITICAL() | 进入临界段(本质:关中断) |
taskEXIT_CRITICAL() | 退出临界段(本质:开中断) |
中断级:在中断服务函数中调用的
taskENTER_CRITICAL_FROM_ISR() | 进入临界段(本质:关中断) |
taskEXIT_CRITICAL_FROM_ISR() | 退出临界段(本质:开中断) |
注意事项
进入临界区函数和退出临界区函数要成对使用,如下。
任务级: taskENTER_CRITICAL(); ...... //临界区 taskEXIT_CRITICAL(); 中断级: uint32_t save_status; save_status = taskENTER_CRITICAL_FROM_ISR(); ...... //临界区 taskEXIT_CRITICAL_FROM_ISR(save_status);
要尽量保持临界段代码执行耗时短,因为长时间关中断可能会有安全隐患。