237 lines
6.8 KiB
ReStructuredText
237 lines
6.8 KiB
ReStructuredText
.. _events_v2:
|
|
|
|
Events
|
|
######
|
|
|
|
An :dfn:`event` is a kernel object that allows an application to perform
|
|
asynchronous signalling when a condition of interest occurs.
|
|
|
|
.. contents::
|
|
:local:
|
|
:depth: 2
|
|
|
|
Concepts
|
|
********
|
|
|
|
Any number of events can be defined. Each event is referenced by
|
|
its memory address.
|
|
|
|
An event has the following key properties:
|
|
|
|
* An **event handler**, which specifies the action to be taken
|
|
when the event is signalled. The action may instruct the system workqueue
|
|
to execute a function to process the event, mark the event as pending
|
|
so it can be later processed by a thread, or ignore the event.
|
|
|
|
* An **event pending flag**, which is set if the event is signalled
|
|
and an event handler function does not consume the signal.
|
|
|
|
An event must be initialized before it can be used. This establishes
|
|
its event handler and clears the event pending flag.
|
|
|
|
Event Lifecycle
|
|
===============
|
|
|
|
An ISR or a thread signals an event by **sending** the event
|
|
when a condition of interest occurs that cannot be handled by the detector.
|
|
|
|
Each time an event is sent, the kernel examines its event handler
|
|
to determine what action to take.
|
|
|
|
* :c:macro:`K_EVT_IGNORE` causes the event to be ignored.
|
|
|
|
* :c:macro:`K_EVT_DEFAULT` causes the event pending flag to be set.
|
|
|
|
* Any other value is assumed to be the address of an event handler function,
|
|
and is invoked by the system workqueue thread. If the function returns
|
|
zero, the signal is deemed to have been consumed; otherwise, the event
|
|
pending flag is set.
|
|
|
|
The kernel ensures that the event handler function is executed once
|
|
for each time an event is sent, even if the event is sent multiple times
|
|
in rapid succession.
|
|
|
|
An event whose event pending flag becomes set remains pending until
|
|
the event is accepted by a thread. This clears the event pending flag.
|
|
|
|
A thread accepts a pending event by **receiving** the event.
|
|
If the event's pending flag is currently clear, the thread may choose
|
|
to wait for the event to become pending.
|
|
Any number of threads may wait for a pending event simultaneously;
|
|
when the event is pended it is accepted by the highest priority thread
|
|
that has waited longest.
|
|
|
|
.. note::
|
|
A thread that accepts an event cannot directly determine how many times
|
|
the event pending flag was set since the event was last accepted.
|
|
|
|
Comparison to Unix-style Signals
|
|
================================
|
|
|
|
Zephyr events are somewhat akin to Unix-style signals, but have a number of
|
|
significant differences. The most notable of these are listed below:
|
|
|
|
* A Zephyr event cannot be blocked --- it is always delivered to its event
|
|
handler immediately.
|
|
|
|
* A Zephyr event pends *after* it has been delivered to its event handler,
|
|
and only if an event handler function does not consume the event.
|
|
|
|
* Zephyr has no pre-defined events or actions. All events are application
|
|
defined, and all have a default action that pends the event.
|
|
|
|
Implementation
|
|
**************
|
|
|
|
Defining an Event
|
|
=================
|
|
|
|
An event is defined using a variable of type :c:type:`struct k_event`.
|
|
It must then be initialized by calling :cpp:func:`k_event_init()`.
|
|
|
|
The following code defines and initializes an event.
|
|
|
|
.. code-block:: c
|
|
|
|
extern int my_event_handler(struct k_event *event);
|
|
|
|
struct k_event my_event;
|
|
|
|
k_event_init(&my_event, my_event_handler);
|
|
|
|
Alternatively, an event can be defined and initialized at compile time
|
|
by calling :c:macro:`K_EVENT_DEFINE()`.
|
|
|
|
The following code has the same effect as the code segment above.
|
|
|
|
.. code-block:: c
|
|
|
|
extern int my_event_handler(struct k_event *event);
|
|
|
|
K_EVENT_DEFINE(my_event, my_event_handler);
|
|
|
|
Signaling an Event
|
|
==================
|
|
|
|
An event is signalled by calling :cpp:func:`k_event_send()`.
|
|
|
|
The following code illustrates how an ISR can signal an event
|
|
to indicate that a key press has occurred.
|
|
|
|
.. code-block:: c
|
|
|
|
extern int my_event_handler(struct k_event *event);
|
|
|
|
K_EVENT_DEFINE(my_event, my_event_handler);
|
|
|
|
void keypress_interrupt_handler(void *arg)
|
|
{
|
|
...
|
|
k_event_send(&my_event);
|
|
...
|
|
}
|
|
|
|
Handling an Event
|
|
=================
|
|
|
|
An event handler function is used when a signalled event should not be ignored
|
|
or immediately pended. It has the following form:
|
|
|
|
.. code-block:: c
|
|
|
|
int <function_name>(struct k_event *event)
|
|
{
|
|
/* catch the event signal; return zero if the signal is consumed, */
|
|
/* or non-zero to let the event pend */
|
|
...
|
|
}
|
|
|
|
The following code illustrates an event handler function that processes
|
|
key presses detected by an ISR (as shown in the previous section).
|
|
|
|
.. code-block:: c
|
|
|
|
int my_event_handler(struct k_event *event_id_is_unused)
|
|
{
|
|
/* determine what key was pressed */
|
|
char c = get_keypress();
|
|
|
|
/* do complex processing of the keystroke */
|
|
...
|
|
|
|
/* signalled event has been consumed */
|
|
return 0;
|
|
}
|
|
|
|
Accepting an Event
|
|
==================
|
|
|
|
A pending event is accepted by a thread by calling :cpp:func:`k_event_recv()`.
|
|
|
|
The following code is an alternative to the code in the previous section.
|
|
It uses a dedicated thread to do very complex processing
|
|
of key presses that would otherwise monopolize the system workqueue.
|
|
The event handler function is now used only to filter out unwanted key press
|
|
events, allowing the dedicated thread to wake up and process key press events
|
|
only when a numeric key is pressed.
|
|
|
|
.. code-block:: c
|
|
|
|
int my_event_handler(struct k_event *event_id_is_unused)
|
|
{
|
|
/* determine what key was pressed */
|
|
char c = get_keypress();
|
|
|
|
/* signal thread only if key pressed was a digit */
|
|
if ((c >= '0') && (c <= '9')) {
|
|
/* save key press information */
|
|
...
|
|
/* signalled event should be pended */
|
|
return 1;
|
|
} else {
|
|
/* signalled event has been consumed */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void keypress_thread(void *unused1, void *unused2, void *unused3)
|
|
{
|
|
/* consume numeric key presses */
|
|
while (1) {
|
|
|
|
/* wait for a key press event to pend */
|
|
k_event_recv(&my_event, K_FOREVER);
|
|
|
|
/* process saved key press, which must be a digit */
|
|
...
|
|
}
|
|
}
|
|
|
|
Suggested Uses
|
|
**************
|
|
|
|
Use an event to minimize ISR processing by deferring interrupt-related
|
|
work to a thread to reduce the amount of time interrupts are locked.
|
|
|
|
Use an event to allow the kernel's system workqueue to handle an event,
|
|
rather than defining an application thread to handle it.
|
|
|
|
Use an event to allow the kernel's system workqueue to pre-process an event,
|
|
prior to letting an application thread handle it.
|
|
|
|
Configuration Options
|
|
*********************
|
|
|
|
Related configuration options:
|
|
|
|
* None.
|
|
|
|
APIs
|
|
****
|
|
|
|
The following event APIs are provided by :file:`kernel.h`:
|
|
|
|
* :cpp:func:`k_event_handler_set()`
|
|
* :cpp:func:`k_event_send()`
|
|
* :cpp:func:`k_event_recv()`
|