从单片机初学者迈向单片机工程师—完整 - 图文 下载本文

浪费,事实也是如此,绝大多数简单任务,CPU都是在“空转” (循环踏步延时) 。对任务总 结还可以知道,很多任务需要 CPU 不断“关照” ,其实这种“不断”也是有极限的,比如数 码管动态扫描,能够做到40Hz 就可以了,又如键盘扫描,能够做到20Hz(经验值),基本上 也就不会丢有效按键键值了,再如LCD刷新,我觉得做到 10Hz 就可以了,等等。看来,绝 大多数任务都是工作在低速频度。而我们的CPU一旦运行起来,速度又很快,CPU本身就是 靠很快的速度执行很简单的指令来胜任复杂的任务(逻辑)的。如果有办法把“快”的 CPU 分成多个慢的CPU,然后给不同的任务分配不同速度的CPU,这种设想是不是很好呢!确实 很好,下面就看如何将“快”的CPU划分成多个“慢”的 CPU。

根据这种想法,我们需要合理分配CPU资源来“关照”不同的任务,最好能够根据任务 本身合理占用CPU资源,首先看如图 3 所示的流程图,各个任务流程独立,各任务通过全局 变量来交互信息,在流程中有一个重要的模块“任务切换”,就是任务切换模块实现 CPU 合 理分配,这个任务切换模块是怎么实现的呢?

(原文件名:2.JPG)

图3 多任务复杂流程图

首先需要理解,CPU 一旦运行起来,就无法停止(硬件支持时钟停止的不在这里讨论), 谁能够控制一批脱缰的马呢?对了,有中断,中断能够让CPU回到特定的位置,设想,能不 能用一个定时中断,周期性的将 CPU这匹运行着的脱缰的马召唤回来,重新给它安排特定的 任务,事实上,任务切换就是这样实现的。

(原文件名:3.JPG)

图 4 定时中断实现任务切换

如图 4A 所示,CPU 在空闲任务循环等待,定时中断将 CPU 周期性唤回,根据任务设计 了不同的响应频度,满足条件的任务将获得CPU资源,CPU为不同任务“关照”完成后,再 次返回空闲任务,如此周而复始,对于各个任务而言,好像各自拥有一个独立的CPU,各自 独立运行。用这种思想构建的程序框架,最大的好处是任务很容易裁剪,系统能够做得很复 杂。

在充分考虑单片机中断特性(在哪里中断就返回到哪里)后,实际可行的任务切换如图 4B所示,定时中断可能发生在任务调度,随机任务执行的任何时候,图中最大的框框所示, 不管中断在何时发生,它都会正常返回,定时中断所产生的影响只在任务调度模块起作用, 即依次让不同的任务按不同的节拍就绪。任务调度会按一定的优先级执行就绪任务。 总结不同的任务需要CPU关照的频度,选择最快的那个频度来设定定时器中断的节拍, 一般选择 200Hz,或者 100Hz 都可以。另外再给每个任务设定一个节拍控制计数器 C,也就 是定时器每中断多少次后执行任务一次。例如取定时中断节拍为 200Hz,给任务设定的 C=10, 则任务执行频度为 200/10=20Hz,如果是数码管扫描,按 40Hz 不闪烁规律,则任务节拍控制 计数器 C=5 即可。在程序设计中,C 代表着任务运行的节拍控制参数,我们习惯用 delay 来 描述,不同的任务用task0,task1……来描述。 明天继续写如何用代码实现!2009-6-29 下面我们来用代码实现以上多任务程序设计思想。 首先是任务切换 while(1) {

if(task_delay[0]==0) task0(); //task0就绪, if(task_delay[1]==0) task1(); //task1就绪, …… }

很显然,执行任务的条件是任务延时量task_delay=0,那么任务延时量谁来控制呢?定时 器啊!定时器中断对任务延时量减一直到归零,标志任务就绪。当没有任务就绪时,任务切 换本身就是一个Idle 任务。 void timer0(void) interrupt 1 {

if(task_delay[0]) task_delay[0]--; if(task_delay[1]) task_delay[1]--; …… }

例如 timer0 的中断节拍为 200Hz,task0_delay 初值为 10,则 task0()执行频度为 200/10=20Hz。

有了以上基础,我们来设计一个简单多任务程序,进一步深入理解这种程序设计思想。 任务要求:用单片机不同 IO 脚输出 1Hz,5Hz,10Hz,20Hz 方波信号,这个程序很短,将 直接给出。 #include \

#define TIME_PER_SEC 200 //定义任务时钟频率,200Hz #define CLOCK 22118400 //定义时钟晶振,单位Hz #define MAX_TASK 4 //定义任务数量

extern void task0(void); //任务声明 extern void task1(void); extern void task2(void); extern void task3(void);

sbit f1Hz = P1^0; //端口定义

sbit f5Hz = P1^1; sbit f10Hz = P1^2; sbit f20Hz = P1^3;

unsigned char task_delay[4]; //任务延时变量定义

//定时器0初始化 void timer0_init(void) {

unsigned char i;

for(i=0;i