Timer Drivers

Files supporting the timer driver can be found in the following locations:

  • Interface Definition. The header file for the NuttX timer driver reside at include/nuttx/timers/timer.h. This header file includes both the application level interface to the timer driver as well as the interface between the “upper half” and “lower half” drivers. The timer driver uses a standard character driver framework.

  • “Upper Half” Driver. The generic, “upper half” timer driver resides at drivers/timers/timer.c.

  • “Lower Half” Drivers. Platform-specific timer drivers reside in arch/<architecture>/src/<hardware> directory for the specific processor <architecture> and for the specific <chip> timer peripheral devices.

There are two ways to enable Timer Support along with the Timer Example. The first is faster and simpler. Just run the following command to use a ready config file with timer support and example included. You need to check if there’s a timer config file for your specific chip. You may check it at the specific board’s path: /boards/<arch>/<chip>/<variant>/config.

$ ./tools/configure.sh <variant>:timer

And the second way is creating your own config file. To do so, follow the next instructions.

Enabling the Timer Support and Example in menuconfing

  1. Select Timer Instances

To select these timers browse in the menuconfig using the following path:

Go into menu System Type ‣ <Chip> Peripheral Selection and press Enter.

Then select one or more timers according to availability.

  1. Enable the Timer Support

Go into menu Device Drivers ‣ Timer Driver Support and press Enter. Then enable:

  • [x] Timer Support

  • [x] Timer Arch Implementation

  1. Include the Timer Example

Go into menu Application Configuration ‣ Examples and press Enter. Then select the Timer Example.

  • [x] Timer example

Below the option, it is possible to manually configure some parameters as the standard timer device path, the timeout, the sample rate in which the counter will be read, the number of samples to be executed, and other parameters.

Timer Example

The previously selected example will basically consult the timer status, set a timer alarm interval, set a timer signal handler function to be notified at the alarm, which only increments a variable, and then it will start the timer. The application will periodically consult the timer status at the sample rate previously configured through the menuconfig to follow the time left until the timer expires. After the samples have been read, the application stops the timer.

The example code may be explored, its path is at /examples/timer/timer_main.c in the apps’ repository.

In NuttX, the timer driver is a character driver and when a chip supports multiple timers, each one is accessible through its respective file in /dev directory. Each timer is registered using a unique numeric identifier (i.e. /dev/timer0, /dev/timer1, …).

Use the following command to run the example:

`nsh> timer`

This command will use the timer 0. To use the others, specify it through a parameter (where x is the timer number):

`nsh> timer -d /dev/timerx`

Application Level Interface

The first necessary thing to be done in order to use the timer driver in an application is to include the header file for the NuttX timer driver. It contains the Application Level Interface to the timer driver. To do so, include:

#include <nuttx/timers/timer.h>

At an application level, the timer functionalities may be accessed through ioctl systems calls. The available ioctl commands are:

These ioctl commands internally call lower-half layer operations and the parameters are forwarded to these ops through the ioctl system call. The return of a system call is the return of an operation. These struct timer_ops_s keeps pointers to the implementation of each operation. Following is the struct.

struct timer_ops_s
struct timer_ops_s
{
   /* Required methods *******************************************************/

   /* Start the timer, resetting the time to the current timeout */

   CODE int (*start)(FAR struct timer_lowerhalf_s *lower);

   /* Stop the timer */

   CODE int (*stop)(FAR struct timer_lowerhalf_s *lower);

   /* Get the current timer status */

   CODE int (*getstatus)(FAR struct timer_lowerhalf_s *lower,
                           FAR struct timer_status_s *status);

   /* Set a new timeout value (and reset the timer) */

   CODE int (*settimeout)(FAR struct timer_lowerhalf_s *lower,
                           uint32_t timeout);

   /* Call the NuttX INTERNAL timeout callback on timeout.
      * NOTE:  Providing callback==NULL disable.
      * NOT to call back into applications.
      */

   CODE void (*setcallback)(FAR struct timer_lowerhalf_s *lower,
                              CODE tccb_t callback, FAR void *arg);

   /* Any ioctl commands that are not recognized by the "upper-half" driver
      * are forwarded to the lower half driver through this method.
      */

   CODE int (*ioctl)(FAR struct timer_lowerhalf_s *lower, int cmd,
                     unsigned long arg);

   /* Get the maximum supported timeout value */

   CODE int (*maxtimeout)(FAR struct timer_lowerhalf_s *lower,
                           FAR uint32_t *maxtimeout);
};

Since ioctl system calls expect a file descriptor, before using these commands, it’s necessary to open the timer device special file in order to get a file descriptor. The following snippet demonstrates how to do so:

/* Open the timer device */

printf("Open %s\n", devname);

fd = open(devname, O_RDONLY);
if (fd < 0)
  {
    fprintf(stderr, "ERROR: Failed to open %s: %d\n",
            devname, errno);
    return EXIT_FAILURE;
  }
TCIOC_START

The TCIOC_START command calls the start operation, which is described below.

int start(void)

The start operation configures the timer, enables the interrupt if TCIOC_NOTIFICATION has already been called and finally starts the timer.

Returns:

A Linux System Error Code for failing or 0 for success.

This command may be used like so:

/* Start the timer */

printf("Start the timer\n");

ret = ioctl(fd, TCIOC_START, 0);
if (ret < 0)
  {
    fprintf(stderr, "ERROR: Failed to start the timer: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }
TCIOC_STOP

The TCIOC_STOP command calls the stop operation, which is described below.

int stop(void)

The stop operation stops the timer and disables the interrupt.

Returns:

A Linux System Error Code for failing or 0 for success.

This command may be used like so:

/* Stop the timer */

printf("\nStop the timer\n");

ret = ioctl(fd, TCIOC_STOP, 0);
if (ret < 0)
  {
    fprintf(stderr, "ERROR: Failed to stop the timer: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }
TCIOC_GETSTATUS

The TCIOC_GETSTATUS command calls the getstatus operation, which is described below.

int getstatus(FAR struct timer_status_s *status)

The getstatus operation gathers the timer’s current information.

Parameters:
  • status – A writable pointer to a struct timer_status_s. This struct contains 3 fields: flags (uint32_t), timeout (uint32_t) and timeleft (uint32_t). Bit 0 from flags indicates the timer’s status, 1 indicates that the timer is running, zero it is stopped. Bit 1 from flags indicates if there’s a callback registered. The timeout indicates the time interval that was configured to trigger an alarm, it is in microseconds. The timeleft interval indicates how many microseconds it’s missing to trigger the alarm. The following snippet demonstrates how to use it and how to access these fields.

Returns:

A Linux System Error Code for failing or 0 for success.

This command may be used like so:

/* Get timer status */

ret = ioctl(fd, TCIOC_GETSTATUS, (unsigned long)((uintptr_t)&status));
if (ret < 0)
  {
    fprintf(stderr, "ERROR: Failed to get timer status: %d\n", errno);
    close(fd);
    exit(EXIT_FAILURE);
  }

/* Print the timer status */

printf("  flags: %08lx timeout: %lu timeleft: %lu\n",
       (unsigned long)status.flags, (unsigned long)status.timeout,
       (unsigned long)status.timeleft);
TCIOC_SETTIMEOUT

The TCIOC_SETTIMEOUT command calls the settimeout operation, which is described below.

int settimeout(uint32_t timeout)

The settimeout operation sets a timeout interval to trigger the alarm and then trigger an interrupt. It defines the timer interval in which the handler will be called.

Parameters:
  • timeout – An argument of type uint32_t with the timeout value in microseconds.

Returns:

A Linux System Error Code for failing or 0 for success.

This command may be used like so:

/* Set the timer interval */

printf("Set timer interval to %lu\n",
       (unsigned long)CONFIG_EXAMPLES_TIMER_INTERVAL);

ret = ioctl(fd, TCIOC_SETTIMEOUT, CONFIG_EXAMPLES_TIMER_INTERVAL);
if (ret < 0)
  {
    fprintf(stderr, "ERROR: Failed to set the timer interval: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }
TCIOC_NOTIFICATION

The TCIOC_NOTIFICATION is used to configure the timer callback to notify the application via a signal when the timer expires. This command calls the setcallback operation. Which will not be described here, since the application does not set a callback directly. Instead, the user should configure a signal handler to catch notifications, and then, configure a timer notifier to notify and to signal the previously configured signal handler. For a better performance, a separate pthread may be configured to wait on sigwaitinfo() for timer events.

In any case, this command expects a read-only pointer to a struct timer_notify_s. This struct contains 2 fields: pid (pid_t), that indicates the ID of the task/thread to receive the signal and event (struct sigevent), which describes the way a task will be notified.

This command may be used like so:

printf("Configure the notification\n");

notify.pid   = getpid();
notify.event.sigev_notify = SIGEV_SIGNAL;
notify.event.sigev_signo  = CONFIG_EXAMPLES_TIMER_SIGNO;
notify.event.sigev_value.sival_ptr = NULL;

ret = ioctl(fd, TCIOC_NOTIFICATION, (unsigned long)((uintptr_t)&notify));
if (ret < 0)
  {
    fprintf(stderr, "ERROR: Failed to set the timer handler: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }
TCIOC_MAXTIMEOUT

The TCIOC_MAXTIMEOUT command calls the maxtimeout operation, which is described below.

int maxtimeout(uint32_t *status)

The maxtimeout operation gets the maximum timeout value that can be configured.

Parameters:
  • maxtimeout – A writable pointer to a variable of uint32_t type in which the value will be stored.

Returns:

A Linux System Error Code for failing or 0 for success.

This command may be used like so:

/* Get the maximum timer timeout  */

printf("Get the maximum timer timeout\n");

ret = ioctl(fd, TCIOC_MAXTIMEOUT, (uint32_t*)(&max_timeout));
if (ret < 0)
  {
    fprintf(stderr, "ERROR: Failed to read the timer's maximum timeout: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }

/* Print the maximum supported timeout */

printf("Maximum supported timeout: %" PRIu32 "\n", max_timeout);

The TCIOC_TICK_GETSTATUS command invokes the getstatus lower-half operation and returns the current timer status expressed in timer ticks. The conversion from microseconds to ticks is performed by the timer upper-half driver.

The getstatus operation gathers the timer’s current information. When invoked via TCIOC_TICK_GETSTATUS, the timeout and timeleft fields are converted from microseconds to timer ticks before being returned to the caller.

param status:

A writable pointer to a struct timer_status_s. This structure contains the same fields as used by TCIOC_GETSTATUS, but the timeout and timeleft values are expressed in timer ticks instead of microseconds.

return:

A Linux System Error Code for failing or 0 for success.

This command may be used like so:

/* Get timer status in ticks */

ret = ioctl(fd, TCIOC_TICK_GETSTATUS, (unsigned long)((uintptr_t)&status));
if (ret < 0)
  {
    fprintf(stderr, "ERROR: Failed to get timer tick status: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }

printf("flags: %08lx timeout(ticks): %lu timeleft(ticks): %lu\n",
        (unsigned long)status.flags, (unsigned long)status.timeout,
        (unsigned long)status.timeleft);

The TCIOC_TICK_SETTIMEOUT command calls the settimeout operation and sets a new timeout value expressed in timer ticks.

The settimeout operation configures the timer to expire after the specified number of timer ticks and resets the timer. The timeout value is converted from ticks to microseconds by the timer upper-half driver before invoking the lower-half settimeout operation.

param timeout:

An argument of type uint32_t that specifies the timeout interval in timer ticks.

return:

A Linux System Error Code for failing or 0 for success.

This command may be used like so:

/* Set timer timeout in ticks */

printf("Set timer timeout to %lu ticks\n",
(unsigned long)timeout_ticks);

ret = ioctl(fd, TCIOC_TICK_SETTIMEOUT, timeout_ticks);
if (ret < 0)
  {
    fprintf(stderr, "ERROR: Failed to set timer tick timeout: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }

The TCIOC_TICK_MAXTIMEOUT command calls the maxtimeout operation and returns the maximum supported timeout value expressed in timer ticks.

The maxtimeout operation gets the maximum timeout value that can be configured for the timer when using tick-based time units.

param maxtimeout:

A writable pointer to a variable of uint32_t type in which the maximum supported timeout (in ticks) will be stored.

return:

A Linux System Error Code for failing or 0 for success.

This command may be used like so:

/* Get the maximum timer timeout in ticks */

printf("Get the maximum timer timeout in ticks\n");

ret = ioctl(fd, TCIOC_TICK_MAXTIMEOUT, (uint32_t *)(&max_timeout));
if (ret < 0)
  {
    fprintf(stderr, "ERROR: Failed to read timer tick maximum timeout: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }

/* Print the maximum supported timeout (ticks) */

printf("Maximum supported timeout (ticks): %" PRIu32 "\n", max_timeout);

Those snippets were taken from the Example which provides a great resource to demonstrate how to use those ioctl commands.