/** * @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; }
/** * @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; }
/** * @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"); } }
/** * @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; }