打印

一个简单易懂的程序架构

[复制链接]
3613|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 nefudongfang 于 2013-9-17 10:17 编辑

将一个任务分成若干段,确保每段需要CPU  关照时长小于定时器  中断节拍长,这样CPU
在处理这些长任务时,就不会影响到其它任务的执行。   

Easy51RTOS   正是基于以上程序设计思想,总结完善后提出一种耗费资源特别少并且不使
用堆栈的多线程操作系统,这个操作系统以纯C语言实现,无硬件依赖性,需要单片机的资
源极少。起名为  Easy51RTOS,特别适合初学者学习使用。有任务优先级,通过技巧可以
任务间延时,缺点是高优先级任务不具有抢占功能,一个具有抢占功能的操作系统,一定要
涉及到现场保护与恢复,需要更多的  RAM  资源,涉及到堆栈知识,文件系统将很复杂,初
学者学习难度大。为了便于初学者学习,将代码文件压缩至  4  个文件。   
Easy51RTOS.Uv2  Keil工程文件,KEIL用户很熟悉的   
main.c        main函数和用户任务  task  函数文件   
os_c.c        Easy51RTOS相关函数文件   
os_cfg.h        Easy51RTOS相关配置参数头文件   
文件解读如下:   
os_cfg.h   
#include  "reg51.h"   
#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);   
extern  unsigned  char  task_delay[MAX_TASK];   
extern  void  run(void  (*ptask)());   
extern  void  os_timer0_init(void);   

os_c.c 文件  
#include  "os_cfg.h"   
unsigned  char  task_delay[MAX_TASK];    //定义任务延时量变量   
//定时器0初始化   
void  os_timer0_init(void)   
{   
        unsigned  char  i;   
        for(i=0;i<MAX_TASK;i++)  task_delay=0;   
        TMOD  =  (TMOD  &  0XF0)  |  0X01;                   //定时器  0工作在模式  1,16Bit  定时器模式      
        TH0  =  255-CLOCK/TIME_PER_SEC/12/256;     //CRY_OSC,TIME_PER_SEC在  os_cfg.h中定义   
        TL0  =  255-CLOCK/TIME_PER_SEC/12%256;        
        TR0  =1;      
        ET0  =1;                  //开启定时器和中断   
}   
   
//  系统  OS定时中断服务   
void  os_timer0(void)  interrupt  1   
{   
        unsigned  char  i;   
        TH0  =  255-CLOCK/TIME_PER_SEC/12/256;   
        TL0  =  255-CLOCK/TIME_PER_SEC/12%256;                  
        for(i=0;i<MAX_TASK;i++)  if(task_delay)  task_delay--;      //每节拍对任务延时变量减1  ,减至  0    后,任务就绪。        
}         
//指向函数的指针函数   
void  run(void  (*ptask)())   
{      
      (*ptask)();      
}      
main.c  文件  
#include  "os_cfg.h"      
#define  TASK_DELAY0  TIME_PER_SEC/1            //任务执行频度为1Hz   
#define  TASK_DELAY1  TIME_PER_SEC/2            //任务执行频度为2Hz   
#define  TASK_DELAY2  TIME_PER_SEC/10            //任务执行频度为10Hz   
#define  TASK_DELAY3  TIME_PER_SEC/20          //任务执行频度为20Hz   
void  (*  code  task[])()  =  {task0,task1,task2,task3};    //获得任务PC指针   
   
sbit  LED0  =  P1^0;     //演示用  LED接口定义   
sbit  LED1  =  P1^1;   
sbit  LED2  =  P1^2;   
sbit  LED3  =  P1^3;   
   
/*main主函数*/   
void  main(void)   
{   
        unsigned  char  i;        
        os_timer0_init();      //节拍发生器定时器初始化   
        EA  =  1;                  //开总中断   
         
      while(1)   
      {   
          for(i=0;i<MAX_TASK;i++)      
          if  (task_delay==0)  {run(task);  break;}    //就绪任务调度   
      }    //上一行  break有特殊作用,详细解释见后文   
}   

void  task0(void)    //任务  0   
{         
        LED0  =  !LED0;        
        task_delay[0]  =  TASK_DELAY0;   
}   
   
void  task1(void)    //任务  1   
{         
        LED1  =  !LED1;        
        task_delay[1]  =  TASK_DELAY1;   
}   
   
void  task2(void)    //任务  2   
{         
        LED2  =  !LED2;        
        task_delay[2]  =  TASK_DELAY2;   
}   
   
void  task3(void)    //任务内分段设计      
{         
     static  unsigned  char  state=0;    //定义静态局部变量   
  switch  (state)   
  {   
    case  0:   
      LED3  =  !LED3;        
      state  =  1;   
      task_delay[3]  =  TASK_DELAY3;   
      break;   
   
    case  1:   
      LED3  =  !LED3;        
      state  =  2;   
      task_delay[3]  =  TASK_DELAY3*2;   
      break;   
            
    case  2:   
      LED3  =  !LED3;        
      state  =  0;   
      task_delay[3]  =  TASK_DELAY3*4;   
      break;   
   
    default:   
      state  =  0;   
      task_delay[3]  =  TASK_DELAY3;   
      break;   
  }      
仿真如下图


主程序巧妙实现优先级设定:   
for(i=0;i<MAX_TASK;i++)        
if  (task_delay==0)  {run(task);  break;}    //就绪任务调度   
这里的  break  将跳出  for  循环,使得每次重新任务调度总是从  task0  开始,就意味着优先  
级高的任务就绪会先执行。这样task0具有最高优先级,task1、task2、task3优先级依次降低。   
特别是  void  task3(void)用  switch(state)状态机实现了任务分段,这也是任务内系统延时的  
一种方法。   
   

以上内容选自《新手迈向工程师》,有兴趣者可通读全文。  

相关帖子

沙发
ayb_ice| | 2013-9-17 09:44 | 只看该作者
直接循环调用还简单点,各个任务自己保证不死占CPU即可

使用特权

评论回复
板凳
ranen| | 2013-9-17 09:57 | 只看该作者
:)不错不错

使用特权

评论回复
地板
ygl968| | 2013-9-17 10:07 | 只看该作者
收藏学习

使用特权

评论回复
5
kingxw| | 2013-9-17 12:03 | 只看该作者
学习了

使用特权

评论回复
6
逍遥派掌门| | 2013-9-17 15:32 | 只看该作者
这种方法在只有简单逻辑的时候使用还可以。

这种方法里,任务的切换是被动的,可能会打断其中的原子操作,从而产生意外。

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
nefudongfang + 1 赞一个!
7
anjf163| | 2013-9-17 22:27 | 只看该作者
这种方法太简单了,只适合做简单的程序。

表面上看似乎能执行独立的任务,实际上只是函数延时执行而已。

建议增加完整的现场保护和恢复函数,就可以将每个任务独立执行了。

使用特权

评论回复
8
GZZXB| | 2013-9-17 22:32 | 只看该作者
新手迈向工程师 全文在哪?

使用特权

评论回复
9
sunhq02| | 2013-9-18 13:46 | 只看该作者
这个思路对于 51来说还是有一定用处的
但是这个实现太过简单了, 很多问题都无法避免
建议你参看
时间触发嵌入式系统设计模式-8051系列微控制器开发可靠应用(patterns for time-triggered embedded systems-building reliable applications with the 8051 family of microcontrollers)
这本书

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
nefudongfang + 1
10
Super_| | 2013-9-19 22:37 | 只看该作者
看应用场合了。

使用特权

评论回复
11
xlsbz| | 2013-9-19 23:49 | 只看该作者
差的代码

使用特权

评论回复
12
nefudongfang|  楼主 | 2013-9-22 09:27 | 只看该作者
xlsbz 发表于 2013-9-19 23:49
差的代码

可否有好的代码,分享一下?

使用特权

评论回复
13
ayb_ice| | 2013-9-22 09:58 | 只看该作者
直接上RTX TINY明显比这强

使用特权

评论回复
14
xlsbz| | 2013-9-25 12:07 | 只看该作者
nefudongfang 发表于 2013-9-22 09:27
可否有好的代码,分享一下?


51裸奔,不同时刻的控制不碰撞,落到同一时间轴上就可以看出来。

呵呵  我也没啥好代码。你的应该也不错  我没仔细看  就盲目下结论了 呵呵  


使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
nefudongfang + 1
15
xlsbz| | 2013-9-25 12:17 | 只看该作者
nefudongfang 发表于 2013-9-22 09:27
可否有好的代码,分享一下?

呵呵  看了 挺不错了 适合当裸奔和操作系统之间的过度例子讲解用。
但实际场合 还是裸奔比较好一点。搞成你这种分析的不清楚的话,使用者水平不高的话, 容易出问题。低优先级容易运行不到。

使用特权

评论回复
16
Schvian| | 2013-9-25 13:29 | 只看该作者
还51呢,直接上ARM吧,ARM都白菜价了:lol

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

22

帖子

1

粉丝