12.OSLA

预备知识

OSAL(Operating System Abstraction Layer,系统抽象层),可以通俗地理解为一个简化版的操作系统,为Z-Stack的正确运行提供了内存管理、中断管理和任务调度等基本功能。

  1. 任务ID:应用层是一个任务,它有一个系统分配给他的数值唯一的编号

  2. 任务事件处理函数:任务可以处理事件,处理事件的这些代码都在一个函数里

  3. 任务事件变量:应用层任务还有一个 2个字节的变量

  4. 应用层任务事件变量和应用层定义的事件【之前通过宏定义,定义的代表这个事件的2个字节变量】的关系

    • 如果事件变量和某个事件的宏值与操作为1,那么表示应用层任务将要处理这个事件。
  5. 系统在运行的时候会不断的去读应用层任务事件变量,当它发现这个变量为0时,就认为应用层任务当前没有事件需要去处理;如果发现这个变量不为0,它就认为应用层任务有事件将要去处理,它就会去调用应用层任务事件处理函数TestApp_ProcessEvent( byte task_id, UINT16 events ),并且把任务事件变量的值传给events

  6. 在事件处理函数里,events会分别和应用层定义的所有事件宏值进行与操作,如果发现那个为0,那么就去执行这个事件处理的相应代码

  7. osal_set_event()调用设置任务事件函数,TestApp_TaskID就会去执行TestApp_SEND_MSG_EVT事件相应的处理代码。

    • 本质:把TestApp_TaskID(应用层任务)的任务事件变量中对应于TestApp_SEND_MSG_EVT宏值为1的那个为变成1,这样话首先这个任务事件变量就变成了非0,系统在检测到任务事件变量为非0,就会去调用任务事件处理函数TestApp_ProcessEvent,在这个函数里,任务事件变量肯定和TestApp_SEND_MSG_EVT宏值与操作为1,所以必定会执行这个事件相关的代码。
  8. 每一个层都是一个任务,因此每一个层都有1个任务ID。 任务事件处理函数 任务事件变量

    • FUN函数数组={任务事件处理A,任务事件处理B,任务事件处理C…}
    • Arr变量数组={任务事件变量a,任务事件变量b,任务事件变量c…}
  9. 任务ID,系统不是随便分配,可以通过这个值立即找到这个任务自己的任务事件处理函数事件变量

    • 例如:应用层任务的任务ID是8
    • 那么FUN[8]里面是对应的任务处理函数的函数名
    • Arr[8]里面就是该任务的事件变量

其他资料上的专业性定义:

  • 任务(Task):可以理解为需要处理器处理的具体任务,例如“在1秒后开灯”或者“关灯”等等。

  • 任务池:是一个可以存储多个任务的缓冲区,例如一个任务池中可以存放“1秒后开灯”、“2秒后关灯”、“3秒后开灯”及“1分钟后关灯”这几个任务。系统会在指定的时间去执行任务池中的各个任务。

  • 优先级:由于可能存在在同一个时刻需要执行多个任务的情况,所以需要区分在这个时刻优先处理哪些任务、延后处理哪些任务,而优先级是用来标记每一个任务的优先等级。在相同条件下,系统会优先处理高优先级的任务,同时低优先级的任务需要等待处理。另外,系统也可能会中断优先级较底的任务,转而去处理优先级较高的任务。

  • 轮询:系统会每隔一段时间在任务池中检查有没有现在需要处理的任务,这个过程称为轮询。

  • 操作系统调度周期:调度周期是指轮询概念中“每隔一段时间”的具体时间长度。系统调度周期也是任务的最小时间周期,例如系统调度周期是1秒钟,但是存在一个任务是“0.1秒后关灯”,该任务虽然要求0.1秒后关灯,但由于0.1秒小于系统调度周期,所以这个任务在1秒后才会被执行。

动手实现系统调度

osal_init_system

函数osal_init_system中有一个osalInitTasks();函数,在这个函数里系统给所有任务分配任务ID.

void osalInitTasks( void ) // 定义函数 osalInitTasks,该函数不接受任何参数并且没有返回值  
{  
  uint8 taskID = 0; // 定义一个 uint8 类型的变量 taskID 并初始化为 0,用于表示任务ID  
  
  // 为 tasksEvents 分配内存,tasksCnt 是任务的数量,这里分配的是 uint16 类型的大小乘以 tasksCnt 的大小  
  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);  
  // 将分配的内存区域用 0 填充  
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));  
  
  // 初始化 macTask,并使用 ++taskID 作为参数  
  macTaskInit( taskID++ );  
  // 初始化 nwk,并使用 ++taskID 作为参数  
  nwk_init( taskID++ );  
  // 初始化 Hal,并使用 ++taskID 作为参数  
  Hal_Init( taskID++ );  
    
#if defined( MT_TASK ) // 如果定义了 MT_TASK  
  // 初始化 MT_Task,并使用 ++taskID 作为参数  
  MT_TaskInit( taskID++ );  
    
#endif  
  // 初始化 APS,并使用 ++taskID 作为参数  
  APS_Init( taskID++ );  
    
#if defined ( ZIGBEE_FRAGMENTATION ) // 如果定义了 ZIGBEE_FRAGMENTATION  
  // 初始化 APSF,并使用 ++taskID 作为参数  
  APSF_Init( taskID++ );  
    
#endif  
  // 初始化 ZDApp,并使用 ++taskID 作为参数  
  ZDApp_Init( taskID++ );  
    
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  // 如果定义了 ZIGBEE_FREQ_AGILITY 或者 ZIGBEE_PANID_CONFLICT  
  // 初始化 ZDNwkMgr,并使用 ++taskID 作为参数  
  ZDNwkMgr_Init( taskID++ ); 
    
#endif  
  // 初始化 TestAPP,没有提供任务ID,可能是最后初始化的任务,或者是之前的任务ID都在其他任务中使用了  
  TestAPP_Init( taskID );  
}

osal_start_system

do-while循环的作用:

轮询整个任务池,也就是看一下有没有要处理的任务。循环中只有一个条件判断,如果条件成立,那么就结束循环。

其中的tasksEvents是一个uint16类型的数组,其中的每一个元素都表示一种类型的任务,也就是说,tasksEvents就是一个任务池,tasksCnt是这个任务池的大小。

这个循环的运行逻辑是:

  • 首先,idx的初始值为0;
  • 当tasksEvents[idx]的值为0时,表示该任务中没有事情要处理,这时候条件判断不成立,进入下一次循环;
  • 每执行1次循环前,idx加1,然后判断是否小于tasksCnt;
  • 当tasksEvents[idx]的值不等于0时,表示该任务中有事情要处理,这时候条件判断成立,于是通过break结束循环;
  • 当循环结束后,如果整个任务池中都没有任务要处理,那么idx必定会>=tasksCnt。因此,如果idx < tasksCnt,表示现在任务池中有任务需要处理,并且tasksEvents[idx]就是当前需要处理的任务。因此在循环结束后,Z-Stack先用if (idx < tasksCnt)语句来判断有没有任务需要处理。
/*
 * 这是一个不返回的函数,是任务系统的主循环函数。它将遍历所有任务事件并为有事件的任务调用task_event_processor()函数。  
 * 如果所有任务都没有事件,此函数会让处理器进入睡眠模式。  
 */  
void osal_start_system( void )
{  
#if !defined ( ZBIT ) && !defined ( UBIT ) // 如果未定义ZBIT和UBIT宏  
  for(;;) // 进入无限循环,除非外部中断或者其他条件停止  
#endif  
  {  
    uint8 idx = 0; // 定义一个uint8类型的变量idx并初始化为0,用于表示任务索引  
  
    osalTimeUpdate(); // 更新系统时间  
    Hal_ProcessPoll(); // 执行Hal的轮询处理,这替换了MT_SerialPoll()和osal_check_timer()函数  
     
    // 开始do-while循环,直到idx达到tasksCnt(任务数量)为止 
    do {   
      if (tasksEvents[idx]) // 如果当前任务有事件(事件为非零值)  
      {  
        break; // 则跳出循环,执行后续代码  
      }  
    } while (++idx < tasksCnt); // idx自增1并判断是否小于tasksCnt,若小于则继续循环  
  
    // 如果idx小于tasksCnt(还有任务未处理) 
    if (idx < tasksCnt) 
    {  
      uint16 events; // 定义一个uint16类型的变量events,用于存储事件  
      halIntState_t intState; // 定义一个halIntState_t类型的变量intState,用于存储中断状态(用于锁住临界区)  
  
      HAL_ENTER_CRITICAL_SECTION(intState); // 进入临界区,锁定中断以保护临界区代码的执行,将intState设置为当前的中断状态  
      events = tasksEvents[idx]; // 从tasksEvents数组中获取当前任务的事件  
      tasksEvents[idx] = 0; // 清零当前任务的未处理事件(清除事件)  
      HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区,解锁中断恢复原始的中断状态  
  
      // 调用tasksArr数组中对应任务的回调函数,参数为当前任务的索引idx和已处理的事件events,并将返回的事件添加到当前任务的事件中  
      events = (tasksArr[idx])( idx, events );   
  
      HAL_ENTER_CRITICAL_SECTION(intState); // 再次进入临界区,锁定中断以保护临界区代码的执行,将intState设置为当前的中断状态  
      tasksEvents[idx] |= events; // 将未处理的事件添加回当前任务的事件中(添加未处理事件)  
      HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区,解锁中断恢复原始的中断状态  
    }  
#if defined( POWER_SAVING ) // 如果定义了POWER_SAVING宏(开启节能模式)  
    else // 如果整个任务事件循环没有任何活动(没有任务有事件)  
    {  
      osal_pwrmgr_powerconserve(); // 则调用osal_pwrmgr_powerconserve()函数将处理器/系统置于睡眠模式以节省电力(节能模式)  
    }  
#endif // 结束if宏定义判断(如果有定义POWER_SAVING宏)  
  } // 结束for循环(整个任务事件处理流程)  
} // 结束函数定义(osal_start_system函数)

你可能感兴趣的:(ZigBee,笔记,单片机,单片机,物联网,嵌入式硬件,ZigBee,cc2530)