1FreeRTOS Additions 2================== 3 4Overview 5-------- 6 7ESP-IDF FreeRTOS is based on the Xtensa port of FreeRTOS v10.2.0 with significant modifications 8for SMP compatibility (see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`). 9However various features specific to ESP-IDF FreeRTOS have been added. The features are as follows: 10 11:ref:`ring-buffers`: Ring buffers were added to provide a form of buffer that could accept 12entries of arbitrary lengths. 13 14:ref:`hooks`: ESP-IDF FreeRTOS hooks provides support for registering extra Idle and 15Tick hooks at run time. Moreover, the hooks can be asymmetric amongst both CPUs. 16 17:ref:`component-specific-properties`: Currently added only one component specific property `ORIG_INCLUDE_PATH`. 18 19 20.. _ring-buffers: 21 22Ring Buffers 23------------ 24 25The ESP-IDF FreeRTOS ring buffer is a strictly FIFO buffer that supports arbitrarily sized items. 26Ring buffers are a more memory efficient alternative to FreeRTOS queues in situations where the 27size of items is variable. The capacity of a ring buffer is not measured by the number of items 28it can store, but rather by the amount of memory used for storing items. The ring buffer provides API 29to send an item, or to allocate space for an item in the ring buffer to be filled manually by the user. 30For efficiency reasons, 31**items are always retrieved from the ring buffer by reference**. As a result, all retrieved 32items *must also be returned* to the ring buffer by using :cpp:func:`vRingbufferReturnItem` or :cpp:func:`vRingbufferReturnItemFromISR`, in order for them to be removed from the ring buffer completely. 33The ring buffers are split into the three following types: 34 35**No-Split** buffers will guarantee that an item is stored in contiguous memory and will not 36attempt to split an item under any circumstances. Use no-split buffers when items must occupy 37contiguous memory. *Only this buffer type allows you getting the data item address and writting 38to the item by yourself.* 39 40**Allow-Split** buffers will allow an item to be split when wrapping around if doing so will allow 41the item to be stored. Allow-split buffers are more memory efficient than no-split buffers but 42can return an item in two parts when retrieving. 43 44**Byte buffers** do not store data as separate items. All data is stored as a sequence of bytes, 45and any number of bytes and be sent or retrieved each time. Use byte buffers when separate items 46do not need to be maintained (e.g. a byte stream). 47 48.. note:: 49 No-split/allow-split buffers will always store items at 32-bit aligned addresses. Therefore when 50 retrieving an item, the item pointer is guaranteed to be 32-bit aligned. This is useful 51 especially when you need to send some data to the DMA. 52 53.. note:: 54 Each item stored in no-split/allow-split buffers will **require an additional 8 bytes for a header**. 55 Item sizes will also be rounded up to a 32-bit aligned size (multiple of 4 bytes), however the true 56 item size is recorded within the header. The sizes of no-split/allow-split buffers will also 57 be rounded up when created. 58 59Usage 60^^^^^ 61 62The following example demonstrates the usage of :cpp:func:`xRingbufferCreate` 63and :cpp:func:`xRingbufferSend` to create a ring buffer then send an item to it. 64 65.. code-block:: c 66 67 #include "freertos/ringbuf.h" 68 static char tx_item[] = "test_item"; 69 70 ... 71 72 //Create ring buffer 73 RingbufHandle_t buf_handle; 74 buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT); 75 if (buf_handle == NULL) { 76 printf("Failed to create ring buffer\n"); 77 } 78 79 //Send an item 80 UBaseType_t res = xRingbufferSend(buf_handle, tx_item, sizeof(tx_item), pdMS_TO_TICKS(1000)); 81 if (res != pdTRUE) { 82 printf("Failed to send item\n"); 83 } 84 85The following example demonstrates the usage of :cpp:func:`xRingbufferSendAcquire` and 86:cpp:func:`xRingbufferSendComplete` instead of :cpp:func:`xRingbufferSend` to apply for the 87memory on the ring buffer (of type `RINGBUF_TYPE_NOSPLIT`) and then send an item to it. This way 88adds one more step, but allows getting the address of the memory to write to, and writing to the 89memory yourself. 90 91.. code-block:: c 92 93 #include "freertos/ringbuf.h" 94 #include "soc/lldesc.h" 95 96 typedef struct { 97 lldesc_t dma_desc; 98 uint8_t buf[1]; 99 } dma_item_t; 100 101 #define DMA_ITEM_SIZE(N) (sizeof(lldesc_t)+(((N)+3)&(~3))) 102 103 ... 104 105 //Retrieve space for DMA descriptor and corresponding data buffer 106 //This has to be done with SendAcquire, or the address may be different when copy 107 dma_item_t item; 108 UBaseType_t res = xRingbufferSendAcquire(buf_handle, 109 &item, DMA_ITEM_SIZE(buffer_size), pdMS_TO_TICKS(1000)); 110 if (res != pdTRUE) { 111 printf("Failed to acquire memory for item\n"); 112 } 113 item->dma_desc = (lldesc_t) { 114 .size = buffer_size, 115 .length = buffer_size, 116 .eof = 0, 117 .owner = 1, 118 .buf = &item->buf, 119 }; 120 //Actually send to the ring buffer for consumer to use 121 res = xRingbufferSendComplete(buf_handle, &item); 122 if (res != pdTRUE) { 123 printf("Failed to send item\n"); 124 } 125 126The following example demonstrates retrieving and returning an item from a **no-split ring buffer** 127using :cpp:func:`xRingbufferReceive` and :cpp:func:`vRingbufferReturnItem` 128 129.. code-block:: c 130 131 ... 132 133 //Receive an item from no-split ring buffer 134 size_t item_size; 135 char *item = (char *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(1000)); 136 137 //Check received item 138 if (item != NULL) { 139 //Print item 140 for (int i = 0; i < item_size; i++) { 141 printf("%c", item[i]); 142 } 143 printf("\n"); 144 //Return Item 145 vRingbufferReturnItem(buf_handle, (void *)item); 146 } else { 147 //Failed to receive item 148 printf("Failed to receive item\n"); 149 } 150 151 152The following example demonstrates retrieving and returning an item from an **allow-split ring buffer** 153using :cpp:func:`xRingbufferReceiveSplit` and :cpp:func:`vRingbufferReturnItem` 154 155.. code-block:: c 156 157 ... 158 159 //Receive an item from allow-split ring buffer 160 size_t item_size1, item_size2; 161 char *item1, *item2; 162 BaseType_t ret = xRingbufferReceiveSplit(buf_handle, (void **)&item1, (void **)&item2, &item_size1, &item_size2, pdMS_TO_TICKS(1000)); 163 164 //Check received item 165 if (ret == pdTRUE && item1 != NULL) { 166 for (int i = 0; i < item_size1; i++) { 167 printf("%c", item1[i]); 168 } 169 vRingbufferReturnItem(buf_handle, (void *)item1); 170 //Check if item was split 171 if (item2 != NULL) { 172 for (int i = 0; i < item_size2; i++) { 173 printf("%c", item2[i]); 174 } 175 vRingbufferReturnItem(buf_handle, (void *)item2); 176 } 177 printf("\n"); 178 } else { 179 //Failed to receive item 180 printf("Failed to receive item\n"); 181 } 182 183 184The following example demonstrates retrieving and returning an item from a **byte buffer** 185using :cpp:func:`xRingbufferReceiveUpTo` and :cpp:func:`vRingbufferReturnItem` 186 187.. code-block:: c 188 189 ... 190 191 //Receive data from byte buffer 192 size_t item_size; 193 char *item = (char *)xRingbufferReceiveUpTo(buf_handle, &item_size, pdMS_TO_TICKS(1000), sizeof(tx_item)); 194 195 //Check received data 196 if (item != NULL) { 197 //Print item 198 for (int i = 0; i < item_size; i++) { 199 printf("%c", item[i]); 200 } 201 printf("\n"); 202 //Return Item 203 vRingbufferReturnItem(buf_handle, (void *)item); 204 } else { 205 //Failed to receive item 206 printf("Failed to receive item\n"); 207 } 208 209 210For ISR safe versions of the functions used above, call :cpp:func:`xRingbufferSendFromISR`, :cpp:func:`xRingbufferReceiveFromISR`, 211:cpp:func:`xRingbufferReceiveSplitFromISR`, :cpp:func:`xRingbufferReceiveUpToFromISR`, and :cpp:func:`vRingbufferReturnItemFromISR` 212 213.. note:: 214 215 Two calls to RingbufferReceive[UpTo][FromISR]() are required if the bytes wraps around the end of the ring buffer. 216 217Sending to Ring Buffer 218^^^^^^^^^^^^^^^^^^^^^^ 219 220The following diagrams illustrate the differences between no-split/allow-split buffers 221and byte buffers with regards to sending items/data. The diagrams assume that three 222items of sizes **18, 3, and 27 bytes** are sent respectively to a **buffer of 128 bytes**. 223 224.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag 225 :caption: Sending items to no-split/allow-split ring buffers 226 :align: center 227 228For no-split/allow-split buffers, a header of 8 bytes precedes every data item. Furthermore, the space 229occupied by each item is **rounded up to the nearest 32-bit aligned size** in order to maintain overall 23032-bit alignment. However the true size of the item is recorded inside the header which will be 231returned when the item is retrieved. 232 233Referring to the diagram above, the 18, 3, and 27 byte items are **rounded up to 20, 4, and 28 bytes** 234respectively. An 8 byte header is then added in front of each item. 235 236.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag 237 :caption: Sending items to byte buffers 238 :align: center 239 240Byte buffers treat data as a sequence of bytes and does not incur any overhead 241(no headers). As a result, all data sent to a byte buffer is merged into a single item. 242 243Referring to the diagram above, the 18, 3, and 27 byte items are sequentially written to the 244byte buffer and **merged into a single item of 48 bytes**. 245 246Using SendAcquire and SendComplete 247^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 248 249Items in no-split buffers are acquired (by SendAcquire) in strict FIFO order and must be sent to 250the buffer by SendComplete for the data to be accessible by the consumer. Multiple items can be 251sent or acquired without calling SendComplete, and the items do not necessarily need to be 252completed in the order they were acquired. However the receiving of data items must occur in FIFO 253order, therefore not calling SendComplete the earliest acquired item will prevent the subsequent 254items from being received. 255 256The following diagrams illustrate what will happen when SendAcquire/SendComplete don't happen in 257the same order. At the beginning, there is already an data item of 16 bytes sent to the ring 258buffer. Then SendAcquire is called to acquire space of 20, 8, 24 bytes on the ring buffer. 259 260.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_acquire_complete.diag 261 :caption: SendAcquire/SendComplete items in no-split ring buffers 262 :align: center 263 264After that, we fill (use) the buffers, and send them to the ring buffer by SendComplete in the 265order of 8, 24, 20. When 8 bytes and 24 bytes data are sent, the consumer still can only get the 26616 bytes data item. Due to the usage if 20 bytes item is not complete, it's not available, nor 267the following data items. 268 269When the 20 bytes item is finally completed, all the 3 data items can be received now, in the 270order of 20, 8, 24 bytes, right after the 16 bytes item existing in the buffer at the beginning. 271 272Allow-split/byte buffers do not allow using SendAcquire/SendComplete since acquired buffers are 273required to be complete (not wrapped). 274 275 276Wrap around 277^^^^^^^^^^^ 278 279The following diagrams illustrate the differences between no-split, allow-split, and byte 280buffers when a sent item requires a wrap around. The diagrams assumes a buffer of **128 bytes** 281with **56 bytes of free space that wraps around** and a sent item of **28 bytes**. 282 283.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag 284 :caption: Wrap around in no-split buffers 285 :align: center 286 287No-split buffers will **only store an item in continuous free space and will not split 288an item under any circumstances**. When the free space at the tail of the buffer is insufficient 289to completely store the item and its header, the free space at the tail will be **marked as dummy data**. 290The buffer will then wrap around and store the item in the free space at the head of the buffer. 291 292Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is 293insufficient to store the 28 byte item. Therefore the 16 bytes is marked as dummy data and 294the item is written to the free space at the head of the buffer instead. 295 296.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag 297 :caption: Wrap around in allow-split buffers 298 :align: center 299 300Allow-split buffers will attempt to **split the item into two parts** when the free space at the tail 301of the buffer is insufficient to store the item data and its header. Both parts of the 302split item will have their own headers (therefore incurring an extra 8 bytes of overhead). 303 304Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient 305to store the 28 byte item. Therefore the item is split into two parts (8 and 20 bytes) and written 306as two parts to the buffer. 307 308.. note:: 309 Allow-split buffers treats the both parts of the split item as two separate items, therefore call 310 :cpp:func:`xRingbufferReceiveSplit` instead of :cpp:func:`xRingbufferReceive` to receive both 311 parts of a split item in a thread safe manner. 312 313.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag 314 :caption: Wrap around in byte buffers 315 :align: center 316 317Byte buffers will **store as much data as possible into the free space at the tail of buffer**. The remaining 318data will then be stored in the free space at the head of the buffer. No overhead is incurred when wrapping 319around in byte buffers. 320 321Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient to 322completely store the 28 bytes of data. Therefore the 16 bytes of free space is filled with data, and the 323remaining 12 bytes are written to the free space at the head of the buffer. The buffer now contains 324data in two separate continuous parts, and each part continuous will be treated as a separate item by the 325byte buffer. 326 327Retrieving/Returning 328^^^^^^^^^^^^^^^^^^^^ 329 330The following diagrams illustrates the differences between no-split/allow-split and 331byte buffers in retrieving and returning data. 332 333.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag 334 :caption: Retrieving/Returning items in no-split/allow-split ring buffers 335 :align: center 336 337Items in no-split/allow-split buffers are **retrieved in strict FIFO order** and **must be returned** 338for the occupied space to be freed. Multiple items can be retrieved before returning, and the items 339do not necessarily need to be returned in the order they were retrieved. However the freeing of space 340must occur in FIFO order, therefore not returning the earliest retrieved item will prevent the space 341of subsequent items from being freed. 342 343Referring to the diagram above, the **16, 20, and 8 byte items are retrieved in FIFO order**. However the items 344are not returned in they were retrieved (20, 8, 16). As such, the space is not freed until the first item 345(16 byte) is returned. 346 347.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag 348 :caption: Retrieving/Returning data in byte buffers 349 :align: center 350 351Byte buffers **do not allow multiple retrievals before returning** (every retrieval must be followed by a return 352before another retrieval is permitted). When using :cpp:func:`xRingbufferReceive` or 353:cpp:func:`xRingbufferReceiveFromISR`, all continuous stored data will be retrieved. :cpp:func:`xRingbufferReceiveUpTo` 354or :cpp:func:`xRingbufferReceiveUpToFromISR` can be used to restrict the maximum number of bytes retrieved. Since 355every retrieval must be followed by a return, the space will be freed as soon as the data is returned. 356 357Referring to the diagram above, the 38 bytes of continuous stored data at the tail of the buffer is retrieved, 358returned, and freed. The next call to :cpp:func:`xRingbufferReceive` or :cpp:func:`xRingbufferReceiveFromISR` 359then wraps around and does the same to the 30 bytes of continuous stored data at the head of the buffer. 360 361Ring Buffers with Queue Sets 362^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 363 364Ring buffers can be added to FreeRTOS queue sets using :cpp:func:`xRingbufferAddToQueueSetRead` such that every 365time a ring buffer receives an item or data, the queue set is notified. Once added to a queue set, every 366attempt to retrieve an item from a ring buffer should be preceded by a call to :cpp:func:`xQueueSelectFromSet`. 367To check whether the selected queue set member is the ring buffer, call :cpp:func:`xRingbufferCanRead`. 368 369The following example demonstrates queue set usage with ring buffers. 370 371.. code-block:: c 372 373 #include "freertos/queue.h" 374 #include "freertos/ringbuf.h" 375 376 ... 377 378 //Create ring buffer and queue set 379 RingbufHandle_t buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT); 380 QueueSetHandle_t queue_set = xQueueCreateSet(3); 381 382 //Add ring buffer to queue set 383 if (xRingbufferAddToQueueSetRead(buf_handle, queue_set) != pdTRUE) { 384 printf("Failed to add to queue set\n"); 385 } 386 387 ... 388 389 //Block on queue set 390 xQueueSetMemberHandle member = xQueueSelectFromSet(queue_set, pdMS_TO_TICKS(1000)); 391 392 //Check if member is ring buffer 393 if (member != NULL && xRingbufferCanRead(buf_handle, member) == pdTRUE) { 394 //Member is ring buffer, receive item from ring buffer 395 size_t item_size; 396 char *item = (char *)xRingbufferReceive(buf_handle, &item_size, 0); 397 398 //Handle item 399 ... 400 401 } else { 402 ... 403 } 404 405Ring Buffers with Static Allocation 406^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 407 408The :cpp:func:`xRingbufferCreateStatic` can be used to create ring buffers with specific memory requirements (such as a ring buffer being allocated in external RAM). All blocks of memory used by a ring buffer must be manually allocated beforehand then passed to the :cpp:func:`xRingbufferCreateStatic` to be initialized as a ring buffer. These blocks include the following: 409 410- The ring buffer's data structure of type :cpp:type:`StaticRingbuffer_t` 411- The ring buffer's storage area of size ``xBufferSize``. Note that ``xBufferSize`` must be 32-bit aligned for no-split/allow-split buffers. 412 413The manner in which these blocks are allocated will depend on the users requirements (e.g. all blocks being statically declared, or dynamically allocated with specific capabilities such as external RAM). 414 415.. note:: 416 When deleting a ring buffer created via :cpp:func:`xRingbufferCreateStatic`, 417 the function :cpp:func:`vRingbufferDelete` will not free any of the memory blocks. This must be done manually by the user after :cpp:func:`vRingbufferDelete` is called. 418 419The code snippet below demonstrates a ring buffer being allocated entirely in external RAM. 420 421.. code-block:: c 422 423 #include "freertos/ringbuf.h" 424 #include "freertos/semphr.h" 425 #include "esp_heap_caps.h" 426 427 #define BUFFER_SIZE 400 //32-bit aligned size 428 #define BUFFER_TYPE RINGBUF_TYPE_NOSPLIT 429 ... 430 431 //Allocate ring buffer data structure and storage area into external RAM 432 StaticRingbuffer_t *buffer_struct = (StaticRingbuffer_t *)heap_caps_malloc(sizeof(StaticRingbuffer_t), MALLOC_CAP_SPIRAM); 433 uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*BUFFER_SIZE, MALLOC_CAP_SPIRAM); 434 435 //Create a ring buffer with manually allocated memory 436 RingbufHandle_t handle = xRingbufferCreateStatic(BUFFER_SIZE, BUFFER_TYPE, buffer_storage, buffer_struct); 437 438 ... 439 440 //Delete the ring buffer after used 441 vRingbufferDelete(handle); 442 443 //Manually free all blocks of memory 444 free(buffer_struct); 445 free(buffer_storage); 446 447 448Ring Buffer API Reference 449------------------------- 450 451.. note:: 452 Ideally, ring buffers can be used with multiple tasks in an SMP fashion where the **highest 453 priority task will always be serviced first.** However due to the usage of binary semaphores 454 in the ring buffer's underlying implementation, priority inversion may occur under very 455 specific circumstances. 456 457 The ring buffer governs sending by a binary semaphore which is given whenever space is 458 freed on the ring buffer. The highest priority task waiting to send will repeatedly take 459 the semaphore until sufficient free space becomes available or until it times out. Ideally 460 this should prevent any lower priority tasks from being serviced as the semaphore should 461 always be given to the highest priority task. 462 463 However in between iterations of acquiring the semaphore, there is a **gap in the critical 464 section** which may permit another task (on the other core or with an even higher priority) to 465 free some space on the ring buffer and as a result give the semaphore. Therefore the semaphore 466 will be given before the highest priority task can re-acquire the semaphore. This will result 467 in the **semaphore being acquired by the second highest priority task** waiting to send, hence 468 causing priority inversion. 469 470 This side effect will not affect ring buffer performance drastically given if the number 471 of tasks using the ring buffer simultaneously is low, and the ring buffer is not operating 472 near maximum capacity. 473 474.. include-build-file:: inc/ringbuf.inc 475 476 477.. _hooks: 478 479Hooks 480----- 481 482FreeRTOS consists of Idle Hooks and Tick Hooks which allow for application 483specific functionality to be added to the Idle Task and Tick Interrupt. 484ESP-IDF provides its own Idle and Tick Hook API in addition to the hooks 485provided by Vanilla FreeRTOS. ESP-IDF hooks have the added benefit of 486being run time configurable and asymmetrical. 487 488Vanilla FreeRTOS Hooks 489^^^^^^^^^^^^^^^^^^^^^^ 490 491Idle and Tick Hooks in vanilla FreeRTOS are implemented by the user 492defining the functions ``vApplicationIdleHook()`` and ``vApplicationTickHook()`` 493respectively somewhere in the application. Vanilla FreeRTOS will run the user 494defined Idle Hook and Tick Hook on every iteration of the Idle Task and Tick 495Interrupt respectively. 496 497Vanilla FreeRTOS hooks are referred to as **Legacy Hooks** in ESP-IDF FreeRTOS. 498To enable legacy hooks, :ref:`CONFIG_FREERTOS_LEGACY_HOOKS` should be enabled 499in :doc:`project configuration menu </api-reference/kconfig>`. 500 501Due to vanilla FreeRTOS being designed for single core, ``vApplicationIdleHook()`` 502and ``vApplicationTickHook()`` can only be defined once. However, the ESP32 is dual core 503in nature, therefore same Idle Hook and Tick Hook are used for both cores (in other words, 504the hooks are symmetrical for both cores). 505 506In a dual core system, ``vApplicationTickHook()`` must be located in IRAM (for example 507by adding the IRAM_ATTR attribute). 508 509ESP-IDF Idle and Tick Hooks 510^^^^^^^^^^^^^^^^^^^^^^^^^^^ 511Due to the the dual core nature of the ESP32, it may be necessary for some 512applications to have separate hooks for each core. Furthermore, it may 513be necessary for the Idle Tasks or Tick Interrupts to execute multiple hooks 514that are configurable at run time. Therefore the ESP-IDF provides it's own hooks 515API in addition to the legacy hooks provided by Vanilla FreeRTOS. 516 517The ESP-IDF tick/idle hooks are registered at run time, and each tick/idle hook 518must be registered to a specific CPU. When the idle task runs/tick Interrupt 519occurs on a particular CPU, the CPU will run each of its registered idle/tick hooks 520in turn. 521 522 523Hooks API Reference 524------------------- 525 526.. include-build-file:: inc/esp_freertos_hooks.inc 527 528 529.. _component-specific-properties: 530 531Component Specific Properties 532----------------------------- 533 534Besides standart component variables that could be gotten with basic cmake build properties FreeRTOS component also provides an arguments (only one so far) for simpler integration with other modules: 535 536- `ORIG_INCLUDE_PATH` - contains an absolute path to freertos root include folder. Thus instead of `#include "freertos/FreeRTOS.h"` you can refer to headers directly: `#include "FreeRTOS.h"`. 537