RT Virtual Timers

Virtual Timers are an unique ChibiOS/RT feature. It is a software system able to provide an “unlimited” number of one-shot timers with the same resolution of the system tick. Virtual timers are also used internally in order to implement the timeout feature present in many API functions.

One Shot Timers

Virtual timers are one-shot timers that can be started, stopped prematurely or trigger a callback after their programmed time is expired. The timers state machine is:

Callbacks

Timers callbacks are always invoked from ISR context, this means that the API that can be utilized from a timer callback is subject to the same restrictions applicable to ISRs. By re-arming a virtual timer from the callback it is possible to implement periodic or aperiodic timers as well.

Tickless Mode

Common RTOS kernels are triggered by a periodic interrupt, called system tick, driving the internal timings-relate mechanisms. In ChibiOS/RT the system tick is handled efficiently however it can still limits the system in several ways:

  • CPU usage is increased by the frequent IRQ servicing, the problem gets worse at higher frequencies.
  • The system tick limits the resolution of the virtual timers and of the system time because a too high frequency would negatively affect the system performance.
  • The system jitter is worsened by the continuous interruptions.
  • Frequent interrupts can prevent the system from entering deeper sleep modes. This affects negatively the system power usage.

ChibiOS/RT implements a unique tickless mode in its virtual timers subsystem. When the tickless mode is activated the system timer is no more generating periodic interrupts but is programmed to generate an interrupt only when the system has some scheduled activity to execute, usually a virtual timer expiration. This approach has several positive aspects:

  • Lower power consumption thanks to the use of deeper sleep modes not continuously interrupted by a system tick.
  • Better overall jitter in ISR servicing.
  • Higher resolution for system time and virtual timers because the timer frequency is no more constrained.

There are some things to consider:

  • A new ChibiOS/RT port is more complex if the tickless mode has to be implemented.
  • A special timer must be present in HW and dedicated to the tickless mode. It must be an up-counter with a comparator register. A 16 bits counter is sufficient however a 32 bits counter is recommended.
  • The behavior of the system time within critical sections is slightly different, in tick mode the system time is not incremented, in tickless mode the time is always incremented because the timer counter register is used and it is not affected by the critical section.
  • Slightly larger kernel image.

The system can use both modes without impacts for applications so the mode can be changed without problems, in order to make comparisons for example.

Virtual Timers API

virtual_timer_t Type of a virtual timer object.
chVTObjectInit() Initializes a virtual timer object virtual_timer_t.
chVTSet() Starts or restarts a virtual timer.
chVTSetI() Starts or restarts a virtual timer (I-Class variant).
chVTReset() Stops, if active, a virtual timer.
chVTResetI() Stops, if active, a virtual timer (I-Class variant).
chVTIsArmedI() Returns true if the timer is armed.
chVTDoSetI() Starts a virtual timer, the timer must not be already armed. Slightly faster than chVTSetI().
chVTDoResetI() Stops a virtual timer, the timer must be already armed. Slightly faster than chVTResetI().

Examples

Monostable LED Blinker

A function blinks a LED one time for a fixed period, if called repeatedly the LED stays on.

static virtual_timer_t led_vt;
 
/*
 * LED timer callback.
 */
static void led_cb(void *arg) {
 
  LED_off();
}
 
/*
 * Monostable blinker function.
 */
void blink(void) {
 
  LED_on();
  chVTSet(&led_vt, TIME_MS2ST(500), led_cb, NULL);
}
 
/*
 * Application main function.
 */
void main(void) {
 
  /*
   * System initializations.
   * - Kernel initialization, the main() function becomes a thread and the
   *   RTOS is active. Interrupts are enabled on chSysInit() exit.
   */
  chSysInit();
 
  /* LED timer initialization.*/
  chVTObjectInit(&led_vt);
 
  /* Blinking the LED when the key is pressed.*/
  while (true) {
    if (key_pressed())
      blink();
 
    chThdSleepMilliseconds(50);
  }  
}

Continuous LED Blinker

A virtual timer is used a dedicated LED blinker. The blinker can be started and stopped with a single command.

static virtual_timer_t led_vt;
 
/*
 * LED timer callback.
 */
static void led_cb(void *arg) {
 
  LED_toggle();
  chSysLockFromISR();
  chVTSetI(&led_vt, TIME_MS2ST(500), led_cb, NULL);
  chSysUnlockFromISR();
}
 
/*
 * Application main function.
 */
void main(void) {
 
  /*
   * System initializations.
   * - Kernel initialization, the main() function becomes a thread and the
   *   RTOS is active. Interrupts are enabled on chSysInit() exit.
   */
  chSysInit();
 
  /* LED timer initialization.*/
  chVTObjectInit(&led_vt);
 
  /* Starting blinker.*/
  chVTSet(&led_vt, TIME_MS2ST(500), led_cb, NULL);
 
  ...;
 
  /* Stopping blinker.*/
  chVTReset(&led_vt);
  LED_off();
}