2. OSAL实现时间片轮转的分析

2. OSAL实现时间片轮转的分析

1. OSAL初始化及其OSAL相关结构体

  • 紧接上文,我们打开osal_main.c 文件,我们可以看到 osal_init_system(); 。这个函数是初始化osal 的,我们查看它具体的代码。

    uint8 osal_init_system( void )
    {
      // 内存分配初始化  --- 1
      osal_mem_init();
    
      // 消息队列初始化  --- 2
      osal_qHead = NULL;
    
    #if defined( OSAL_TOTAL_MEM )
      osal_msg_cnt = 0;
    #endif
      // 系统时间初始化 --- 3
      osalTimerInit();
      // 任务链表初始化 --- 4 
      osal_init_TaskHead();  
    
      return ( ZSUCCESS );
    }
    
    1. 内存分配初始化在动态申请内存章节进行分析。

    2. 消息队列初始化在消息队列章节进行分析

    3. osalTimerInit 系统事件初始化函数,我们进去看看它做了什么。

      void osalTimerInit( void )
      {
          //初始化硬件定时器,这个函数调用的为systick的初始化,因为我们是有stm32cudemx进行初始化,
          // 本函数并没有使用
        OSAL_TIMER_TICKINIT();    
        
        // 初始化任务定时器更新时自减的数值单位
        // 在 osal_timer.h 中宏定义 TIMER_DECR_TIME为1,也就是systick定时器中断一次,事件过去
        //  1ms
        tmr_decr_time = TIMER_DECR_TIME;
      
        // 关闭系统定时器
        osal_timer_activate( FALSE );
        timerActive = FALSE;
        // 系统时间初始化,osal_systemClock 为系统事件的全局变量,用来记录系统事件。
        osal_systemClock = 0;
      }
      
    4. osal_init_TaskHead() 为系统任务链表指针初始化,查看具体代码如下:

      void  osal_init_TaskHead( void )
      {
      	// 初始化任务链表头指针,下方三个变量都为全局变量
      	TaskHead   = (OsalTadkREC_t *)NULL;
          // 当前活动任务指针
      	TaskActive = (OsalTadkREC_t *)NULL;
          // 任务ID统计
      	Task_id = 0;
       }
      
    5. 在分析系统链表初始化的时候,我们发现 OsalTadkREC_t 结构体,这个结构体为 任务链表,是系统非常重要的结构体。

      //任务链表
      typedef struct OSALTaskREC
      {
        struct  OSALTaskREC  *next;
        pTaskInitFn          pfnInit;				//任务初始化函数指针
        pTaskEventHandlerFn  pfnEventProcessor;	//任务事件处理函数指针
        uint8                taskID;				//任务ID
        uint8                taskPriority;		//任务优先级
        uint16               events;				//任务事件
      } OsalTadkREC_t;
      
      // 其中 pTaskInitFn 任务初始化函数指针格式如下
      typedef void (*pTaskInitFn)( uint8 task_id ); 
      
      // pfnEventProcessor 任务事件处理函数指针格式如下
      typedef uint16 (*pTaskEventHandlerFn)(uint8 task_id,uint16 task_event);
      
      // osal用这个结构体来管理整个系统的任务,通过链表的方式将一个一个的任务串联起来,我们接下来
      // 分析如下进行任务的创建,也就是创建这个任务链表
      

2. OSAL任务的创建

  • 我们是有osal_add_Task(pTaskInitFn pfnInit, pTaskEventHandlerFn pfnEventProcessor, uint8 taskPriority) 来创建任务,第一个参数为任务初始化函数,第二个参数为任务循环函数,第三个参数为任务优先级。其创建的代码如下:

    void  osal_add_Task(pTaskInitFn pfnInit,
                      	pTaskEventHandlerFn pfnEventProcessor,
                      	uint8 taskPriority)
    {
    	// TaskNew 为新任务指针,TaskSech为任务链表指针,TaskPTR为任务链表指针的指针
    	OsalTadkREC_t  *TaskNew;
    	OsalTadkREC_t  *TaskSech;
    	OsalTadkREC_t  **TaskPTR;
    	// 申请任务内存,内存申请在下个章节会讲
    	TaskNew = osal_mem_alloc(sizeof(OsalTadkREC_t));
    	if(TaskNew)
    	{
    		// 初始化任务指针
    		TaskNew->pfnInit = pfnInit;
    		// 初始化任务事件处理函数指针
    		TaskNew->pfnEventProcessor = pfnEventProcessor;
    		// 初始化任务ID,全局变量Task_id记录着当前创建了几个任务
    		TaskNew->taskID = Task_id++;
    		// 初始化任务事件
    		TaskNew->events = 0;
    		// 初始化任务优先级
    		TaskNew->taskPriority = taskPriority;
    		// 初始化任务链表指针,链表的下一个肯定为Null,因为当前还没有创建
    		TaskNew->next = (OsalTadkREC_t *)NULL;
    		// 任务链表指针的指针
    		TaskPTR = &TaskHead;
            // TaskHead 为全局变量,存储着当前的系统任务链表
    	    TaskSech = TaskHead;
    		
    	    tasksCnt++;			//任务数量统计
    		
            // 进行链表循环,TaskSech是一个指针,在不为Null的时候,进去进行遍历
    		while(TaskSech)
    		{
    			// 遍历任务优先级 比 当前任务链表 指针指向的任务优先级高
                // 此处进行任务优先级的判断,因为OSAL基于时间片,在链表前面的函数会更早执行
                // 所有高优先级的要添加在前面
    			if(TaskNew->taskPriority > TaskSech->taskPriority)
    			{
    				// 插入到遍历到的任务链表指针的前面
    				TaskNew->next = TaskSech;
    				*TaskPTR = TaskNew;
    				return;
    			}
    			// 遍历任务链表,进行指针的下移
    			TaskPTR = &TaskSech->next;
    			TaskSech = TaskSech->next;
    		}
    		// 没有任务,直接插入到任务链表头
            // 当TaskSech为空时,则直接将TaskNew的地址赋值到TaskHead
    		*TaskPTR = TaskNew;
    	}
    	return;
    }
    

3. OSAL任务初始化

  • 在完成所有的任务注册之后,就要进行任务的初始化了。注意:任务的注册必须在初始化之前,初始化的函数非常简单,就是按照链表的顺序一次调用注册时的初始化函数。代码如下:

    void osal_Task_init(void)
    {
    	// 遍历任务链表,初始化任务
        // 下方代码非常简单,遍历链表,依次调用初始化函数
    	TaskActive = TaskHead;
    	while(TaskActive)
    	{
    	  if(TaskActive->pfnInit)
    	  {
    		 TaskActive->pfnInit(TaskActive->taskID);
    	  }
    	  TaskActive = TaskActive->next;
    	}
    	TaskActive = (OsalTadkREC_t *)NULL;
    }
    

4. OSAL任务循环事件的设置

  • 在进行任务初始化之后,就需要设置任务按照某个时间进行运行。这是根据时间片流转的必要条件,函数如下:uint8 osal_start_reload_timer( uint8 taskID, uint16 event_id, uint16 timeout_value ) ,其中taskID 为任务ID,在创建任务的时候会返回,我们需要进行保存。event_id 为消息ID,因为任务不会只接收一个任务消息,所以需要创建的时候传入消息ID。timeout_value 为任务循环事件。创建的具体代码如下:

    uint8 osal_start_reload_timer( uint8 taskID, uint16 event_id, uint16 timeout_value )
    {
      // 新建任务定时器      --- 1
      osalTimerRec_t *newTimer;
    
      HAL_ENTER_CRITICAL_SECTION(); 
    
      // Add timer 添加新的定时器
      newTimer = osalAddTimer( taskID, event_id, timeout_value ); // --2
      // 如果申请成功
      if ( newTimer )
      {
        // Load the reload timeout value
        // 重装定时时间
        newTimer->reloadTimeout = timeout_value;  // 申请成功,将重装载事件设timeout_value
      }
    
      if ( newTimer )
      {
        // 如果硬件定时器没有运行,启动硬件定时器
        if ( timerActive == FALSE )
        {
          // 打开硬件定时器
          osal_timer_activate( TRUE );
        }
      }
    
      HAL_EXIT_CRITICAL_SECTION(); 
    
      return ( (newTimer != NULL) ? SUCCESS : NO_TIMER_AVAIL );
    }
    
    // 1. 出现了第一个没见过的结构体,osalTimerRec_t。它也是一个链表,存储着所有创建的定时器
    // 并且用任务ID与任务进行联系
    typedef struct
    {
      void   *next;
      uint16 timeout;           //定时时间,每过一个系统时钟会自减
      uint16 event_flag;        //定时事件,定时时间减完产生任务事件
      uint8  task_id;           //响应的任务ID
      uint16 reloadTimeout;     //重装定时时间
    } osalTimerRec_t;           //任务定时器,链表结构
    
    
    // 2. 第点为 osalAddTime创建定时器函数,这个函数将定时器创建完成,并且返回。
    osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint16 timeout )
    {
      osalTimerRec_t *newTimer;
      osalTimerRec_t *srchTimer;
    
      // 查找注册的定时器是否已经存在,这里查找的方式为查找任务ID,和消息ID是否同时存在
      newTimer = osalFindTimer( task_id, event_flag );
      // 如果存在定时器,更新定时时间
      if ( newTimer )
      {
        // Timer is found - update it.
        newTimer->timeout = timeout;
    
        return ( newTimer );
      }
      // 如果不存在定时器,新建定时器
      else
      {
        // 申请内存,新建定时器
        newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) );
        // 如果申请成功
        if ( newTimer )
        {
          // 结构体进行初始化
          newTimer->task_id = task_id;
          newTimer->event_flag = event_flag;
          newTimer->timeout = timeout;
          newTimer->next = (void *)NULL;
          newTimer->reloadTimeout = 0;
            
          // 如果为空,新建任务定时器链表,timerHead为定时器链表头,如果当前为链表第一个元素
          // 那么newTimer 就是头,将 newTimer的地址赋值给timerHead
          if ( timerHead == NULL )
          {
            timerHead = newTimer;
          }
          // 如果不为空,添加到链表尾部
          else
          {
            srchTimer = timerHead;
            // 查找链表尾部
            while ( srchTimer->next )
              srchTimer = srchTimer->next;
              
            srchTimer->next = newTimer;
          }
          // 返回新建的任务定时器
          return ( newTimer );
        }
        // 如果申请失败,返回空指针
        else
          return ( (osalTimerRec_t *)NULL );
      }
    
        
        // 查找定时器的代码非常简单
        	osalTimerRec_t *osalFindTimer( uint8 task_id, uint16 event_flag )
            {
              osalTimerRec_t *srchTimer;
    
              // Head of the timer list
              srchTimer = timerHead;
    
              // Stop when found or at the end
              while ( srchTimer )
              {
                if ( srchTimer->event_flag == event_flag &&
                     srchTimer->task_id == task_id )
                  break;
    
                // Not this one, check another
                srchTimer = srchTimer->next;
              }
    
              return ( srchTimer );
            }
    

5. OSAL主任务循环函数

  • OSAL主任务循环函数osal_start_system 主要进行任务逻辑的处理,当某个任务的时间到达之后,进行任务循环函数处理。具体代码如下:

    void osal_start_system( void )
    {
      
      uint16 events;
      uint16 retEvents;
    
      while(1)
      {
        // 返回下一次需要处理事件的任务指针,TaskActive为当前处理的任务,在每次循环中进行切换,切换到下一个需要处理任务,osalNextActiveTask任务。需要处理的标识就是任务事件 events。
        TaskActive = osalNextActiveTask(); // -- 1
        // 如果有任务需要处理事件
        if ( TaskActive )
        {
          HAL_ENTER_CRITICAL_SECTION();
          // 获取任务事件
          events = TaskActive->events;
          // Clear the Events for this task
          // 清除任务事件
          TaskActive->events = 0;
          HAL_EXIT_CRITICAL_SECTION();
          // 如果有事件需要处理
          if ( events != 0 )
          {
            // Call the task to process the event(s)
            // 调用任务事件处理函数
            if ( TaskActive->pfnEventProcessor )
            {
              // 任务事件处理函数返回值
              retEvents = (TaskActive->pfnEventProcessor)( TaskActive->taskID, events );
    
              // Add back unprocessed events to the current task
              HAL_ENTER_CRITICAL_SECTION();
              // 处理完成事件,清除标志位
              TaskActive->events |= retEvents;
              HAL_EXIT_CRITICAL_SECTION();
            }
          }
        }
      }
    }
    
    
    // osalNextActiveTask 函数如下,非常的简单
    OsalTadkREC_t *osalNextActiveTask( void )
    {
      	OsalTadkREC_t  *TaskSech;
    
      	// Start at the beginning
      	TaskSech = TaskHead;
    
      	// When found or not
      	while ( TaskSech ) 
    	{
            if ( TaskSech->events)  
            {
    		  	// task is highest priority that is ready
              	return  TaskSech;
          	}
          	TaskSech =  TaskSech->next;
      	}
      	return NULL;
    }
    

6.OSAL SystemClock时钟函数

  • SystemClock 函数通常会以1000Hz的周期调用,每次调用时间增加1ms。具体实现如下:

    void  osalTimerUpdate( uint16 updateTime )
    {
      osalTimerRec_t *srchTimer;
      osalTimerRec_t *prevTimer;
    
      // 关闭中断
      HAL_ENTER_CRITICAL_SECTION(); 
    
      // 更新系统时间
      osal_systemClock += updateTime;
      // 重新打开中断
      HAL_EXIT_CRITICAL_SECTION(); 
    
    
      // 查找空闲的定时器
      if ( timerHead != NULL )
      {
    
        // srchTimer 定时器表头
        srchTimer = timerHead;
        // prevTimer 定时器表头的前一个定时器
        prevTimer = (void *)NULL;
    
        // Look for open timer slot
        while ( srchTimer )
        {
          
          osalTimerRec_t *freeTimer = NULL;
         // 关闭中断
          HAL_ENTER_CRITICAL_SECTION(); // Hold off interrupts.
          // 检查定时器是否超时
          if (srchTimer->timeout <= updateTime)
          {
            // 超时,置0
            srchTimer->timeout = 0;
          }
          else
          {
            // 未超时,减去更新时间
            srchTimer->timeout = srchTimer->timeout - updateTime;
          }
          
          // Check for reloading
          // 检查是否需要重装定时时间 
          /**
           * srchTimer->timeout  定时时间
           * srchTimer->reloadTimeout 重装定时时间
           * srchTimer->event_flag 定时事件
          */
          if ( (srchTimer->timeout == 0) && (srchTimer->reloadTimeout) && (srchTimer->event_flag) )
          {
            // Notify the task of a timeout
            // 通知任务超时事件
            osal_set_event( srchTimer->task_id, srchTimer->event_flag );
            
            // Reload the timer timeout value
            // 重装定时时间
            srchTimer->timeout = srchTimer->reloadTimeout;
          }
          
            
          // 定时时间为0或者事件标志位为0
          if ( srchTimer->timeout == 0 || srchTimer->event_flag == 0 )
          {
            // Take out of list
            // 从链表中删除
            if ( prevTimer == NULL )
              timerHead = srchTimer->next;
            else
              prevTimer->next = srchTimer->next;
    
    
            // 释放内存
            freeTimer = srchTimer;
    
            // 获取下一个定时器
            srchTimer = srchTimer->next;
          }
          else
          {
    
            // 获取下一个定时器
            prevTimer = srchTimer;
            srchTimer = srchTimer->next;
          }
          // 重新打开中断
          HAL_EXIT_CRITICAL_SECTION(); 
          // 释放内存
          if ( freeTimer )
          {
            // 如果定时器超时时间为0
            if ( freeTimer->timeout == 0 )
            {
              // 设置任务事件
              osal_set_event( freeTimer->task_id, freeTimer->event_flag );
            }
            // 释放内存
            osal_mem_free( freeTimer );
          }
        }
      }
    }
    
  • 以上,OSAL时间片的主要函数就是这些,还有一个无关紧要的函数。读懂上面这些函数之后就非常容易理解。完整的代码请查看第一章节。

  • 第一章节

  • 目录

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值