376 lines
12 KiB
ReStructuredText
376 lines
12 KiB
ReStructuredText
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``.
|
|
|
|
.. code-block:: console
|
|
|
|
$ ./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 :menuselection:`System Type --> <Chip> Peripheral Selection` and press :kbd:`Enter`.
|
|
|
|
Then select one or more timers according to availability.
|
|
|
|
2. Enable the Timer Support
|
|
|
|
Go into menu :menuselection:`Device Drivers --> Timer Driver Support` and press :kbd:`Enter`. Then enable:
|
|
|
|
- [x] Timer Support
|
|
- [x] Timer Arch Implementation
|
|
|
|
3. Include the Timer Example
|
|
|
|
Go into menu :menuselection:`Application Configuration --> Examples` and press :kbd:`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 <https://github.com/apache/nuttx-apps/blob/master/examples/timer/timer_main.c>`_
|
|
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:
|
|
|
|
.. code-block:: console
|
|
|
|
`nsh> timer`
|
|
|
|
This command will use the timer 0. To use the others, specify it through a
|
|
parameter (where x is the timer number):
|
|
|
|
.. code-block:: console
|
|
|
|
`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:
|
|
|
|
.. code-block:: c
|
|
|
|
#include <nuttx/timers/timer.h>
|
|
|
|
At an application level, the timer functionalities may be accessed through ``ioctl``
|
|
systems calls. The available ``ioctl`` commands are:
|
|
|
|
* :c:macro:`TCIOC_START`
|
|
* :c:macro:`TCIOC_STOP`
|
|
* :c:macro:`TCIOC_GETSTATUS`
|
|
* :c:macro:`TCIOC_SETTIMEOUT`
|
|
* :c:macro:`TCIOC_NOTIFICATION`
|
|
* :c:macro:`TCIOC_MAXTIMEOUT`
|
|
|
|
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.
|
|
|
|
.. c:struct:: timer_ops_s
|
|
.. code-block:: c
|
|
|
|
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:
|
|
|
|
.. code-block:: c
|
|
|
|
/* 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;
|
|
}
|
|
|
|
.. c:macro:: TCIOC_START
|
|
|
|
The ``TCIOC_START`` command calls the ``start`` operation, which is described below.
|
|
|
|
.. c:function:: int start(void)
|
|
|
|
The start operation configures the timer, enables the interrupt if ``TCIOC_NOTIFICATION``
|
|
has already been called and finally starts the timer.
|
|
|
|
:return: A Linux System Error Code for failing or 0 for success.
|
|
|
|
This command may be used like so:
|
|
|
|
.. code-block:: c
|
|
|
|
/* 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;
|
|
}
|
|
|
|
.. c:macro:: TCIOC_STOP
|
|
|
|
The ``TCIOC_STOP`` command calls the ``stop`` operation, which is described below.
|
|
|
|
.. c:function:: int stop(void)
|
|
|
|
The stop operation stops the timer and disables the interrupt.
|
|
|
|
:return: A Linux System Error Code for failing or 0 for success.
|
|
|
|
This command may be used like so:
|
|
|
|
.. code-block:: c
|
|
|
|
/* 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;
|
|
}
|
|
|
|
.. c:macro:: TCIOC_GETSTATUS
|
|
|
|
The ``TCIOC_GETSTATUS`` command calls the ``getstatus`` operation, which is described below.
|
|
|
|
.. c:function:: int getstatus(FAR struct timer_status_s *status)
|
|
|
|
The getstatus operation gathers the timer's current information.
|
|
|
|
:param 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.
|
|
|
|
:return: A Linux System Error Code for failing or 0 for success.
|
|
|
|
This command may be used like so:
|
|
|
|
.. code-block:: c
|
|
|
|
/* 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);
|
|
|
|
.. c:macro:: TCIOC_SETTIMEOUT
|
|
|
|
The ``TCIOC_SETTIMEOUT`` command calls the ``settimeout`` operation, which is described below.
|
|
|
|
.. c:function:: int settimeout(uint32_t timeout)
|
|
|
|
The getstatus 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.
|
|
|
|
:param timeout: An argument of type ``uint32_t`` with the timeout value in microseconds.
|
|
|
|
:return: A Linux System Error Code for failing or 0 for success.
|
|
|
|
This command may be used like so:
|
|
|
|
.. code-block:: c
|
|
|
|
/* 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;
|
|
}
|
|
|
|
.. c:macro:: 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:
|
|
|
|
.. code-block:: c
|
|
|
|
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)¬ify));
|
|
if (ret < 0)
|
|
{
|
|
fprintf(stderr, "ERROR: Failed to set the timer handler: %d\n", errno);
|
|
close(fd);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
.. c:macro:: TCIOC_MAXTIMEOUT
|
|
|
|
The ``TCIOC_MAXTIMEOUT`` command calls the ``maxtimeout`` operation, which is described below.
|
|
|
|
.. c:function:: int maxtimeout(uint32_t *status)
|
|
|
|
The maxtimeout operation gets the maximum timeout value that can be configured.
|
|
|
|
:param maxtimeout: A writable pointer to a variable of ``uint32_t`` type in
|
|
which the value will be stored.
|
|
:return: A Linux System Error Code for failing or 0 for success.
|
|
|
|
This command may be used like so:
|
|
|
|
.. code-block:: c
|
|
|
|
/* 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 reat 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);
|
|
|
|
Those snippets were taken from the Example which provides a great resource to
|
|
demonstrate how to use those ``ioctl`` commands.
|