时间触发和状态机思想的嵌入式系统

时间:2021-12-04 19:23:42

今天我给大家讲一下:
      时间触发的嵌入式系统 :

      我先给大家了一个程序:这个程序是在PC机上测试过,大家给点意见!
   #include "stdio.h"  #define TASKmax   5
   typedef unsigned long   u32;
   typedef unsigned int    u16;
    typedef unsigned char   u8;  
struct Task     
         
          void (*Ptask)(void);     
        u8 delay;        
       u8 period;      
        u8 Runme;      
  };
struct Task task[TASKmax];
u8   Task_G;
u8   Task1_time= 10;//10ms
u8   Task2_time= 20;
void task1(void)
       
int i=0;      
  for(i=0;i<2;i++)   
       
    printf("1\r\n");       
}}
void task2(void ){     
   char j=0;       
for(j=0;j<2;j++)    
    {        
printf("2\r\n");       
};}
u8 TASK_dele(u8 cn){  
      if(task[cn].Ptask ==0)        return 0;                task[cn].Ptask= 0x00;     
   task[cn].delay= 0;    
    task[cn].period= 0;      
  task[cn].Runme= 0;   
return 1;}
void Task_tr(void )
  
     u8 taskIndex;       
   for(taskIndex=0;taskIndex< TASKmax;taskIndex++)      
               
  if(task[taskIndex].Runme>0)       
          
         (*task[taskIndex].Ptask)();                                                 


           task[taskIndex].Runme--;         
                      
  if(task[taskIndex].period==0)                 TASK_dele(taskIndex);      
               
   }
u8 task_add(void (*function)(),
u8 delay,u8 period)
     
  u8 dex=0;     
   while((task[dex].Ptask!=0) && (dex< TASKmax))        dex++;       
if(dex==TASKmax)    return 0;        task[dex].Ptask= function;   
     task[dex].delay= delay;     
   task[dex].period= period;   
     task[dex].Runme= 1;   
     return 1;}
void Task_ref(void )//任务控制
       
u8 Task_tran;       
Task_tran=Task_G;    
    switch(Task_tran)    
    {             
   case  task11:                    //时间判断                    //                       break;              
  case  task12:                   //时间判断                    //如果到了就到下一个状态                   break;                default:break;      
  }
}
void main(){              
  //初始化定时器   控制标志位       
//初始化须要的变量    
    //增加任务      
  //while(1);  
  task_add(task1,10,1); 
   task_add(task2,30,2);     
        //Task_tr();定时器   控制标志位 
    }//以上是系统和基本思想,可以根据自己的程序更改。
stateMachine + timerTick + queue。

在RTOS环境下的多任务模型:
任务通常阻塞在一个OS调用上(比如从消息队列取数据)。
外部如果想让该任务运转,就要向消息队列发送消息。
任务收到消息时,根据当前状态,决定如何处理消息。这就是状态机。
任务将消息队列中的消息处理完毕后,重新进入阻塞状态。
任务在处理中,有时要延时一段时间,然后才继续工作:
    为了充分使用CPU,可以通过OS调用让其它任务去工作。
    OS通常会提供一个taskDelay调用。
    当任务调用taskDelay时,即进入阻塞状态,直到超时,才重新进入可工作状态(就绪状态)。

下面说说裸奔环境下的多任务模型:
裸奔也可以多任务,但调度是由用户自主控制。
在RTOS环境下,一般提供抢占式调度。在裸奔时,一般是任务在处理告一段落后,主动结束处理。
RTOS环境下的任务,一般处于一个while(1)循环中。
    while(1){
        从消息队列接收消息。如果没有,将阻塞。
        处理消息。
        
裸奔下的任务,一般采用查询方式:

    查询是否有待处理的事件。
    如果没有,返回。
    如果有,根据任务的当前状态,进行处理。处理完毕后,可能返回,也可能将待处理事件全部处理完毕后再返回。

裸奔任务其实也处于一个while(1)循环中,只不过这个循环在任务外部。
main()
{
    A_taskInit();        //任务的初始化
    B_taskInit();
    ...
    while(1){
        A_taskProc();    //任务的处理
        B_taskProc();
        


状态机既适用于OS环境,也适用于裸奔环境。
但在裸奔环境下,状态可能被切分得更细。例如后面讲的如何在裸奔环境实现taskDelay()。

消息队列既适用于OS环境,也适用于裸奔环境。
在OS环境下,消息队列机制由OS提供。
在裸奔环境下,消息队列要自己来实现。如果对队列的概念不清楚,可参考《数据结构》教材。
这个队列机制,可做成通用模块,在不同的程序中复用。
消息队列用于缓冲事件。事件不知道什么时候会到来,也不能保证来了就能迅速得到处理。
    使用消息队列,可以保证每个事件都被处理到,以及处理顺序。
一般在两种情况下会用到消息队列:
    存储外部事件:外部事件由中断收集,然后存储到队列。
        串口接收程序中的接收循环缓冲区,可理解为消息队列。
    任务间通讯:一个任务给其它任务发送消息。

timerTick,就是系统中的时钟基准。OS中总是有一个这样的基准。
在裸奔时,我们要用一个定时器(或RTC或watchdog)来建立这个时间基准。
一个tick间隔可以设置为10ms(典型RTOS的缺省设置)。让定时器10ms中断一次,中断发生时给tickNum++。
    以前,我在定时器中断中设置1S标志、200ms标志等等。时间相关的任务根据这些标志判断是否要执行。
    近来,一般让任务直接去察看tickNum。两次相减来判断定时是否到达。
    也可以在系统中建立一个通用定时器任务,管理与不同任务相关的多个定时器;在定时到达时,由定时器任务去调用相应的callback。
系统时钟基准是所谓“零耗时裸奔”的基础。
timerTick的分辨率,决定了只适于于较大的时间延时。
    在做时序时的小延时,用传统方法好了。

OS中的taskDelay()在裸奔环境下的一种实现:
OS环境:
void xxxTask(void)
{

    while(1){
        //waitEvent

        //do step_1

        taskDelay(TIME_OUT_TICK_NUM);

        //do step_2
    }

裸奔环境:
void xxxTask(void)
{
    static unsigned int taskStat = STAT_GENERAL;    //任务状态变量
    static timer_t startTick;
    timer_t currTick;
    
    if (taskStat == STAT_GENERAL)
    {
        //check event

        //if no event
            return;

        //do step_1

        startTick = sysGetTick();    //sysGetTick()就是察看系统时间
        taskStat = STAT_WAIT;
        return;
    }
    else if (taskStat == STAT_WAIT)
    {
        currTick = sysGetTick();    //sysGetTick()就是察看系统时间
        if ((currTick - startTick) >= TIME_OUT_TICK_NUM)
        {
            //do step_2    

            taskStat = STAT_GENERAL;
            return;
        }
        else
            return;
    }

基于状态机控制的面向对象的前后台协从多任务系统设计

一、任务分析
    根据题目要求,划分任务如下:
    1、键盘扫描线程
    2、灯显示线程
    3、LED1-LED4四个独立线程
    4、后台监视线程
    5、串口收发中断
    共计7个线程1个中断。

二、软件整体结构设计

               后台                         前台                     串口中断

  ---------|             ---------------                 --------------
                     | int10ms中断 |                 | serial中断 |
   -------------       ---------------                 --------------
   |监视monitor|             |                               |
   -------------      -----------------                --------------
                    |键盘扫描keyscan|                | 收 RI 检查 |
  ---------|            -----------------                --------------
                               |