mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 22:43:50 +01:00
Merge pull request #21899 from LeonardHerbst/origin/event_queue_guide
doc/guides/advanced_tutorials: added event queue guide
This commit is contained in:
commit
5196bac642
158
doc/guides/advanced_tutorials/event_queue.md
Normal file
158
doc/guides/advanced_tutorials/event_queue.md
Normal file
@ -0,0 +1,158 @@
|
||||
---
|
||||
title: Working with Event Queues
|
||||
description: This tutorial provides a quick introduction to using event queues in RIOT OS.
|
||||
code_folder: examples/guides/event_queue/
|
||||
---
|
||||
|
||||
## Introduction
|
||||
An event queue is a FIFO (First In First Out) data structure that holds events
|
||||
to be processed by an event handler.
|
||||
|
||||
Each event has exactly one callback function called the `handler`,
|
||||
that is executed when the event is processed.
|
||||
Posting an event to a queue triggers the execution of the event's handler
|
||||
on the thread that owns the event queue.
|
||||
Posting an event to a queue can be done from any thread or interrupt context.
|
||||
|
||||
This guide will give a quick introduction to using event queues in RIOT OS.
|
||||
We will cover the following topics:
|
||||
- [Regular Events](#regular-events)
|
||||
- [Custom Events](#custom-events)
|
||||
- [Handling Events](#handling-events)
|
||||
- [Periodic Events](#periodic-events)
|
||||
|
||||
:::note
|
||||
The whole source code for this guide can be found
|
||||
[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/event_queue).
|
||||
|
||||
If your project is not working as expected,
|
||||
you can compare your code with the code in this repository to see if you missed anything.
|
||||
:::
|
||||
|
||||
### Regular Events
|
||||
Creating and posting regular events is straightforward in RIOT.
|
||||
First, we define the event handler function:
|
||||
```c title="main.c"
|
||||
static void regular_handler(event_t *event)
|
||||
{
|
||||
(void) event;
|
||||
printf("\tTriggered regular event.\n");
|
||||
}
|
||||
```
|
||||
Then, we can create an instance of the event:
|
||||
```c title="main.c"
|
||||
static event_t event = { .handler = regular_handler };
|
||||
```
|
||||
This event can now be posted to an event queue:
|
||||
```c title="main.c"
|
||||
event_post(&my_queue, &event);
|
||||
```
|
||||
:::tip
|
||||
See [Handling Events](#handling-events) for information about setting up an event queue.
|
||||
:::
|
||||
### Custom Events
|
||||
Custom events allow us to pass additional data to the event handler.
|
||||
To create a custom event, we need to define a new struct that contains
|
||||
an `event_t` member and any additional data we want to pass.
|
||||
For example, let's create a custom event that passes a string message:
|
||||
```c time="main.c"
|
||||
typedef struct {
|
||||
event_t super;
|
||||
const char *text;
|
||||
} custom_event_t;
|
||||
```
|
||||
Next, we define the event handler function for the custom event:
|
||||
```c title="main.c"
|
||||
static void custom_handler(event_t *event)
|
||||
{
|
||||
/* The handler receives a pointer to the base event structure.
|
||||
* We need to get the pointer to our custom event structure.
|
||||
*/
|
||||
custom_event_t *custom_event = container_of(event, custom_event_t, super);
|
||||
printf("\tTriggered custom event with text: \"%s\".\n", custom_event->text);
|
||||
}
|
||||
```
|
||||
Notice how we do not get passed the `custom_event_t` pointer directly,
|
||||
but rather the embedded `event_t` pointer.
|
||||
To access the additional fields of our custom event, we use the `container_of`
|
||||
macro to access the parent structure.
|
||||
|
||||
Then, we can create an instance of our custom event:
|
||||
```c title="main.c"
|
||||
static custom_event_t custom_event = { .super.handler = custom_handler,
|
||||
.text = "CUSTOM EVENT" };
|
||||
```
|
||||
|
||||
Posting a custom event means posting the embedded `event_t` member:
|
||||
```c title="main.c"
|
||||
event_post(&my_queue, &custom_event.super);
|
||||
```
|
||||
:::tip
|
||||
See [Handling Events](#handling-events) for information about setting up an event queue.
|
||||
:::
|
||||
### Handling Events
|
||||
It is common to have a dedicated thread for handling events.
|
||||
A simple thread function that processes incoming events looks like this:
|
||||
```c title="main.c"
|
||||
void *event_handler_thread(void *arg)
|
||||
{
|
||||
assert(arg != NULL);
|
||||
event_queue_t *queue = (event_queue_t *)arg;
|
||||
|
||||
/* A thread must own an event queue to process its events.
|
||||
* Ownership is initially assigned to the thread that initializes the queue.
|
||||
* It can later be transferred by calling `event_queue_claim`.
|
||||
*/
|
||||
event_queue_init(queue);
|
||||
|
||||
puts("Event handler thread listening for events...");
|
||||
event_loop(queue);
|
||||
return NULL;
|
||||
}
|
||||
```
|
||||
:::note
|
||||
The thread calling `event_queue_init` becomes the owner of the event queue. Only the owner can
|
||||
process events from the queue. The ownership can be transferred to another thread using the
|
||||
`event_queue_claim` function.
|
||||
:::
|
||||
Once the thread is started and has initialized the event queue, we can post events to it
|
||||
from any other thread or interrupt context.
|
||||
The handler functions of the posted events will be executed on the event handler thread.
|
||||
|
||||
Since using a dedicated thread for handling events is common,
|
||||
RIOT provides a module which sets up event queues and a handler thread for us.
|
||||
|
||||
To use it, add the module to your application's Makefile:
|
||||
```makefile
|
||||
USEMODULE += event_thread
|
||||
```
|
||||
|
||||
Now we can post to the `EVENT_PRIO_HIGHEST`, `EVENT_PRIO_MEDIUM`, and `EVENT_PRIO_LOWEST` queues.
|
||||
A single thread will be automatically created that handles events posted to the queues
|
||||
according to their priority.
|
||||
Posting an event to one of these queues is as simple as:
|
||||
```c title="main.c"
|
||||
event_post(EVENT_PRIO_MEDIUM, &event);
|
||||
```
|
||||
## Periodic Events
|
||||
Periodic events are events that are posted to an event queue at regular intervals.
|
||||
First we need to include the module in our application's Makefile:
|
||||
```makefile
|
||||
USEMODULE += event_periodic
|
||||
```
|
||||
|
||||
To create a periodic event, we need to declare and initialize a `event_periodic_t` structure.
|
||||
```c title="main.c"
|
||||
/* This initializes the periodic event. We set the timer it should use,
|
||||
* the queue it should post to, and the event it should post.
|
||||
*/
|
||||
event_periodic_init(&periodic_event, ZTIMER_SEC, EVENT_PRIO_MEDIUM, &event);
|
||||
```
|
||||
Once the periodic event is initialized, we can start it:
|
||||
```c title="main.c"
|
||||
event_periodic_start(&periodic_event, 1);
|
||||
```
|
||||
:::note
|
||||
A periodic event can be set to repeat only a certain number of times using the
|
||||
`event_periodic_set_count` function.
|
||||
:::
|
||||
@ -137,6 +137,7 @@ export default defineConfig({
|
||||
"advanced_tutorials/creating_modules",
|
||||
"advanced_tutorials/device_drivers",
|
||||
"advanced_tutorials/porting_boards",
|
||||
"advanced_tutorials/event_queue",
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@ -155,3 +155,4 @@ Here is a quick overview of the examples available in the RIOT:
|
||||
| [saul](./guides/saul/README.md) | Teaches you how to interact with sensors and actuators through the SAUL interface. [SAUL](https://guide.riot-os.org/c_tutorials/saul/) tutorial |
|
||||
| [threads](./guides/threads/README.md) | Teaches you how to create and manage multiple execution threads in your RIOT application. [Threads](https://guide.riot-os.org/c_tutorials/threads/) tutorial |
|
||||
| [timers](./guides/timers/README.md) | Teaches you how to use timers for periodic tasks and time measurement in RIOT. [Timers](https://guide.riot-os.org/c_tutorials/timers/) tutorial |
|
||||
| [event_queue](./guides/event_queue/README.md) | Teaches you how to use the event queue. [Event Queue](https://guide.riot-os.org/advanced_tutorials/event_queue/) guide |
|
||||
|
||||
28
examples/guides/event_queue/Makefile
Normal file
28
examples/guides/event_queue/Makefile
Normal file
@ -0,0 +1,28 @@
|
||||
# name of your application
|
||||
APPLICATION = event_queue_example
|
||||
|
||||
# Change this to your board if you want to build for a different board
|
||||
BOARD ?= native
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
# If you are following the tutorial, your RIOT base directory will
|
||||
# most likely be something like RIOTBASE ?= $(CURDIR)/RIOT
|
||||
# instead of this
|
||||
RIOTBASE ?= $(CURDIR)/../../..
|
||||
|
||||
# Add the e
|
||||
USEMODULE += event_callback
|
||||
USEMODULE += event_periodic
|
||||
USEMODULE += event_thread
|
||||
|
||||
USEMODULE += ztimer_sec
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
DEVELHELP ?= 1
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
12
examples/guides/event_queue/Makefile.ci
Normal file
12
examples/guides/event_queue/Makefile.ci
Normal file
@ -0,0 +1,12 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
atmega328p-xplained-mini \
|
||||
atmega8 \
|
||||
nucleo-f031k6 \
|
||||
nucleo-l011k4 \
|
||||
samd10-xmini \
|
||||
stm32f030f4-demo \
|
||||
#
|
||||
3
examples/guides/event_queue/README.md
Normal file
3
examples/guides/event_queue/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Event Queue Guide
|
||||
This guide demonstrates how to use the event queue system to handle events in a structured manner.
|
||||
Please refer to `doc/guides/advanced_tutorials/event_queue.md` for a more detailed explanation.
|
||||
101
examples/guides/event_queue/main.c
Normal file
101
examples/guides/event_queue/main.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 TU Dresden
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "event.h"
|
||||
#include "event/thread.h"
|
||||
#include "event/periodic.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
/* A custom event structure that extends the base event structure
|
||||
* the custom event carries an additional text field.
|
||||
*/
|
||||
typedef struct {
|
||||
event_t super;
|
||||
const char *text;
|
||||
} custom_event_t;
|
||||
|
||||
/* Function to handle regular events */
|
||||
static void regular_handler(event_t *event)
|
||||
{
|
||||
(void) event;
|
||||
printf("\tTriggered regular event.\n");
|
||||
}
|
||||
|
||||
/* Function to handle custom events */
|
||||
static void custom_handler(event_t *event)
|
||||
{
|
||||
/* The handler receives a pointer to the base event structure.
|
||||
* We need to get the pointer to our custom event structure.
|
||||
*/
|
||||
custom_event_t *custom_event = container_of(event, custom_event_t, super);
|
||||
printf("\tTriggered custom event with text: \"%s\".\n", custom_event->text);
|
||||
}
|
||||
|
||||
/* Defining a regular event and setting its handler */
|
||||
static event_t event = { .handler = regular_handler };
|
||||
/* Defining a custom event and setting its handler and text */
|
||||
static custom_event_t custom_event = { .super.handler = custom_handler,
|
||||
.text = "CUSTOM EVENT" };
|
||||
/* Declaring a periodic event */
|
||||
static event_periodic_t periodic_event;
|
||||
/* Declaring an event queue */
|
||||
static event_queue_t my_queue;
|
||||
|
||||
/* This thread will handle events from the event queue */
|
||||
void *event_handler_thread(void *arg)
|
||||
{
|
||||
assert(arg != NULL);
|
||||
event_queue_t *queue = (event_queue_t *)arg;
|
||||
|
||||
/* A thread must own an event queue to process its events.
|
||||
* Ownership is initially assigned to the thread that initializes the queue.
|
||||
* It can later be transferred by calling `event_queue_claim`.
|
||||
*/
|
||||
event_queue_init(queue);
|
||||
|
||||
puts("Event handler thread listening for events...");
|
||||
event_loop(queue);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Stack for the event handler thread */
|
||||
char _event_handler_thread_stack[THREAD_STACKSIZE_MAIN];
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
||||
/* Starting the event handler thread */
|
||||
thread_create(_event_handler_thread_stack, sizeof(_event_handler_thread_stack),
|
||||
THREAD_PRIORITY_MAIN - 1, 0, event_handler_thread, &my_queue,
|
||||
"Event Handler Thread");
|
||||
|
||||
puts("Posting regular event...");
|
||||
/* Posting a regular event to the queue handled by our event handler thread */
|
||||
event_post(&my_queue, &event);
|
||||
ztimer_sleep(ZTIMER_SEC, 1);
|
||||
|
||||
puts("Posting custom event...");
|
||||
/* Posting a custom event to the queue handled by our event handler thread */
|
||||
event_post(&my_queue, &custom_event.super);
|
||||
ztimer_sleep(ZTIMER_SEC, 1);
|
||||
|
||||
puts("Posting regular event to the medium priority event thread...");
|
||||
/* Posting a regular event to medium priority queue.
|
||||
* This queue will initialized and handled by the `event_thread` module.
|
||||
*/
|
||||
event_post(EVENT_PRIO_MEDIUM, &event);
|
||||
ztimer_sleep(ZTIMER_SEC, 1);
|
||||
|
||||
puts("Starting periodic event that posts regular events every second...");
|
||||
/* This initializes the periodic event. We set the timer it should use,
|
||||
* the queue it should post to, and the event it should post.
|
||||
*/
|
||||
event_periodic_init(&periodic_event, ZTIMER_SEC, EVENT_PRIO_MEDIUM, &event);
|
||||
/* This starts the periodic event with a period of 1 second. */
|
||||
event_periodic_start(&periodic_event, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user