初始化寄存器
假设定时器的定时中断为1ms(可以根据需求改为任意值),与操作系统一致,中断过于频繁效率就低,中断太长,实时性差;定义相关数值
#define TASK_NUM (3) // 这里定义的任务数为3,表示有三个任务会使用此定时器定时。 uint16 TaskCount[TASK_NUM] ; // 这里为三个任务定义三个变量来存放定时值。 uint8 TaskMark[TASK_NUM]; // 同样对应三个标志位,为0表示时间没到,为1表示定时时间到。添加中断服务
定时中断服务函数,在中断中逐个判断,如果定时值为0,表示没有使用此定时器或此定时器已经完成定时,不着处理。否则定时器减一,直到为零时,相应标志位值1,表示此任务的定时值到了。 /************************************************************************************** * FunctionName : TimerInterrupt() * Description : 定时中断服务函数 * EntryParameter : None * ReturnValue : None **************************************************************************************/ void TimerInterrupt(void) { uint8 i; for (i=0; i<TASKS_NUM; i++) { if (TaskCount[i]) { TaskCount[i]--; if (TaskCount[i] == 0) { TaskMark[i] = 0x01; } } } }添加切换代码
在我们的应用程序中,在需要的应用定时的地方添加如下代码,以任务1为例: TaskCount[0] = 20; // 延时20ms TaskMark[0] = 0x00; // 启动此任务的定时器 到此我们只需要在任务中判断TaskMark0是否为0x01即可,其他任务添加相同,至此一个定时器的复用问题就实现了。通过上面对1个定时器的复用我们可以看出,在等待一个定时的到来的同时我们可以循环判断标志位,同时也可以去执行其他函数。设计结构体
// 任务结构 typedef struct _TASK_COMPONENTS { uint8 Run; // 程序运行标记:0-不运行,1运行 uint8 Timer; // 计时器 uint8 ItvTime; // 任务运行间隔时间 void (*TaskHook)(void); // 要运行的任务函数 } TASK_COMPONENTS; // 任务定义添加服务函数
/************************************************************************************** * FunctionName : TaskRemarks() * Description : 任务标志处理 * EntryParameter : None * ReturnValue : None **************************************************************************************/ void TaskRemarks(void) { uint8 i; for (i=0; i<TASKS_MAX; i++) // 逐个任务时间处理 { if (TaskComps[i].Timer) // 时间不为0 { TaskComps[i].Timer--; // 减去一个节拍 if (TaskComps[i].Timer == 0) // 时间减完了 { TaskComps[i].Timer = TaskComps[i].ItvTime; // 恢复计时器值,从新下一次 TaskComps[i].Run = 1; // 任务可以运行 } } } }修改任务处理函数
/************************************************************************************** * FunctionName : TaskRemarks() * Description : 任务标志处理 * EntryParameter : None * ReturnValue : None **************************************************************************************/ void TaskRemarks(void) { uint8 i; for (i=0; i<TASKS_MAX; i++) // 逐个任务时间处理 { if (TaskComps[i].Timer) // 时间不为0 { TaskComps[i].Timer--; // 减去一个节拍 if (TaskComps[i].Timer == 0) // 时间减完了 { TaskComps[i].Timer = TaskComps[i].ItvTime; // 恢复计时器值,从新下一次 TaskComps[i].Run = 1; // 任务可以运行 } } } }定义结构体
/************************************************************************************** * Variable definition **************************************************************************************/ static TASK_COMPONENTS TaskComps[] = { {0, 60, 60, TaskDisplayClock}, // 显示时钟 {0, 20, 20, TaskKeySan}, // 按键扫描 {0, 30, 30, TaskDispStatus}, // 显示工作状态 // 这里添加你的任务。。。。 }; 在定义变量时,我们已经初始化了值,这些值的初始化,非常重要,跟具体的执行时间优先级等有关系,这个需要自己掌握。 上述的定义显示:我们有三个任务,每1秒执行以下时钟显示,因为我们的时钟最小单位是1s,所以在秒变化才显示一次足够了;由于按键在按下时会参数抖动,而我们知道一般按键的抖动大概是20ms,那么我们在顺序执行的函数中一般是延伸20ms,而这里我们每20ms扫描一次,是非常不错的处理,既达到了消抖的目的,也不会漏掉按键输入;为了能够显示按键后的其他提示和工作界面,我们这里设计每30ms显示一次,如果觉得反应慢了,可以让这些值小一点。后面的名称是对应的函数名,你必须在应用程序中编写这函数名称和这三个一样的任务。创建任务列表
这里定义的任务清单的目的其实就是参数TASK_MAX的值,其他值是没有具体的意义的,只是为了清晰的表面任务的关系而已。 // 任务清单 typedef enum _TASK_LIST { TAST_DISP_CLOCK, // 显示时钟 TAST_KEY_SAN, // 按键扫描 TASK_DISP_WS, // 工作状态显示 // 这里添加其他任务。。。。 TASKS_MAX // 总的可供分配的定时任务数目 } TASK_LIST编写任务函数
/************************************************************************************** * FunctionName : TaskDisplayClock() * Description : 显示任务 * EntryParameter : None * ReturnValue : None **************************************************************************************/ void TaskDisplayClock(void) { } /************************************************************************************** * FunctionName : TaskKeySan() * Description : 扫描任务 * EntryParameter : None * ReturnValue : None **************************************************************************************/ void TaskKeySan(void) { } /************************************************************************************** * FunctionName : TaskDispStatus() * Description : 工作状态显示 * EntryParameter : None * ReturnValue : None **************************************************************************************/ void TaskDispStatus(void) { } // 这里添加其他任务。。。。。。。。。修改主函数
关键注意点:TaskCompsi.TaskHook(); 如果执行时间较长,就会影响轮询调度的周期,导致计时周期出问题。一定要确保函数的执行时间,小于运行任务时间间隔。 /************************************************************************************** * FunctionName : main() * Description : 主函数 * EntryParameter : None * ReturnValue : None **************************************************************************************/ int main(void) { InitSys(); // 初始化 while (1) { TaskProcess(); // 任务处理 } }