Example #1
0
/**
 * @brief   Enables a virtual timer.
 * @details The timer is enabled and programmed to trigger after the delay
 *          specified as parameter.
 * @pre     The timer must not be already armed before calling this function.
 * @note    The callback function is invoked from interrupt context.
 *
 * @param[out] vtp      the @p virtual_timer_t structure pointer
 * @param[in] delay     the number of ticks before the operation timeouts, the
 *                      special values are handled as follow:
 *                      - @a TIME_INFINITE is allowed but interpreted as a
 *                        normal time specification.
 *                      - @a TIME_IMMEDIATE this value is not allowed.
 *                      .
 * @param[in] vtfunc    the timer callback function. After invoking the
 *                      callback the timer is disabled and the structure can
 *                      be disposed or reused.
 * @param[in] par       a parameter that will be passed to the callback
 *                      function
 *
 * @iclass
 */
void chVTDoSetI(virtual_timer_t *vtp, systime_t delay,
                vtfunc_t vtfunc, void *par) {
  virtual_timer_t *p;

  chDbgCheckClassI();
  chDbgCheck((vtp != NULL) && (vtfunc != NULL) && (delay != TIME_IMMEDIATE));

  vtp->vt_par = par;
  vtp->vt_func = vtfunc;
  p = ch.vtlist.vt_next;

#if CH_CFG_ST_TIMEDELTA > 0 || defined(__DOXYGEN__)
  {
    systime_t now = port_timer_get_time();

    /* If the requested delay is lower than the minimum safe delta then it
       is raised to the minimum safe value.*/
    if (delay < CH_CFG_ST_TIMEDELTA)
      delay = CH_CFG_ST_TIMEDELTA;

    if (&ch.vtlist == (virtual_timers_list_t *)p) {
      /* The delta list is empty, the current time becomes the new
         delta list base time.*/
      ch.vtlist.vt_lasttime = now;
      port_timer_start_alarm(ch.vtlist.vt_lasttime + delay);
    }
    else {
      /* Now the delay is calculated as delta from the last tick interrupt
         time.*/
      delay += now - ch.vtlist.vt_lasttime;

      /* If the specified delay is closer in time than the first element
         in the delta list then it becomes the next alarm event in time.*/
      if (delay < p->vt_delta)
        port_timer_set_alarm(ch.vtlist.vt_lasttime + delay);
    }
  }
#endif /* CH_CFG_ST_TIMEDELTA > 0 */

  /* The delta list is scanned in order to find the correct position for
     this timer. */
  while (p->vt_delta < delay) {
    delay -= p->vt_delta;
    p = p->vt_next;
  }

  /* The timer is inserted in the delta list.*/
  vtp->vt_prev = (vtp->vt_next = p)->vt_prev;
  vtp->vt_prev->vt_next = p->vt_prev = vtp;
  vtp->vt_delta = delay

  /* Special case when the timer is in last position in the list, the
     value in the header must be restored.*/;
  p->vt_delta -= delay;
  ch.vtlist.vt_delta = (systime_t)-1;
}
Example #2
0
File: chvt.c Project: mabl/ChibiOS
/**
 * @brief   Enables a virtual timer.
 * @details The timer is enabled and programmed to trigger after the delay
 *          specified as parameter.
 * @pre     The timer must not be already armed before calling this function.
 * @note    The callback function is invoked from interrupt context.
 *
 * @param[out] vtp      the @p virtual_timer_t structure pointer
 * @param[in] delay     the number of ticks before the operation timeouts, the
 *                      special values are handled as follow:
 *                      - @a TIME_INFINITE is allowed but interpreted as a
 *                        normal time specification.
 *                      - @a TIME_IMMEDIATE this value is not allowed.
 *                      .
 * @param[in] vtfunc    the timer callback function. After invoking the
 *                      callback the timer is disabled and the structure can
 *                      be disposed or reused.
 * @param[in] par       a parameter that will be passed to the callback
 *                      function
 *
 * @iclass
 */
void chVTDoSetI(virtual_timer_t *vtp, sysinterval_t delay,
                vtfunc_t vtfunc, void *par) {
  virtual_timer_t *p;
  sysinterval_t delta;

  chDbgCheckClassI();
  chDbgCheck((vtp != NULL) && (vtfunc != NULL) && (delay != TIME_IMMEDIATE));

  vtp->par = par;
  vtp->func = vtfunc;

#if CH_CFG_ST_TIMEDELTA > 0
  {
    systime_t now = chVTGetSystemTimeX();

    /* If the requested delay is lower than the minimum safe delta then it
       is raised to the minimum safe value.*/
    if (delay < (sysinterval_t)CH_CFG_ST_TIMEDELTA) {
      delay = (sysinterval_t)CH_CFG_ST_TIMEDELTA;
    }

    /* Special case where the timers list is empty.*/
    if (&ch.vtlist == (virtual_timers_list_t *)ch.vtlist.next) {

      /* The delta list is empty, the current time becomes the new
         delta list base time, the timer is inserted.*/
      ch.vtlist.lasttime = now;
      ch.vtlist.next = vtp;
      ch.vtlist.prev = vtp;
      vtp->next = (virtual_timer_t *)&ch.vtlist;
      vtp->prev = (virtual_timer_t *)&ch.vtlist;
      vtp->delta = delay;

#if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
      /* The delta could be too large for the physical timer to handle.*/
      if (delay > (sysinterval_t)TIME_MAX_SYSTIME) {
        delay = (sysinterval_t)TIME_MAX_SYSTIME;
      }
#endif

      /* Being the first element in the list the alarm timer is started.*/
      port_timer_start_alarm(chTimeAddX(ch.vtlist.lasttime, delay));

      return;
    }

    /* Pointer to the first element in the delta list, which is non-empty.*/
    p = ch.vtlist.next;

    /* Delay as delta from 'lasttime'. Note, it can overflow and the value
       becomes lower than 'now'.*/
    delta = chTimeDiffX(ch.vtlist.lasttime, now) + delay;

    if (delta < chTimeDiffX(ch.vtlist.lasttime, now)) {
      /* Scenario where a very large delay excedeed the numeric range, it
         requires a special handling. We need to skip the first element and
         adjust the delta to wrap back in the previous numeric range.*/
      delta -= p->delta;
      p = p->next;
    }
    else if (delta < p->delta) {
      sysinterval_t deadline_delta;

      /* A small delay that will become the first element in the delta list
         and next deadline.*/
      deadline_delta = delta;
#if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
      /* The delta could be too large for the physical timer to handle.*/
      if (deadline_delta > (sysinterval_t)TIME_MAX_SYSTIME) {
        deadline_delta = (sysinterval_t)TIME_MAX_SYSTIME;
      }
#endif
      port_timer_set_alarm(chTimeAddX(ch.vtlist.lasttime, deadline_delta));
    }
  }
#else /* CH_CFG_ST_TIMEDELTA == 0 */
  /* Delta is initially equal to the specified delay.*/
  delta = delay;

  /* Pointer to the first element in the delta list.*/
  p = ch.vtlist.next;
#endif /* CH_CFG_ST_TIMEDELTA == 0 */

  /* The delta list is scanned in order to find the correct position for
     this timer. */
  while (p->delta < delta) {
    /* Debug assert if the timer is already in the list.*/
    chDbgAssert(p != vtp, "timer already armed");

    delta -= p->delta;
    p = p->next;
  }

  /* The timer is inserted in the delta list.*/
  vtp->next = p;
  vtp->prev = vtp->next->prev;
  vtp->prev->next = vtp;
  p->prev = vtp;
  vtp->delta = delta;

  /* Calculate new delta for the following entry.*/
  p->delta -= delta;

  /* Special case when the timer is in last position in the list, the
     value in the header must be restored.*/
  ch.vtlist.delta = (sysinterval_t)-1;
}
Example #3
0
/**
 * @brief   Puts the current thread to sleep into the specified state with
 *          timeout specification.
 * @details The thread goes into a sleeping state, if it is not awakened
 *          explicitly within the specified system time then it is forcibly
 *          awakened with a @p NIL_MSG_TMO low level message.
 *
 * @param[in] newstate  the new thread state or a semaphore pointer
 * @param[in] timeout   the number of ticks before the operation timeouts.
 *                      the following special values are allowed:
 *                      - @a TIME_INFINITE no timeout.
 *                      .
 * @return              The wakeup message.
 * @retval NIL_MSG_TMO  if a timeout occurred.
 *
 * @sclass
 */
msg_t chSchGoSleepTimeoutS(tstate_t newstate, systime_t timeout) {
  thread_t *ntp, *otp = nil.current;

  chDbgAssert(otp != &nil.threads[NIL_CFG_NUM_THREADS],
               "idle cannot sleep");

  /* Storing the wait object for the current thread.*/
  otp->state = newstate;

#if NIL_CFG_ST_TIMEDELTA > 0
  if (timeout != TIME_INFINITE) {
    systime_t abstime;

    /* TIMEDELTA makes sure to have enough time to reprogram the timer
       before the free-running timer counter reaches the selected timeout.*/
    if (timeout < (systime_t)NIL_CFG_ST_TIMEDELTA) {
      timeout = (systime_t)NIL_CFG_ST_TIMEDELTA;
    }

    /* Absolute time of the timeout event.*/
    abstime = chVTGetSystemTimeX() + timeout;

    if (nil.lasttime == nil.nexttime) {
      /* Special case, first thread asking for a timeout.*/
      port_timer_start_alarm(abstime);
      nil.nexttime = abstime;
    }
    else {
      /* Special case, there are already other threads with a timeout
         activated, evaluating the order.*/
      if (chVTIsTimeWithinX(abstime, nil.lasttime, nil.nexttime)) {
        port_timer_set_alarm(abstime);
        nil.nexttime = abstime;
      }
    }

    /* Timeout settings.*/
    otp->timeout = abstime - nil.lasttime;
  }
#else

  /* Timeout settings.*/
  otp->timeout = timeout;
#endif

  /* Scanning the whole threads array.*/
  ntp = nil.threads;
  while (true) {
    /* Is this thread ready to execute?*/
    if (NIL_THD_IS_READY(ntp)) {
      nil.current = nil.next = ntp;
      if (ntp == &nil.threads[NIL_CFG_NUM_THREADS]) {
        NIL_CFG_IDLE_ENTER_HOOK();
      }
      port_switch(ntp, otp);
      return nil.current->u1.msg;
    }

    /* Points to the next thread in lowering priority order.*/
    ntp++;
    chDbgAssert(ntp <= &nil.threads[NIL_CFG_NUM_THREADS],
                "pointer out of range");
  }
}
Example #4
0
/**
 * @brief   Enables a virtual timer.
 * @details The timer is enabled and programmed to trigger after the delay
 *          specified as parameter.
 * @pre     The timer must not be already armed before calling this function.
 * @note    The callback function is invoked from interrupt context.
 *
 * @param[out] vtp      the @p virtual_timer_t structure pointer
 * @param[in] delay     the number of ticks before the operation timeouts, the
 *                      special values are handled as follow:
 *                      - @a TIME_INFINITE is allowed but interpreted as a
 *                        normal time specification.
 *                      - @a TIME_IMMEDIATE this value is not allowed.
 *                      .
 * @param[in] vtfunc    the timer callback function. After invoking the
 *                      callback the timer is disabled and the structure can
 *                      be disposed or reused.
 * @param[in] par       a parameter that will be passed to the callback
 *                      function
 *
 * @iclass
 */
void chVTDoSetI(virtual_timer_t *vtp, systime_t delay,
                vtfunc_t vtfunc, void *par) {
  virtual_timer_t *p;
  systime_t delta;

  chDbgCheckClassI();
  chDbgCheck((vtp != NULL) && (vtfunc != NULL) && (delay != TIME_IMMEDIATE));

  vtp->par = par;
  vtp->func = vtfunc;

#if CH_CFG_ST_TIMEDELTA > 0
  {
    systime_t now = chVTGetSystemTimeX();

    /* If the requested delay is lower than the minimum safe delta then it
       is raised to the minimum safe value.*/
    if (delay < (systime_t)CH_CFG_ST_TIMEDELTA) {
      delay = (systime_t)CH_CFG_ST_TIMEDELTA;
    }

    /* Special case where the timers list is empty.*/
    if (&ch.vtlist == (virtual_timers_list_t *)ch.vtlist.next) {

      /* The delta list is empty, the current time becomes the new
         delta list base time, the timer is inserted.*/
      ch.vtlist.lasttime = now;
      ch.vtlist.next = vtp;
      ch.vtlist.prev = vtp;
      vtp->next = (virtual_timer_t *)&ch.vtlist;
      vtp->prev = (virtual_timer_t *)&ch.vtlist;
      vtp->delta = delay;

      /* Being the first element in the list the alarm timer is started.*/
      port_timer_start_alarm(ch.vtlist.lasttime + delay);

      return;
    }

    /* Special case where the timer will be placed as first element in a
       non-empty list, the alarm needs to be recalculated.*/
    delta = now + delay - ch.vtlist.lasttime;
    if (delta < ch.vtlist.next->delta) {

      /* New alarm deadline.*/
      port_timer_set_alarm(ch.vtlist.lasttime + delta);
    }
  }
#else /* CH_CFG_ST_TIMEDELTA == 0 */
  /* Delta is initially equal to the specified delay.*/
  delta = delay;
#endif /* CH_CFG_ST_TIMEDELTA == 0 */

  /* The delta list is scanned in order to find the correct position for
     this timer. */
  p = ch.vtlist.next;
  while (p->delta < delta) {
    delta -= p->delta;
    p = p->next;
  }

  /* The timer is inserted in the delta list.*/
  vtp->next = p;
  vtp->prev = vtp->next->prev;
  vtp->prev->next = vtp;
  p->prev = vtp;
  vtp->delta = delta

  /* Special case when the timer is in last position in the list, the
     value in the header must be restored.*/;
  p->delta -= delta;
  ch.vtlist.delta = (systime_t)-1;
}