固定优先级抢占式调度:
- 每个任务都被赋予了一个优先级,该优先级只能被任务修改,不能被内核本身修改
- 每个任务都可以存在于一个或多个状态
- 在任何时候都只有一个任务可以处于运行状态
- 调度器总是在处于就绪态的任务中选择具有最高优先级的任务来执行
抢占式:是指任务进入就绪态或者优先级被修改时,如果处于运行态的任务优先级更低,那么该任务总是抢占当前运行任务。
调度策略:
1、单调速率调度:根据任务周期性执行速率来分配一个唯一的优先级,具有最高周期执行频率的任务赋予高优先级。
- 可以最大化整个应用程序的可调度性
- 由于运行时间不定和并非所有任务都具有周期性,导致全面计算复杂
2、协作式调度:(FreeRTOS)
只能在运行态任务进入阻塞态或者运行态任务显示调用taskYIELD()时,才进行上下文切换,任务永远不会被抢占,而具有相同优先级的任务也不会自动共享处理器时间。
- 操作简单
- 导致系统响应不够快
=============================================================================================================
队列管理
读队列时阻塞:任务试图读一个队列时,可以指定阻塞超时时间。
如果队列为空,任务保持阻塞状态知道队列数据有效。当队列被写入数据,该任务将自动从阻塞态转为就绪态。
当等待时间超过了阻塞时间,即使队列中无数据,任务也自动从阻塞态转换为就绪态。
写队列时阻塞:指定阻塞超时时间,当写队列已满时,任务进入阻塞态以等待队列空间有效的最长时间。
备注:在FreeRTOS中,千万不要在中断服务程序中调用xQueueSendToFront()或者xQueueSendToBack(),而是调用xQueueSendToFrontFromISR()或者xQueueSendToBackFromISR()。
如果队列存储的数据单元尺寸比较大,最好利用队列来传递数据的指针,而不是对数据本身在队列上的一个字节一个字节的拷贝。注意以下两点:
1、指针指向内存空间的所有权必须明确,保证不会有任意两个任务同时修改共享内存中的数据。
原则上,共享内存其指针发送到队列之前,其内容只允许发送任务访问;
共享内存其指针从队列被读出之后,其内容只允许接收任务访问;
2、指针指向的内存空间必须有效
若指针指向的内存空间是动态分配,应该只有一个任务负责对其内存释放,释放后,不应有任务访问这段空间。
切忌用指针访问任务栈上分配的空间,因为当栈帧改变后,栈上的数据将不再有效。
=====================================================================================================================
中断管理
FreeRTOS中,0为最低优先级;
FreeRTOS中,只有"FromISR"结束的API函数或者宏才可以在中断服务程序中。
信号量用途:
1、事件计数:
计数值初始化为0,信号量的计数值是已发生的事件的数目与已处理的事件的数目之间的差值
2、资源管理:
计数值初始化为可用资源总数,用于表示可用资源的数目。
一个任务要获取资源的控制权,必须先获得信号量,使信号量的计数值减1。计数值0表示无可用资源。
任务使用资源结束后,将计数值加1.
使用任务事件组实现看门狗
思路:
定义监视的任务事件标志组位
/**
* @brief 最多一次监测MAX_TASK_NUM个任务,如果多于该数,则需要定义多个事件标志组
*/
#define WDG_BIT_DOWN_TASK_1 (1 << 0)
#define WDG_BIT_DOWN_TASK_2 (1 << 1)
#define WDG_BIT_DOWN_TASK_3 (1 << 2)
#define WDG_BIT_DOWN_TASK_4 (1 << 3)
#define WDG_BIT_TASK_ALL ( WDG_BIT_DOWN_TASK_1 | WDG_BIT_DOWN_TASK_2 | WDG_BIT_DOWN_TASK_3 | WDG_BIT_DOWN_TASK_4 )
监视任务
监视任务负责在规定时间内检测个事件标志组位,已达到监测其他任务运行的目的
/**
* @brief 看门狗任务
* @param argument
* @retval None
*/
void vTaskWDG(void * argument)
{
static TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS*10; /* 最大延迟1s */
EventBits_t uxBits;
/* 创建事件标志组 */
xCreatedEventGroup = xEventGroupCreate();
if(xCreatedEventGroup == NULL) {
/* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
return;
}
while(1){
/* 等待所有任务发来事件标志 */
uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件标志组句柄 */
WDG_BIT_TASK_ALL, /* 等待WDG_BIT_TASK_ALL被设置 */
pdTRUE, /* 退出前WDG_BIT_TASK_ALL被清除,这里是TASK_BIT_ALL都被设置才表示“退出”*/
pdTRUE, /* 设置为pdTRUE表示等待TASK_BIT_ALL都被设置*/
xTicksToWait); /* 等待延迟时间 */
if((uxBits & WDG_BIT_TASK_ALL) == WDG_BIT_TASK_ALL){
vWDG_Feed();
}
else {
/* 通过变量uxBits简单的可以在此处检测那个任务长期没有发来运行标志 */
}
}
}
这样,在其他任务中,不断调用xEventGroupSetBits,给相应的位进行置位即可!一旦有任务没有正常置位,则该任务停止喂狗!
当然,这样就出现了一个问题:有的任务可能需要阻塞相当长的时间,这个时间已经远远超过了看门狗的时限。又或者说,用户手动挂起了一个任务,看门狗必须暂停监视该任务。
解决这个问题也很简单,只需要稍微更改一下看门狗任务即可:
- 首先,必须有函数可以同步看门狗任务,处理任务挂起、恢复问题。
- 其次,对于长时间等待任务,看门狗任务可以以固定频率喂狗,在规定的最大时间到时,检测所有事件,如果这时还有没有置位的事件,则认为出错!