# 1.任务调度器
任务调度器时管理多任务执行的先后循序,FreeRTOS 是一个抢占式的实时多任务系统,那么其任务调度器也是抢占式的。高优先级的任务可以打断低优先级任务的运行而取得CPU 的使用权。
RTOS使用时的一个实时应用作为一个独立的任务,每个任务有自己的运行环境,任何一个时间点只能运行一个任务,任何一个时间点只能有一个任务运行,具体运行哪个任务是由RTOS 调度器来决定的,RTOS 调度器因此就会重复的开启、关闭每个任务。任务不需要了解RTOS 调度器的具体行为,RTOS 调度器的职责是确保当一个任务开始执行的时候其上下文环境(寄存器值,堆栈内容等)和任务上一次退出的时候相同。
如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处于运行。
每个任务都可以分配一个从0~(configMAX_PRIORITIES-1) 的优先级,configMAX_PRIORITIES 在文件FreeRTOSConfig.h 中有定义,优先级不超过32级,数字越小优先级越小,0 的优先级最低,configMAX_PRIORITIES-1 的优先级最高。空闲任务的优先级最低为0。特别的configMAX_PRIORITIES 可以为任意值,但是考虑到RAM 的消耗,宏configMAX_PRIORITIES最好设置为一个满足应用的最小值。当宏configUSE_TIME_SLICING 定义为1 的时候多个任务可以共用一个优先级,数量不限。默认情况下宏configUSE_TIME_SLICING 在文件FreeRTOS.h 中已经定义为1。
## 任务实现
使用FreeRTOS 的过程中,我们要使用函数xTaskCreate()或xTaskCreateStatic()来创建任务,第一个参数是pxTaskCode,就是这个任务的任务函数。
FreeRTOS 官方给出的任务函数模板如下:
void vATaskFunction(void *pvParameters) (1)
{
for( ; ; ) (2)
{
--任务应用程序-- (3)
vTaskDelay(); (4)
}
/* 不能从任务函数中返回或者退出, 从任务函数中返回或退出的话就会调用
configASSERT(),前提是你定义了configASSERT()。如果一定要从任务函数中退出的话那一定
要调用函数vTaskDelete(NULL)来删除此任务。*/
vTaskDelete(NULL); (5)
}
任务函数注意一下几点:
1.任务函数的返回类型一定要为void 类型,也就是无返回值,而且任务的参数也是void 指针类型的!任务函数名可以根据实际情况定义。
2.任务的具体执行过程是一个大循环,for(; ; )就代表一个循环,作用和while(1)一样。
3.任务函数一般不允许跳出循环,如果一定要跳出循环的话在跳出循环以后一定要调用函数vTaskDelete(NULL)删除此任务!FreeRTOS 的任务函数和UCOS 的任务函数模式基本相同的
```
任务控制块
每个任务有一些属性需要保存,把这些属性集合到一起的结构体是任务控制块。TCB_t.
在使用函数xTaskCreate()创建任务的时候就会自动的给每个任务分配一个任务控制块。
此结构体在文件tasks.c 中有定义,如下:
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; //任务堆栈栈顶
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings; //MPU 相关设置
#endif
ListItem_t xStateListItem; //状态列表项
ListItem_t xEventListItem; //事件列表项
UBaseType_t uxPriority; //任务优先级
StackType_t *pxStack; //任务堆栈起始地址
char pcTaskName[ configMAX_TASK_NAME_LEN ];//任务名字
#if ( portSTACK_GROWTH > 0 )
StackType_t *pxEndOfStack; //任务堆栈栈底
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting; //临界区嵌套深度
#endif
#if ( configUSE_TRACE_FACILITY == 1 ) //trace 或到debug 的时候用到
UBaseType_t uxTCBNumber;
UBaseType_t uxTaskNumber;
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; //任务基础优先级,优先级反转的时候用到
UBaseType_t uxMutexesHeld; //任务获取到的互斥信号量个数
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) //与本地存储有关
void
*pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
#endif
#if( configGENERATE_RUN_TIME_STATS == 1 )
uint32_t ulRunTimeCounter; //用来记录任务运行总时间
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
struct _reent xNewLib_reent; //定义一个newlib 结构体变量
#endif
#if( configUSE_TASK_NOTIFICATIONS == 1 ) //任务通知相关变量
volatile uint32_t ulNotifiedValue; //任务通知值
volatile uint8_t ucNotifyState; //任务通知状态
#endif
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
//用来标记任务是动态创建的还是静态创建的,如果是静态创建的此变量就为pdTURE,
//如果是动态创建的就为pdFALSE
uint8_t ucStaticallyAllocated;
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
} tskTCB;
//新版本的FreeRTOS 任务控制块重命名为TCB_t,但是本质上还是tskTCB,主要是为了兼容
//旧版本的应用。
typedef tskTCB TCB_t;
阻塞延时
RTOS 中的延时叫阻塞延时,即任务需要延时的时候, 任务会放弃 CPU 的使用权, CPU 可以去干其它的事情,当任务延时时间到,重新获取 CPU 使用权, 任务继续运行,这样就充分地利用了 CPU 的资源,而不是干等着。
阻塞延时的阻塞是指任务调用该延时函数后, 任务会被剥离 CPU 使用权,然后进入阻塞状态,直到延时结束, 任务重新获取 CPU 使用权才可以继续运行。在任务阻塞的这段时间, CPU 可以去执行其它的任务,如果其它的任务也在延时状态,那么 CPU 就将运行空闲任务。
通过时钟TICK判断是否在延时的状态,如果当前任务1延时,就切换到任务2中执行,达到看起来是两个任务是在同时执行的效果。
任务挂起和恢复
1.void xTaskSuspend();
函数形式:
xTaskToSuspend为函数句柄。
void vTaskSuspend( TaskHandle_t xTaskToSuspend)
参数:
xTaskToSuspend: 要挂起的任务的任务句柄,创建任务的时候会为每个任务分配一个任务句柄。如果使用函数xTaskCreate()创建任务的话那么函数的参数pxCreatedTask 就是此任务的任务句柄,如果使用函数xTaskCreateStatic()创建任务的话那么函数的返回值就是此任务的任务句柄。也可以通过函数xTaskGetHandle()来根据任务名字来获取某个任务的任务句柄。注意!如果参数为NULL 的话表示挂起任务自己。
返回值:
无。
2.vTaskResume();
形式:
```c
void vTaskResume( TaskHandle_t xTaskToResume)
```
参数:
xTaskToResume: 要恢复的任务的任务句柄。
3、函数xTaskResumeFromISR()
此函数是vTaskResume()的中断版本,用于在中断服务函数中恢复一个任务。
形式:
```c
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume)
```
参考:
https://blog.csdn.net/tichimi3375/article/details/80671840
``