freertos_additions.rst 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. FreeRTOS Additions
  2. ==================
  3. Overview
  4. --------
  5. ESP-IDF FreeRTOS is based on the Xtensa port of FreeRTOS v8.2.0 with significant modifications
  6. for SMP compatibility (see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`).
  7. However various features specific to ESP-IDF FreeRTOS have been added. The features are as follows:
  8. :ref:`ring-buffers`: Ring buffers were added to provide a form of buffer that could accept
  9. entries of arbitrary lengths.
  10. :ref:`hooks`: ESP-IDF FreeRTOS hooks provides support for registering extra Idle and
  11. Tick hooks at run time. Moreover, the hooks can be asymmetric amongst both CPUs.
  12. .. _ring-buffers:
  13. Ring Buffers
  14. ------------
  15. The ESP-IDF FreeRTOS ring buffer is a strictly FIFO buffer that supports arbitrarily sized items.
  16. Ring buffers are a more memory efficient alternative to FreeRTOS queues in situations where the
  17. size of items is variable. The capacity of a ring buffer is not measured by the number of items
  18. it can store, but rather by the amount of memory used for storing items. Items are sent to
  19. ring buffers by copy, however for efficiency reasons **items are retrieved by reference**. As a
  20. result, all retrieved items **must also be returned** in order for them to be removed from
  21. the ring buffer completely. The ring buffers are split into the three following types:
  22. **No-Split** buffers will guarantee that an item is stored in contiguous memory and will not
  23. attempt to split an item under any circumstances. Use no-split buffers when items must occupy
  24. contiguous memory.
  25. **Allow-Split** buffers will allow an item to be split when wrapping around if doing so will allow
  26. the item to be stored. Allow-split buffers are more memory efficient than no-split buffers but
  27. can return an item in two parts when retrieving.
  28. **Byte buffers** do not store data as separate items. All data is stored as a sequence of bytes,
  29. and any number of bytes and be sent or retrieved each time. Use byte buffers when separate items
  30. do not need to be maintained (e.g. a byte stream).
  31. .. note::
  32. No-split/allow-split buffers will always store items at 32-bit aligned addresses. Therefore when
  33. retrieving an item, the item pointer is guaranteed to be 32-bit aligned.
  34. .. note::
  35. Each item stored in no-split/allow-split buffers will **require an additional 8 bytes for a header**.
  36. Item sizes will also be rounded up to a 32-bit aligned size (multiple of 4 bytes), however the true
  37. item size is recorded within the header. The sizes of no-split/allow-split buffers will also
  38. be rounded up when created.
  39. Usage
  40. ^^^^^
  41. The following example demonstrates the usage of :cpp:func:`xRingbufferCreate`
  42. and :cpp:func:`xRingbufferSend` to create a ring buffer then send an item to it.
  43. .. code-block:: c
  44. #include "freertos/ringbuf.h"
  45. static char tx_item[] = "test_item";
  46. ...
  47. //Create ring buffer
  48. RingbufHandle_t buf_handle;
  49. buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT);
  50. if (buf_handle == NULL) {
  51. printf("Failed to create ring buffer\n");
  52. }
  53. //Send an item
  54. UBaseType_t res = xRingbufferSend(buf_handle, tx_item, sizeof(tx_item), pdMS_TO_TICKS(1000));
  55. if (res != pdTRUE) {
  56. printf("Failed to send item\n");
  57. }
  58. The following example demonstrates retrieving and returning an item from a **no-split ring buffer**
  59. using :cpp:func:`xRingbufferReceive` and :cpp:func:`vRingbufferReturnItem`
  60. .. code-block:: c
  61. ...
  62. //Receive an item from no-split ring buffer
  63. size_t item_size;
  64. char *item = (char *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(1000));
  65. //Check received item
  66. if (item != NULL) {
  67. //Print item
  68. for (int i = 0; i < item_size; i++) {
  69. printf("%c", item[i]);
  70. }
  71. printf("\n");
  72. //Return Item
  73. vRingbufferReturnItem(buf_handle, (void *)item);
  74. } else {
  75. //Failed to receive item
  76. printf("Failed to receive item\n");
  77. }
  78. The following example demonstrates retrieving and returning an item from an **allow-split ring buffer**
  79. using :cpp:func:`xRingbufferReceiveSplit` and :cpp:func:`vRingbufferReturnItem`
  80. .. code-block:: c
  81. ...
  82. //Receive an item from allow-split ring buffer
  83. size_t item_size1, item_size2;
  84. char *item1, *item2;
  85. BaseType_t ret = xRingbufferReceiveSplit(buf_handle, (void **)&item1, (void **)&item2, &item_size1, &item_size2, pdMS_TO_TICKS(1000));
  86. //Check received item
  87. if (ret == pdTRUE && item1 != NULL) {
  88. for (int i = 0; i < item_size1; i++) {
  89. printf("%c", item1[i]);
  90. }
  91. vRingbufferReturnItem(buf_handle, (void *)item1);
  92. //Check if item was split
  93. if (item2 != NULL) {
  94. for (int i = 0; i < item_size2; i++) {
  95. printf("%c", item2[i]);
  96. }
  97. vRingbufferReturnItem(buf_handle, (void *)item2);
  98. }
  99. printf("\n");
  100. } else {
  101. //Failed to receive item
  102. printf("Failed to receive item\n");
  103. }
  104. The following example demonstrates retrieving and returning an item from a **byte buffer**
  105. using :cpp:func:`xRingbufferReceiveUpTo` and :cpp:func:`vRingbufferReturnItem`
  106. .. code-block:: c
  107. ...
  108. //Receive data from byte buffer
  109. size_t item_size;
  110. char *item = (char *)xRingbufferReceiveUpTo(buf_handle, &item_size, pdMS_TO_TICKS(1000), sizeof(tx_item));
  111. //Check received data
  112. if (item != NULL) {
  113. //Print item
  114. for (int i = 0; i < item_size; i++) {
  115. printf("%c", item[i]);
  116. }
  117. printf("\n");
  118. //Return Item
  119. vRingbufferReturnItem(buf_handle, (void *)item);
  120. } else {
  121. //Failed to receive item
  122. printf("Failed to receive item\n");
  123. }
  124. For ISR safe versions of the functions used above, call :cpp:func:`xRingbufferSendFromISR`, :cpp:func:`xRingbufferReceiveFromISR`,
  125. :cpp:func:`xRingbufferReceiveSplitFromISR`, :cpp:func:`xRingbufferReceiveUpToFromISR`, and :cpp:func:`vRingbufferReturnItemFromISR`
  126. Sending to Ring Buffer
  127. ^^^^^^^^^^^^^^^^^^^^^^
  128. The following diagrams illustrate the differences between no-split/allow-split buffers
  129. and byte buffers with regards to sending items/data. The diagrams assume that three
  130. items of sizes **18, 3, and 27 bytes** are sent respectively to a **buffer of 128 bytes**.
  131. .. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag
  132. :caption: Sending items to no-split/allow-split ring buffers
  133. :align: center
  134. For no-split/allow-split buffers, a header of 8 bytes precedes every data item. Furthermore, the space
  135. occupied by each item is **rounded up to the nearest 32-bit aligned size** in order to maintain overall
  136. 32-bit alignment. However the true size of the item is recorded inside the header which will be
  137. returned when the item is retrieved.
  138. Referring to the diagram above, the 18, 3, and 27 byte items are **rounded up to 20, 4, and 28 bytes**
  139. respectively. An 8 byte header is then added in front of each item.
  140. .. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag
  141. :caption: Sending items to byte buffers
  142. :align: center
  143. Byte buffers treat data as a sequence of bytes and does not incur any overhead
  144. (no headers). As a result, all data sent to a byte buffer is merged into a single item.
  145. Referring to the diagram above, the 18, 3, and 27 byte items are sequentially written to the
  146. byte buffer and **merged into a single item of 48 bytes**.
  147. Wrap around
  148. ^^^^^^^^^^^
  149. The following diagrams illustrate the differences between no-split, allow-split, and byte
  150. buffers when a sent item requires a wrap around. The diagrams assumes a buffer of **128 bytes**
  151. with **56 bytes of free space that wraps around** and a sent item of **28 bytes**.
  152. .. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag
  153. :caption: Wrap around in no-split buffers
  154. :align: center
  155. No-split buffers will **only store an item in continuous free space and will not split
  156. an item under any circumstances**. When the free space at the tail of the buffer is insufficient
  157. to completely store the item and its header, the free space at the tail will be **marked as dummy data**.
  158. The buffer will then wrap around and store the item in the free space at the head of the buffer.
  159. Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is
  160. insufficient to store the 28 byte item. Therefore the 16 bytes is marked as dummy data and
  161. the item is written to the free space at the head of the buffer instead.
  162. .. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag
  163. :caption: Wrap around in allow-split buffers
  164. :align: center
  165. Allow-split buffers will attempt to **split the item into two parts** when the free space at the tail
  166. of the buffer is insufficient to store the item data and its header. Both parts of the
  167. split item will have their own headers (therefore incurring an extra 8 bytes of overhead).
  168. Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient
  169. to store the 28 byte item. Therefore the item is split into two parts (8 and 20 bytes) and written
  170. as two parts to the buffer.
  171. .. note::
  172. Allow-split buffers treats the both parts of the split item as two separate items, therefore call
  173. :cpp:func:`xRingbufferReceiveSplit` instead of :cpp:func:`xRingbufferReceive` to receive both
  174. parts of a split item in a thread safe manner.
  175. .. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag
  176. :caption: Wrap around in byte buffers
  177. :align: center
  178. Byte buffers will **store as much data as possible into the free space at the tail of buffer**. The remaining
  179. data will then be stored in the free space at the head of the buffer. No overhead is incurred when wrapping
  180. around in byte buffers.
  181. Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient to
  182. completely store the 28 bytes of data. Therefore the 16 bytes of free space is filled with data, and the
  183. remaining 12 bytes are written to the free space at the head of the buffer. The buffer now contains
  184. data in two separate continuous parts, and each part continuous will be treated as a separate item by the
  185. byte buffer.
  186. Retrieving/Returning
  187. ^^^^^^^^^^^^^^^^^^^^
  188. The following diagrams illustrates the differences between no-split/allow-split and
  189. byte buffers in retrieving and returning data.
  190. .. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag
  191. :caption: Retrieving/Returning items in no-split/allow-split ring buffers
  192. :align: center
  193. Items in no-split/allow-split buffers are **retrieved in strict FIFO order** and **must be returned**
  194. for the occupied space to be freed. Multiple items can be retrieved before returning, and the items
  195. do not necessarily need to be returned in the order they were retrieved. However the freeing of space
  196. must occur in FIFO order, therefore not returning the earliest retrieved item will prevent the space
  197. of subsequent items from being freed.
  198. Referring to the diagram above, the **16, 20, and 8 byte items are retrieved in FIFO order**. However the items
  199. are not returned in they were retrieved (20, 8, 16). As such, the space is not freed until the first item
  200. (16 byte) is returned.
  201. .. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag
  202. :caption: Retrieving/Returning data in byte buffers
  203. :align: center
  204. Byte buffers **do not allow multiple retrievals before returning** (every retrieval must be followed by a return
  205. before another retrieval is permitted). When using :cpp:func:`xRingbufferReceive` or
  206. :cpp:func:`xRingbufferReceiveFromISR`, all continuous stored data will be retrieved. :cpp:func:`xRingbufferReceiveUpTo`
  207. or :cpp:func:`xRingbufferReceiveUpToFromISR` can be used to restrict the maximum number of bytes retrieved. Since
  208. every retrieval must be followed by a return, the space will be freed as soon as the data is returned.
  209. Referring to the diagram above, the 38 bytes of continuous stored data at the tail of the buffer is retrieved,
  210. returned, and freed. The next call to :cpp:func:`xRingbufferReceive` or :cpp:func:`xRingbufferReceiveFromISR`
  211. then wraps around and does the same to the 30 bytes of continuous stored data at the head of the buffer.
  212. Ring Buffers with Queue Sets
  213. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  214. Ring buffers can be added to FreeRTOS queue sets using :cpp:func:`xRingbufferAddToQueueSetRead` such that every
  215. time a ring buffer receives an item or data, the queue set is notified. Once added to a queue set, every
  216. attempt to retrieve an item from a ring buffer should be preceded by a call to :cpp:func:`xQueueSelectFromSet`.
  217. To check whether the selected queue set member is the ring buffer, call :cpp:func:`xRingbufferCanRead`.
  218. The following example demonstrates queue set usage with ring buffers.
  219. .. code-block:: c
  220. #include "freertos/queue.h"
  221. #include "freertos/ringbuf.h"
  222. ...
  223. //Create ring buffer and queue set
  224. RingbufHandle_t buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT);
  225. QueueSetHandle_t queue_set = xQueueCreateSet(3);
  226. //Add ring buffer to queue set
  227. if (xRingbufferAddToQueueSetRead(buf_handle, queue_set) != pdTRUE) {
  228. printf("Failed to add to queue set\n");
  229. }
  230. ...
  231. //Block on queue set
  232. xQueueSetMemberHandle member = xQueueSelectFromSet(queue_set, pdMS_TO_TICKS(1000));
  233. //Check if member is ring buffer
  234. if (member != NULL && xRingbufferCanRead(buf_handle, member) == pdTRUE) {
  235. //Member is ring buffer, receive item from ring buffer
  236. size_t item_size;
  237. char *item = (char *)xRingbufferReceive(buf_handle, &item_size, 0);
  238. //Handle item
  239. ...
  240. } else {
  241. ...
  242. }
  243. Ring Buffer API Reference
  244. -------------------------
  245. .. note::
  246. Ideally, ring buffers can be used with multiple tasks in an SMP fashion where the **highest
  247. priority task will always be serviced first.** However due to the usage of binary semaphores
  248. in the ring buffer's underlying implementation, priority inversion may occur under very
  249. specific circumstances.
  250. The ring buffer governs sending by a binary semaphore which is given whenever space is
  251. freed on the ring buffer. The highest priority task waiting to send will repeatedly take
  252. the semaphore until sufficient free space becomes available or until it times out. Ideally
  253. this should prevent any lower priority tasks from being serviced as the semaphore should
  254. always be given to the highest priority task.
  255. However in between iterations of acquiring the semaphore, there is a **gap in the critical
  256. section** which may permit another task (on the other core or with an even higher priority) to
  257. free some space on the ring buffer and as a result give the semaphore. Therefore the semaphore
  258. will be given before the highest priority task can re-acquire the semaphore. This will result
  259. in the **semaphore being acquired by the second highest priority task** waiting to send, hence
  260. causing priority inversion.
  261. This side effect will not affect ring buffer performance drastically given if the number
  262. of tasks using the ring buffer simultaneously is low, and the ring buffer is not operating
  263. near maximum capacity.
  264. .. include:: /_build/inc/ringbuf.inc
  265. .. _hooks:
  266. Hooks
  267. -----
  268. FreeRTOS consists of Idle Hooks and Tick Hooks which allow for application
  269. specific functionality to be added to the Idle Task and Tick Interrupt.
  270. ESP-IDF provides its own Idle and Tick Hook API in addition to the hooks
  271. provided by Vanilla FreeRTOS. ESP-IDF hooks have the added benefit of
  272. being run time configurable and asymmetrical.
  273. Vanilla FreeRTOS Hooks
  274. ^^^^^^^^^^^^^^^^^^^^^^
  275. Idle and Tick Hooks in vanilla FreeRTOS are implemented by the user
  276. defining the functions ``vApplicationIdleHook()`` and ``vApplicationTickHook()``
  277. respectively somewhere in the application. Vanilla FreeRTOS will run the user
  278. defined Idle Hook and Tick Hook on every iteration of the Idle Task and Tick
  279. Interrupt respectively.
  280. Vanilla FreeRTOS hooks are referred to as **Legacy Hooks** in ESP-IDF FreeRTOS.
  281. To enable legacy hooks, :ref:`CONFIG_FREERTOS_LEGACY_HOOKS` should be enabled
  282. in ``make menuconfig``.
  283. Due to vanilla FreeRTOS being designed for single core, ``vApplicationIdleHook()``
  284. and ``vApplicationTickHook()`` can only be defined once. However, the ESP32 is dual core
  285. in nature, therefore same Idle Hook and Tick Hook are used for both cores (in other words,
  286. the hooks are symmetrical for both cores).
  287. ESP-IDF Idle and Tick Hooks
  288. ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  289. Due to the the dual core nature of the ESP32, it may be necessary for some
  290. applications to have separate hooks for each core. Furthermore, it may
  291. be necessary for the Idle Tasks or Tick Interrupts to execute multiple hooks
  292. that are configurable at run time. Therefore the ESP-IDF provides it's own hooks
  293. API in addition to the legacy hooks provided by Vanilla FreeRTOS.
  294. The ESP-IDF tick/idle hooks are registered at run time, and each tick/idle hook
  295. must be registered to a specific CPU. When the idle task runs/tick Interrupt
  296. occurs on a particular CPU, the CPU will run each of its registered idle/tick hooks
  297. in turn.
  298. Hooks API Reference
  299. -------------------
  300. .. include:: /_build/inc/esp_freertos_hooks.inc