⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tskttsk.c

📁 一个可以立即使用的嵌入式操作系统
💻 C
字号:
/*
   --- Version 2.2 90-11-19 17:59 ---

   TSKTTSK.C - CTask - Timer task.

   CTask - a Multitasking Kernel for C

   Public Domain Software written by
      Thomas Wagner
      Ferrari electronic Gmbh
      Beusselstrasse 27
      D-1000 Berlin 21
      Germany

   No rights reserved.

   This file is new with 2.1. The timer task functions were moved
   from tsktimer to this module.

   Timer logic has been significantly changed in version 2.0.
*/

#include "tsk.h"
#include "tsklocal.h"

/*
   tsk_timer_action 
      performs the necessary action when a timeout occurred.
      Starting with version 2.0, this part is not critical,
      and may be preempted.

      Version 2.1 makes this routine global for use by the keyboard
      hotkey handler.
*/

void Localfunc tsk_timer_action (tlinkptr elem)
{
   tcbptr task;
   byte st;
   CRITICAL;

   switch (elem->struckind)
      {
      case TKIND_WAKE:
      case TKIND_TASK:  task = (tcbptr) elem->strucp;
                        C_ENTER;
                        st = task->state;

                        if (st == ST_WAITING || st == ST_DELAYED)
                           {
                           task->retptr = 
                              (elem->link.kind == TYP_TIMER)
                                 ? TTIMEOUT
                                 : TWATCH;
                           tsk_runable (task);
                           }
                        C_LEAVE;
                        break;

      case TKIND_FLAG:  set_flag ((flagptr) elem->strucp);
                        break;

      case TKIND_COUNTER: inc_counter ((counterptr) elem->strucp);
                        break;

      case TKIND_COUNTDEC: dec_counter ((counterptr) elem->strucp);
                        break;

      case TKIND_PROC:  tsk_callfunc (elem->strucp, elem);
                        break;

      default:          break;
      }
}


/*
   tsk_exec_watch 
      checks the watch condition, and returns 1 if the 
      condition is met.
*/

local int Staticfunc tsk_exec_watch (tlinkptr curr)
{
   word val, cmp;
   int elcmp;

   elcmp = curr->elkind & 0x0f;

   switch (curr->elkind & 0xf0)
      {
      case TELEM_MEM:   val = *(curr->elem.mem.address) & curr->elem.mem.mask;
                        cmp = curr->elem.mem.compare;
                        if (elcmp == TCMP_CHG)
                           curr->elem.mem.compare = val;
                        break;

      /* Microsoft C generates "Internal Compiler Error" on compiling
         the following statement */
#if (TSK_TURBO)
      case TELEM_PORT:  val = (curr->elem.port.in_word)
                              ? tsk_inpw (curr->elem.port.port)
                              : (word)tsk_inp (curr->elem.port.port);
#else
      case TELEM_PORT:  if (curr->elem.port.in_word)
                           val = tsk_inpw (curr->elem.port.port);
                        else
                           val = (word)tsk_inp (curr->elem.port.port);
#endif
                        val &= curr->elem.port.mask;
                        cmp = curr->elem.port.compare;
                        if (elcmp == TCMP_CHG)
                           curr->elem.port.compare = val;
                        break;

      default:          return 0;
      }

   switch (elcmp)
      {
      case TCMP_EQ:  return val == cmp;
      case TCMP_CHG:
      case TCMP_NE:  return val != cmp;
      case TCMP_LE:  return val <= cmp;
      case TCMP_GE:  return val >= cmp;
      case TCMP_LES: return (int)val <= (int)cmp;
      case TCMP_GES: return (int)val >= (int)cmp;
      default:       return 0;
      }
}


/*
   The timer task handles all timeouts.

   Starting with version 2.0, two queues are maintained, one for
   the timeouts, and one for watch elements.

   The timeout queue now is ordered, with the tick count in the queue
   element head holding the difference in ticks to the previous element.
   Thus, only the first element of the timeout queue has to be counted
   down, which will considerably speed up processing here if there are
   multiple elements in the queue.

   The watch queue is unordered.

   The loop to check the queue elements is fully protected.
   This allows other tasks access to the timer queue. The
   concept used in pre-2.0 versions was pretty complicated to
   handle, and still had to run with interupts disabled for
   most parts.

   The new logic unchains expired timeout elements immediately, within
   the protected loop, but delays processing them until after the loop 
   is finished. Modification of those elements should not normally occur,
   and will be rejected. The processing of the timeout/watch action can 
   thus be handled with interrupts enabled.

   Version 2.1 corrects a bug that prevented wakeup elements from being
   correctly processed if they had the repeat attribute. It introduces
   a single link pointer in the timer control block for chaining the
   elements to be acted upon, which is used exclusively by the timer
   task. The 2.0 algorithm used the normal timer queue for chaining,
   which convoluted the code, and, in the end, led to the mentioned bug.

   Version 2.2 cleans up processing by eliminating the "state" field,
   all necessary info is now contained in the flags. This better supports
   enabling and disabling elements while the timer task is processing them.
*/

void Taskfunc tsk_timer (void)
{
   queptr curr;
   queptr help;
   tlinkptr tlast;
   tlinkptr tcurr;
   byte flg;
   CRITICAL;

   while (1)
      {
      wait_counter_set (&tsk_timer_counter, 0L);

      tlast = LNULL;

      C_ENTER;

      /* First, check the timeout queue. Since version 2.0, only
         the first element has to be counted down.
      */

      if (!((curr = GLOBDATA timer_queue.first)->kind & Q_HEAD))
         if (!--curr->el.ticks)
            {
            /* Remove all counted-down elements from the timer queue,
               marking them as not in queue and busy, and chaining them
               through the special "next" pointer.
               We can then process the elements outside the critical 
               section.
            */
            do
               {
               help = curr;
               curr = curr->next;
               tsk_dequeue (help);
               ((tlinkptr)help)->flags |= TFLAG_BUSY;
               ((tlinkptr)help)->next = tlast;
               tlast = (tlinkptr)help;
               }
            while (!(curr->kind & Q_HEAD) && !curr->el.ticks);
            }

      C_LEAVE;
      /* shortly allow interrupts */
      C_ENTER;

      /* Now, check the watch queue. */

      for (curr = GLOBDATA watch_queue.first; !(curr->kind & Q_HEAD); )
         {
         help = curr;
         curr = curr->next;

         if (tsk_exec_watch ((tlinkptr)help))
            {
            tsk_dequeue (help);
            ((tlinkptr)help)->flags |= TFLAG_BUSY;
            ((tlinkptr)help)->next = tlast;
            tlast = (tlinkptr)help;
            }
         }

      /* Ready checking the queues, we can now re-enable interrupts
         for execution of the timeout/watch action. Interrupts are
         disabled only for a short period to check the state and
         re-enqueue repeat elements.  */

      C_LEAVE;

      while (tlast != LNULL)
         {
         tcurr = tlast;
         tlast = tlast->next;
         tsk_timer_action (tcurr);

         C_ENTER;
         flg = tcurr->flags;
         if (flg & TFLAG_REMOVE)
            {
            if (!(flg & TFLAG_TEMP))
               flg &= ~TFLAG_REMOVE;
            }
         else if (!(flg & TFLAG_UNQUEUE))
            {
            if (flg & (TFLAG_REPEAT | TFLAG_ENQUEUE))
               {
               if (tcurr->link.kind == TYP_TIMER)
                  tsk_enqtimer ((queptr)tcurr, tcurr->elem.time.reload);
               else
                  tsk_putqueue (&GLOBDATA watch_queue, (queptr)tcurr);
               }
            }
         tcurr->flags &= ~(TFLAG_BUSY | TFLAG_UNQUEUE | TFLAG_ENQUEUE | TFLAG_REMOVE);
         C_LEAVE;

#if (TSK_DYNAMIC)
         if (flg & TFLAG_REMOVE)
            {
#if (GROUPS)
            tsk_dequeue ((queptr)&tcurr->chain);
#endif
            tsk_pfree (tcurr);
            }
#endif
         }
      }
}


/*
   int8 is the timer interrupt chain task.
*/

#if (IBM && !INT8_EARLY)

void Taskfunc tsk_int8 (void)
{
   while (1)
      {
      wait_counter_set (&tsk_int8_counter, 0L);
      tsk_chain_timer ();
      }
}

#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -