zephyr/doc/kernel/microkernel/microkernel_events.rst

233 lines
6.7 KiB
ReStructuredText

.. _microkernel_events:
Events
######
Concepts
********
The microkernel's :dfn:`event` objects are an implementation of traditional
binary semaphores.
Any number of events can be defined in a microkernel system. An event is
typically *sent* by a task, fiber, or ISR and *received* by a task, which then
takes some action in response. Events are the easiest and most efficient way to
synchronize operations between two different execution contexts.
Each event has a **name** that uniquely identifies it, and an associated
**event state**. Each event starts off in the ``clear`` state. Once that event
gets sent, it is placed into the ``set`` state (where it remains) until it is
received. When the event is received, it reverts back to the ``clear`` state.
Sending an event that is already set is permitted; however, this does not affect
the existing state, it and does not allow the receiving task to recognize whether
the event has been sent more than once.
The receiving task can test the state of an event and decide whether or not
to block it. The kernel allows only a single receiving task to wait for a given
event; if a second task attempts to wait, its receive operation immediately
returns a failure indication.
Each event also has an optional **event handler** function, which is executed
by the microkernel server fiber when the event is sent. An event handler
function lets an event be processed without requiring the kernel to schedule
a receiving task; this allows an event to be processed more quickly.
When an event handler determines that the event can be ignored, or that it
can process the event without the assistance of a task, the event handler
returns a value of zero, and the event's state is left unchanged. When an event
handler determines that additional processing *is* required, it returns a
non-zero value, and the event's state is changed to *set* (if it isn't already
set).
An event handler function can be used to improve the efficiency of event
processing by the receiving task. In some situations, event handlers can even
eliminate the need for a receiving task. Any event that does not require
an event handler can specify the :c:macro:`NULL` function. The event handler
function is passed the name of the event being sent each time it is invoked,
allowing the same function to be shared by multiple events. An event's event
handler function is specified at compile-time, but can be changed subsequently
at run-time.
Purpose
*******
Use an event to signal a task to take action in response to a condition
detected by another task, a fiber, or an ISR.
Use an event handler to allow the microkernel server fiber to handle an event,
prior to (or instead of) letting a task handle the event.
Usage
*****
Defining an Event
=================
The following parameters must be defined:
*name*
This specifies a unique name for the event.
*handler*
This specifies the name of the event handler function,
which should have the following form:
.. code-block:: c
int <entry_point>(int event)
{
/* start handling event; return zero if all done, */
/* or non-zero to let receiving task handle event */
...
}
If no event handler is required specify :c:macro:`NULL`.
Public Event
------------
Define the event in the application's MDEF using the following syntax:
.. code-block:: console
EVENT name handler
For example, the file :file:`projName.mdef` defines two events as follows:
.. code-block:: console
% EVENT NAME ENTRY
% ==========================================
EVENT KEYPRESS validate_keypress
EVENT BUTTONPRESS NULL
A public event can be referenced by name from any source file that includes
the file :file:`zephyr.h`.
Private Event
-------------
Define the event in a source file with the following syntax:
.. code-block:: c
DEFINE_EVENT(name, handler);
Example: Defining a Private Event, Enabling it from Elsewhere in the Application
================================================================================
This code defines a private event named ``PRIV_EVENT`` which has no associated
event handler function.
.. code-block:: c
DEFINE_EVENT(PRIV_EVENT, NULL);
To enable this event from a different source file, use the following syntax:
.. code-block:: c
extern const kevent_t PRIV_EVENT;
Example: Signaling an Event from an ISR
=======================================
This code signals an event during the processing of an interrupt.
.. code-block:: c
void keypress_interrupt_handler(void *arg)
{
...
isr_event_signal(KEYPRESS);
...
}
Example: Consuming an Event using a Task
========================================
This code processes events of a single type using a task.
.. code-block:: c
void keypress_task(void)
{
/* consume key presses */
while (1) {
/* wait for a key press to be signalled */
task_event_recv(KEYPRESS, TICKS_NONE);
/* determine what key was pressed */
char c = get_keypress();
/* process key press */
...
}
}
Example: Filtering Event Signals using an Event Handler
=======================================================
This code registers an event handler to filter out unwanted events,
allowing the receiving task to wake up only when needed.
.. code-block:: c
int validate_keypress(int event_id_is_unused)
{
/* determine what key was pressed */
char c = get_keypress();
/* signal task only if key pressed was a digit */
if ((c >= '0') && (c <= '9')) {
/* save key press information */
...
/* event is signalled to task */
return 1;
} else {
/* event is not signalled to task */
return 0;
}
}
void keypress_task(void)
{
/* register the filtering routine */
task_event_handler_set(KEYPRESS, validate_keypress);
/* consume key presses */
while (1) {
/* wait for a key press to be signalled */
task_event_recv(KEYPRESS, TICKS_NONE);
/* process saved key press, which must be a digit */
...
}
}
APIs
****
Event APIs provided by :file:`microkernel.h`
============================================
:cpp:func:`isr_event_send()`
Signal an event from an ISR.
:cpp:func:`fiber_event_send()`
Signal an event from a fiber.
:cpp:func:`task_event_send()`
Signal an event from a task.
:cpp:func:`task_event_recv()`
Wait for an event signal for a specified time period.
:cpp:func:`task_event_handler_set()`
Register an event handler function for an event.