| .. _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. |