|
19 | 19 | extern "C" {
|
20 | 20 | #endif
|
21 | 21 |
|
| 22 | +/** |
| 23 | + * \defgroup nanostack-eventloop Nanostack's event system. |
| 24 | + * Small event scheduler and timer system written in C. |
| 25 | + * |
| 26 | + * This event system is originating from project called Nanostack and developed within Arm. Therefore |
| 27 | + * some of the types and names within this library are prefixed with `ns_*` or `arm_*` or `eventOS*`. |
| 28 | + * |
| 29 | + * <h3>Concept</h3> |
| 30 | + * |
| 31 | + * Event loop uses a concept called tasklet, which is just a callback function that receives events. |
| 32 | + * There can be as many as 128 tasklets registered if memory allows. This is only limited by event ID being just 8-bits. |
| 33 | + * Each tasklet is first registered to the event system, which then gives 8 bit ID number for the tasklet. |
| 34 | + * |
| 35 | + * @startuml |
| 36 | + * package "eventOS" { |
| 37 | + * [eventOS_event.h] - event_handler_create |
| 38 | + * } |
| 39 | + * node "application" { |
| 40 | + * [tasklet1.cpp] ..> event_handler_create : register |
| 41 | + * [tasklet1.cpp] - tasklet1 |
| 42 | + * [tasklet2.cpp] ..> event_handler_create : register |
| 43 | + * [tasklet2.cpp] - tasklet2 |
| 44 | + * [tasklet3.cpp] ..> event_handler_create : register |
| 45 | + * [tasklet3.cpp] - tasklet3 |
| 46 | + * } |
| 47 | + * @enduml |
| 48 | + * |
| 49 | + * Events are send to a specific tasklet, identified by its ID. |
| 50 | + * Each event is coded into a \ref arm_event_s structure which is then pushed into event loop by calling eventOS_event_send(). |
| 51 | + * |
| 52 | + * @startuml |
| 53 | + * partition tasklet1.cpp { |
| 54 | + * (*) --> tasklet1 |
| 55 | + * } |
| 56 | + * partition "eventOS" { |
| 57 | + * tasklet1 -->[event:\nreceiver: 3\nevent_id: 1] eventOS_event_send |
| 58 | + * } |
| 59 | + * partition tasklet3.cpp { |
| 60 | + * eventOS_event_send -->[event:\nreceiver: 3\nevent_id: 1] tasklet3 |
| 61 | + * } |
| 62 | + * @enduml |
| 63 | + * |
| 64 | + * <h3>Usage</h3> |
| 65 | + * |
| 66 | + * To send or receive events, you first need to register your event handler. |
| 67 | + * \code |
| 68 | + * // In header |
| 69 | + * extern uint8_t my_eventhandler_id; |
| 70 | + * #define INITIALIZATION_EVENT 0 |
| 71 | + * #define MY_EVENT 1 |
| 72 | + * |
| 73 | + * // In my_handler.cpp |
| 74 | + * void my_event_handler(arm_event_t *e) |
| 75 | + * { |
| 76 | + * switch (e->event_type) { |
| 77 | + * case INITIALIZATION_EVENT: |
| 78 | + * // Initialize my module |
| 79 | + * break; |
| 80 | + * case MY_EVENT: |
| 81 | + * // Event received |
| 82 | + * break; |
| 83 | + * } |
| 84 | + * } |
| 85 | + * |
| 86 | + * // Register the handler |
| 87 | + * my_eventhandler_id = eventOS_event_handler_create(my_event_handler, INITIALIZATION_EVENT); |
| 88 | + * if (my_eventhandler_id < 0) { |
| 89 | + * // fail |
| 90 | + * } |
| 91 | + * \endcode |
| 92 | + * |
| 93 | + * Each event is basically a \ref arm_event_s structure. You need to fill in the arm_event_s::receiver field. |
| 94 | + * Rest of the fields are optional, and used only by the receiving callback. So you have different options to |
| 95 | + * deliver data to a receiving tasklet. The structure is copied by the event system, so temporary storage may be used, |
| 96 | + * and the structure may be freed after it has been pushed into event system. |
| 97 | + * |
| 98 | + * \code |
| 99 | + * // Send the event |
| 100 | + * arm_event_t e = { |
| 101 | + * .receiver = my_eventhandler_id, |
| 102 | + * .event_type = MY_EVENT |
| 103 | + * }; |
| 104 | + * |
| 105 | + * if (eventOS_event_send(e) != 0) { |
| 106 | + * // fail |
| 107 | + * } |
| 108 | + * \endcode |
| 109 | + * |
| 110 | + * Where required, event system allows you to delay the event propagation. |
| 111 | + * |
| 112 | + * \code |
| 113 | + * // Wait 3 seconds before the event |
| 114 | + * #define MY_DELAY_MS 3000 |
| 115 | + * |
| 116 | + * arm_event_t e = { |
| 117 | + * .receiver = my_eventhandler_id, |
| 118 | + * .event_type = MY_EVENT |
| 119 | + * }; |
| 120 | + * |
| 121 | + * uint32_t delay = eventOS_event_timer_ms_to_ticks(MY_DELAY_MS); |
| 122 | + * eventOS_event_send_after(e, delay); |
| 123 | + * \endcode |
| 124 | + * |
| 125 | + * \sa eventOS_event.h |
| 126 | + * \sa eventOS_event_send_at |
| 127 | + * \sa eventOS_event_send_in |
| 128 | + * \sa eventOS_event_send_after |
| 129 | + * \sa eventOS_event_send_every |
| 130 | + * |
| 131 | + * <h3>Pre-allocated events</h3> |
| 132 | + * |
| 133 | + * Two options are provided to limit the heap usage. First option is to use recurring events with eventOS_event_send_every(), |
| 134 | + * so your event is only allocated once. This allows you to create application that does not use heap after initialization phase. |
| 135 | + * |
| 136 | + * Second option is to use pre-allocated or statically allocated event structure. In this model you create a space for |
| 137 | + * \ref arm_event_storage structure and send events using eventOS_event_send_user_allocated() call. This is also |
| 138 | + * very robust, as there is no allocation, so the sending of the event will never fail because of lack of memory. |
| 139 | + * |
| 140 | + * \code |
| 141 | + * static bool pending = false; |
| 142 | + * static arm_event_storage_t e; |
| 143 | + * static int8_t foo_tasklet_id; |
| 144 | + * |
| 145 | + * void notify_foo() |
| 146 | + * { |
| 147 | + * if (!pending) { |
| 148 | + * pending = true; |
| 149 | + * e.data.receiver = foo_tasklet_id; |
| 150 | + * e.data.type = MY_EVENT; |
| 151 | + * eventOS_event_send_user_allocated(&e); |
| 152 | + * } |
| 153 | + * } |
| 154 | + * |
| 155 | + * void foo_event_handler(arm_event_t *e) |
| 156 | + * { |
| 157 | + * pending = false; |
| 158 | + * // ... |
| 159 | + * } |
| 160 | + * |
| 161 | + * \endcode |
| 162 | + * |
| 163 | + * <h3>Initialization</h3> |
| 164 | + * |
| 165 | + * Event system does not use malloc(), free() or any system heap directly, but uses nsdynmemLIB.h library instead. |
| 166 | + * Event system must first be initialized by callind eventOS_scheduler_init(). This is usually done just after ns_dyn_mem_init() call. |
| 167 | + * Where porting is already provided, these both are initialized in function called ns_hal_init(). |
| 168 | + * |
| 169 | + * After initialization, you can start the event loop by calling eventOS_scheduler_run() which will never return. This is usually |
| 170 | + * end of the `main()` function. |
| 171 | + * |
| 172 | + * \code |
| 173 | + * extern void my_event_handler(arm_event_t *e); |
| 174 | + * extern int8_t my_eventhandler_id; |
| 175 | + * |
| 176 | + * void main(void) |
| 177 | + * { |
| 178 | + * ns_dyn_mem_init(NULL, HEAP_SIZE, NULL, NULL); |
| 179 | + * eventOS_scheduler_init(); |
| 180 | + * my_eventhandler_id = eventOS_event_handler_create(my_event_handler, INITIALIZATION_EVENT); |
| 181 | + * eventOS_scheduler_run() |
| 182 | + * } |
| 183 | + * \endcode |
| 184 | + */ |
| 185 | + |
| 186 | +/** |
| 187 | + * \file eventOS_event.h |
| 188 | + * \ingroup nanostack-eventloop |
| 189 | + * \brief Nanostack's event loop. |
| 190 | + */ |
| 191 | + |
22 | 192 | #include "ns_types.h"
|
23 | 193 | #include "ns_list.h"
|
24 | 194 |
|
@@ -100,10 +270,10 @@ extern int8_t eventOS_event_send(const arm_event_t *event);
|
100 | 270 |
|
101 | 271 | /* Alternate names for timer function from eventOS_event_timer.h;
|
102 | 272 | * implementations may one day merge */
|
103 |
| -#define eventOS_event_send_at(event, at) eventOS_event_timer_request_at(event, at) |
104 |
| -#define eventOS_event_send_in(event, in) eventOS_event_timer_request_in(event, in) |
105 |
| -#define eventOS_event_send_after(event, after) eventOS_event_timer_request_after(event, after) |
106 |
| -#define eventOS_event_send_every(event, every) eventOS_event_timer_request_every(event, every) |
| 273 | +#define eventOS_event_send_at(event, at) eventOS_event_timer_request_at(event, at) ///< \copydoc eventOS_event_timer_request_at |
| 274 | +#define eventOS_event_send_in(event, in) eventOS_event_timer_request_in(event, in) ///< \copydoc eventOS_event_timer_request_in |
| 275 | +#define eventOS_event_send_after(event, after) eventOS_event_timer_request_after(event, after) ///< \copydoc eventOS_event_timer_request_after |
| 276 | +#define eventOS_event_send_every(event, every) eventOS_event_timer_request_every(event, every) ///< \copydoc eventOS_event_timer_request_every |
107 | 277 |
|
108 | 278 | /**
|
109 | 279 | * \brief Send user-allocated event to event scheduler.
|
|
0 commit comments