时间触发系统调度器思想LED闪烁简例
什么是调度器?
可以从两种角度来看调度器:
1、 调度器可以看成是一个简单的操作系统,允许以周期性或(更少见)单次方式来调用任务。
2、 从底层角度来看,调度器可以看作是一个由许多不同任务共享的定时器中断服务程序。因此,只需要初始化一个定时器,而且改变定时的时候只需要改变一个函数。此外,无论需要运行1个、10个还试100个不同的任务,通常可以使用同一个调度器来完成。这种“功用中断服务程序”与桌面操作系统提供的共用打印功能非常相似。
调度器构成
l 调度去数据结构
l 初始化函数
l 中断服务程序(ISR),用来以一定的间隔刷性调度器。
l 向调度器增加任务的函数。
l 使任务在应当运行的时候被执行的调度函数。
l 从调度器删除任务的函数。
使用场合
l 用8051系列微处理器开发一种嵌入式系统。
l 该系统使用调度器构造一个时间触发结构。
l 事件触发系统:在嵌入式系统中,事件触发行为往往通过使用中断实现。就桌面系统而言,各种各样的的应用程序在运行中必须对诸如单击鼠标或移动鼠标的事件作出反应。用户希望这样的事件将引起“即时的”响应。
l 时间触发系统:理解与事件触发系统之间的区别:医院里的一个医生,必须在一些护理人员的帮助下,通宵照管10哥严重病人:安排护理人员在每个病人出现严重问题时唤醒她,这就是“事件触发”解决方案。把闹钟设置为每小时闹铃。当闹钟闹铃时,起床依次去探访每一个病人,检查他们是否舒服,如果需要的话,则给予治疗。这就是“时间触发”解决方案。
/********************************************************************************/
/******************************数据结构******************************************/
typedef tWord unsigned int;
typedef tByte unsigned char;
//可能的话,存储在DATA区,以供快速存取
//每个任务的存储总和是7个字节
typedef data struct
{
//指向任务的指针,必须是一个void(void)函数
void (code *pTask)(void);
//延迟(时标)直到函数将(下一次)运行
tWord Delay;
//连续运行之间的间隔(时标)
tWord Period;
//当任务需要运行时(由调度器)加1
tByte RunMe;
}sTask;
#define SCH_MAX_TASKS (3)
//定义创建任务队列
sTask SCH_tasks_G[SCH_MAX_TASKS];
/***************************初始化函数*******************************************/
/********************************************************************************/
/*SCH_Init_T2()。调度器初始化函数,准备调度器数据结构并且设置定时器以所需的频率 */
/*中断,必须在使用调度器之前调用这个函数,定时溢出初始化时标间隔为1ms */
/********************************************************************************/
void SCH_Init_T2(void)
{
TBYTE i;
for (i = 0; i < SCH_MAX_TASKS; i++)
{
SCH_Delete_Task(i);
}
//复位全局错误变量,SCH_Delete_Task将产生一个错误代码(因为任务队列是空的)
Error_Code_G = 0;
//设置定时器2,自动重装、16位定时功能。
//晶振假定为12MHz,定时器2的精度是1us,要求的定时器2溢出为1ms
//需要1000个定时器时标,重装值为65536-1000 = 64536 = 0xFC18
//代码略....
}
/***************************刷新函数*********************************************/
/********************************************************************************/
/*SCH_Updata()这是调度器的中断服务程序,初始化函数中的定时器决定了它的调用频率 */
/*这个版本由定时器2中断触发,定时器自动重装 */
/********************************************************************************/
void SCH_Update(void) interrupt INTERRUPT_Timer_2_Overflow
{
tByte Index;
TF2 = 0;//必须手工清除
//注意计算单位为"时标"(不是毫秒)
for (Index = 0; Index < SCH_MAX_TASKS; Index++)
{
//检测这里是否有任务
if (SCH_tasks_G[Index].pTask)
{
if (SCH_tasks_G[Index].Delay == 0)
{
//任务需要运行
SCH_tasks_G[Index].RunMe += 1;//RunMe标志加1
if (SCH_tasks_G[Index].Period)
{
//调度周期性的任务再次运行
SCH_tasks_G[Index].Delay = SCH_tasks_G[Index].Period;
}
else
{
//还没准备好运行,延迟减1
SCH_tasks_G[Index].Delay -= 1;
}
}
}
}
}
/***************************添加任务函数*****************************************/
/********************************************************************************/
/*SCH_Add_Task()使任务(函数)每隔一定间隔或在用户定义的延迟之后执行 */
/*参数:pFunction需要调度的函数名称 */
/* Delay任务第一次执行前的延迟,设置为0任务立即执行 */
/* Period任务重复运行的时标间隔,如果为0任务只执行一次 */
/********************************************************************************/
tByte SCH_Add_Task(void (code * pFunciton)[], const tWord Delay, const tWord PERIDO)
{
tByte Index = 0;
//首先在队列中找到一个空隙(如果有的话)
while ((SCH_tasks_G[Index].pTask != 0) && (Index < SCH_MAX_TASKS))
{
Index++;
}
//是否已经到达队列的结尾?
if (Index == SCH_MAX_TASKS)
{
//任务队列已满
//设置全局错误变量
Error_code_G = ERROR_SCH_TOO_MANY_TASKS;
//同时返回错误代码
return SCH_MAX_TASKS;
}
//如果能脑诵说这里说明任务队列有空间
SCH_tasks_G[Index].pTask = pFunciton;
SCH_tasks_G[Index].Delay = Delay;
SCH_tasks_G[Index].Period = PERIDO;
SCH_tasks_G[Index].RunMe = 0;
return Index;//返回任务的位置(以便以后删除)
}
/***************************"调度"程序函数***************************************/
/********************************************************************************/
/*SCH_Dispatch_Task()这是"调度程序",当任务(函数需要运行时)SCH_Dispatch_Task()*/
/*将运行它。这个函数必须被主循环(重复)调用 */
/********************************************************************************/
void SCH_Dispatch_Task(void)
{
tByte Index;
//调度运行下一个任务(如果有任务就绪)
for (Index = 0; Index < SCH_MAX_TASKS; Index++)
{
if (SCH_tasks_G[Index].RunMe > 0)
{
*SCH_tasks_G[Index].pTask(); //执行任务
SCH_tasks_G[Index].RunMe -= 1; //复位/减小RunMe标志
//周期性的任务将自动再次运行,如果是单次任务,则将它从队列中删除
if (SCH_tasks_G[Index].Period == 0)
{
SCH_Delete_Task(Index);
}
}
}
//报告系统状况
SCH_Report_Status();
//这里调度器进入空闲模式
SCH_Go_To_Sleep();
}
/***************************删除任务函数*****************************************/
/********************************************************************************/
/*SCH_Delete_Task()删除任务函数 */
/********************************************************************************/
bit SCH_Delete_Task(const tByte TASK_INDEX)
{
bit Return_code;
if (SCH_tasks_G[Index].pTask == 0)
{
//这里没有任务
//设置全局变量错误
Error_code_G = ERROR_SCH_CANNOT_DELETE_TASK;
Return_code = RETURN_ERROR;
}
else
{
Return_code = RETURN_NORMAL;
}
SCH_tasks_G[Index].pTask 0;
SCH_tasks_G[Index].Delay = 0;
SCH_tasks_G[Index].Period = 0;
SCH_tasks_G[Index].RunMe = 0;
return Return_code;
}
/***************************降低功耗函数*****************************************/
/********************************************************************************/
/*软件控制进入空闲模式,而当微处理器收到任何中断时返回正常运行模式 */
/********************************************************************************/
void SCH_Go_To_Sleep()
{
PCON |= 0X01; //进入空闲模式
}
/***************************报告错误函数*****************************************/
/********************************************************************************/
/*此函数由刷新函数调用,用来报告这些错误代码 */
/********************************************************************************/
void SCH_Report_Status(void)
{
#if SCH_REPORT_ERRORS
//只在需要报告错误时适用
//检查新的错误代码
//代码略
#endif
}
/***************************开始函数*********************************************/
/********************************************************************************/
/*在所有的任务都被添加之后,将调用这个函数来开始调度过程 */
/********************************************************************************/
void SCH_Start(void)
{
EA = 1;
}
int main(int argc, char* argv[])
{
//设置调度器
SCH_Init_T2();
//为Flash_LED任务做准备
LED_Flash_Init();
//增加FLash LED任务(1000ms亮,1000ms灭)
//定时单位为时标(1ms间隔)
SCH_Add_Task(LED_Flash_Update, 0, 1000);
//开始调度器
SCH_Start();
while (1)
{
SCH_Dispatch_Task();
}
}
使用简例