233 lines
6.7 KiB
ReStructuredText
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.
|