Overview

This part of the platform interface groups functions related to the timers of the MCU. It also makes provisions for using virtual timers on any platform, see this section for details. Keep in mind that in the following paragraphs a timer id can reffer to both a hardware timer or a virtual timer.

Data structures, constants and types

typedef u32 timer_data_type;

This defines the data type used to specify delays and time intervals (which are always specifide in microseconds).

// Timer operations
enum
{
  PLATFORM_TIMER_OP_START,
  PLATFORM_TIMER_OP_READ,
  PLATFORM_TIMER_OP_SET_CLOCK,
  PLATFORM_TIMER_OP_GET_CLOCK,
  PLATFORM_TIMER_OP_GET_MAX_DELAY,
  PLATFORM_TIMER_OP_GET_MIN_DELAY
};

This enum lists all the operations that can be executed on a given timer.

Functions

int platform_timer_exists( unsigned id );

Checks if the platform has the timer specified as argument. Implemented in src/common.c, it uses the NUM_TIMER macro that must be defined in the platform's platform_conf.h file (see here for details) and the virtual timer configuration (here for details). For example:

#define NUM_TIMER   2      // The platform has 2 hardware timers

Arguments: id - the timer ID

Returns: 1 if the timer exists, 0 otherwise

void platform_timer_delay( unsigned id, u32 delay_us );

Waits on a timer, then returns. This function is "split" in two parts: a platform-independent part implemented in src/common.c (that handles virtual timers) and a platform-dependent part that must be implemented by each platform in a function named platform_s_timer_delay. This function handles both hardware timer IDs and virtual timer IDs.
IMPORTANT NOTE: the real delay after executing this functions depends on a number of variables, most notably the base clock of the timer and the size of the timer counter register (32 bits on some platforms, 16 bits on most platforms, other values are less common). To ensure that the delay you're requesting is achievable, use platform_timer_op with PLATFORM_TIMER_OP_GET_MAX_DELAY and PLATFORM_TIMER_OP_GET_MIN_DELAY to obtain the maximum and the minimum achievable wait times on your timer, respectively. Even if your delay is within these limits, the precision of this function still varies a lot, mainly as a function of the timer base clock.

Arguments:

Returns: nothing.

void platform_s_timer_delay( unsigned id, u32 delay_us );

This function is identical in functionality to platform_timer_delay, but this is the function that must actually be implemented by a platform port, and it must never handle virtual timer IDs, only hardware timer IDs. It has the same limitations as platform_timer_delay.

Arguments:

Returns: nothing.

u32 platform_timer_op( unsigned id, int op, u32 data );

Executes an operation on a timer. This function is "split" in two parts: a platform-independent part implemented in src/common.c (that handles virtual timers) and a platform-dependent part that must be implemented by each platform in a function named platform_s_timer_op. This function handles both hardware timer IDs and virtual timer IDs.

Arguments:

Returns:

u32 platform_s_timer_op( unsigned id, int op, u32 data );

This function is identical in functionality to platform_timer_op, but this is the function that must actually be implemented by a platform port, and it must never handle virtual timer IDs, only hardware timer IDs.

Arguments:

Returns:

u32 platform_timer_get_diff_us( unsigned id, timer_data_type end, timer_data_type start );

Return the time difference (in us) betweeen two timer values. This function is generic for all platforms, thus it is implemented in src/common.c.

Arguments:

  • id - the timer ID
  • end - the first timer value
  • start - the second timer value

Returns: the time difference (in microseconds)

Virtual timers

Virtual timers were added to eLua to overcome some limitations:

In this respect, virtual timers are a set of timers that share a single hardware timer. It is possible, in this way, to have a hardware timer that can implement 4, 8 or more virtual/software timers. There are a few drawbacks to this approach:

To enable virtual timers:

  1. edit platform_conf.h (see here for details) and set VTMR_NUM_TIMERS to the number of desired virtual timers and VTMR_FREQ_HZ to the base frequency of the virtual timers (in hertz). For example:
    #define VTMR_NUM_TIMERS       4 // we need 4 virtual timers
    #define VTMR_FREQ_HZ          4 // the base clock for the virtual timers is 4Hz
  2. in your platform port setup a hardware timer to fire an interrupt at VTMR_FREQ_HZ and call the cmn_virtual_timer_cb function (defined in src/common.c) in the timer interrupt handler. For example, if the the interrupt handler is called timer_int_handler, do this:
    void timer_int_handler( void )
    {
      // add code to clear the timer interrupt flag here if needed
      cmn_virtual_timer_cb();
    }

Note that because of step 2 above you are limited by practical constraints on the value of VTMR_FREQ_HZ. If set too high, the timer interrupt will fire too often, thus taking too much CPU time. The maximum value depends largely on the hardware and the desired behaviour of the virtual timers, but in practice values larger than 10 might visibly change the behaviour of your system.

To use a virtual timer, identify it with the constant VTMR_FIRST_ID (defined in inc/common.h) plus an offset. For example, VTMR_FIRST_ID+0 (or simply VTMR_FIRST_ID) is the ID of the first virtual timer in the system, and VTMR_FIRST_ID+2 is the ID of the third virtual timer in the system.