30 |
31 | /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
32 | all the API functions to use the MPU wrappers. That should only be done when
33 | task.h is included from an application file. */
34 | #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
35 |
36 | /* FreeRTOS includes. */
37 | #include "FreeRTOS.h"
38 | #include "task.h"
39 | #include "timers.h"
40 | #include "event_groups.h"
41 |
42 | /* Lint e961, e750 and e9021 are suppressed as a MISRA exception justified
43 | because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined
44 | for the header files above, but not in this file, in order to generate the
45 | correct privileged Vs unprivileged linkage and placement. */
46 | #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021 See comment above. */
47 |
48 | /* The following bit fields convey control information in a task's event list
49 | item value. It is important they don't clash with the
50 | taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */
51 | #if configUSE_16_BIT_TICKS == 1
52 | #define eventCLEAR_EVENTS_ON_EXIT_BIT 0x0100U
53 | #define eventUNBLOCKED_DUE_TO_BIT_SET 0x0200U
54 | #define eventWAIT_FOR_ALL_BITS 0x0400U
55 | #define eventEVENT_BITS_CONTROL_BYTES 0xff00U
56 | #else
57 | #define eventCLEAR_EVENTS_ON_EXIT_BIT 0x01000000UL
58 | #define eventUNBLOCKED_DUE_TO_BIT_SET 0x02000000UL
59 | #define eventWAIT_FOR_ALL_BITS 0x04000000UL
60 | #define eventEVENT_BITS_CONTROL_BYTES 0xff000000UL
61 | #endif
62 |
63 | typedef struct EventGroupDef_t
64 | {
65 | EventBits_t uxEventBits;
66 | List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */
67 |
68 | #if( configUSE_TRACE_FACILITY == 1 )
69 | UBaseType_t uxEventGroupNumber;
70 | #endif
71 |
72 | #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
73 | uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
74 | #endif
75 | } EventGroup_t;
76 |
77 | /*-----------------------------------------------------------*/
78 |
79 | /*
80 | * Test the bits set in uxCurrentEventBits to see if the wait condition is met.
81 | * The wait condition is defined by xWaitForAllBits. If xWaitForAllBits is
82 | * pdTRUE then the wait condition is met if all the bits set in uxBitsToWaitFor
83 | * are also set in uxCurrentEventBits. If xWaitForAllBits is pdFALSE then the
84 | * wait condition is met if any of the bits set in uxBitsToWait for are also set
85 | * in uxCurrentEventBits.
86 | */
87 | static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ) PRIVILEGED_FUNCTION;
88 |
89 | /*-----------------------------------------------------------*/
90 |
91 | #if( configSUPPORT_STATIC_ALLOCATION == 1 )
92 |
93 | EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )
94 | {
95 | EventGroup_t *pxEventBits;
96 |
97 | /* A StaticEventGroup_t object must be provided. */
98 | configASSERT( pxEventGroupBuffer );
99 |
100 | #if( configASSERT_DEFINED == 1 )
101 | {
102 | /* Sanity check that the size of the structure used to declare a
103 | variable of type StaticEventGroup_t equals the size of the real
104 | event group structure. */
105 | volatile size_t xSize = sizeof( StaticEventGroup_t );
106 | configASSERT( xSize == sizeof( EventGroup_t ) );
107 | } /*lint !e529 xSize is referenced if configASSERT() is defined. */
108 | #endif /* configASSERT_DEFINED */
109 |
110 | /* The user has provided a statically allocated event group - use it. */
111 | pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 !e9087 EventGroup_t and StaticEventGroup_t are deliberately aliased for data hiding purposes and guaranteed to have the same size and alignment requirement - checked by configASSERT(). */
112 |
113 | if( pxEventBits != NULL )
114 | {
115 | pxEventBits->uxEventBits = 0;
116 | vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
117 |
118 | #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
119 | {
120 | /* Both static and dynamic allocation can be used, so note that
121 | this event group was created statically in case the event group
122 | is later deleted. */
123 | pxEventBits->ucStaticallyAllocated = pdTRUE;
124 | }
125 | #endif /* configSUPPORT_DYNAMIC_ALLOCATION */
126 |
127 | traceEVENT_GROUP_CREATE( pxEventBits );
128 | }
129 | else
130 | {
131 | /* xEventGroupCreateStatic should only ever be called with
132 | pxEventGroupBuffer pointing to a pre-allocated (compile time
133 | allocated) StaticEventGroup_t variable. */
134 | traceEVENT_GROUP_CREATE_FAILED();
135 | }
136 |
137 | return pxEventBits;
138 | }
139 |
140 | #endif /* configSUPPORT_STATIC_ALLOCATION */
141 | /*-----------------------------------------------------------*/
142 |
143 | #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
144 |
145 | EventGroupHandle_t xEventGroupCreate( void )
146 | {
147 | EventGroup_t *pxEventBits;
148 |
149 | /* Allocate the event group. Justification for MISRA deviation as
150 | follows: pvPortMalloc() always ensures returned memory blocks are
151 | aligned per the requirements of the MCU stack. In this case
152 | pvPortMalloc() must return a pointer that is guaranteed to meet the
153 | alignment requirements of the EventGroup_t structure - which (if you
154 | follow it through) is the alignment requirements of the TickType_t type
155 | (EventBits_t being of TickType_t itself). Therefore, whenever the
156 | stack alignment requirements are greater than or equal to the
157 | TickType_t alignment requirements the cast is safe. In other cases,
158 | where the natural word size of the architecture is less than
159 | sizeof( TickType_t ), the TickType_t variables will be accessed in two
160 | or more reads operations, and the alignment requirements is only that
161 | of each individual read. */
162 | pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); /*lint !e9087 !e9079 see comment above. */
163 |
164 | if( pxEventBits != NULL )
165 | {
166 | pxEventBits->uxEventBits = 0;
167 | vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
168 |
169 | #if( configSUPPORT_STATIC_ALLOCATION == 1 )
170 | {
171 | /* Both static and dynamic allocation can be used, so note this
172 | event group was allocated statically in case the event group is
173 | later deleted. */
174 | pxEventBits->ucStaticallyAllocated = pdFALSE;
175 | }
176 | #endif /* configSUPPORT_STATIC_ALLOCATION */
177 |
178 | traceEVENT_GROUP_CREATE( pxEventBits );
179 | }
180 | else
181 | {
182 | traceEVENT_GROUP_CREATE_FAILED(); /*lint !e9063 Else branch only exists to allow tracing and does not generate code if trace macros are not defined. */
183 | }
184 |
185 | return pxEventBits;
186 | }
187 |
188 | #endif /* configSUPPORT_DYNAMIC_ALLOCATION */
189 | /*-----------------------------------------------------------*/
190 |
191 | EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait )
192 | {
193 | EventBits_t uxOriginalBitValue, uxReturn;
194 | EventGroup_t *pxEventBits = xEventGroup;
195 | BaseType_t xAlreadyYielded;
196 | BaseType_t xTimeoutOccurred = pdFALSE;
197 |
198 | configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
199 | configASSERT( uxBitsToWaitFor != 0 );
200 | #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
201 | {
202 | configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
203 | }
204 | #endif
205 |
206 | vTaskSuspendAll();
207 | {
208 | uxOriginalBitValue = pxEventBits->uxEventBits;
209 |
210 | ( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet );
211 |
212 | if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor )
213 | {
214 | /* All the rendezvous bits are now set - no need to block. */
215 | uxReturn = ( uxOriginalBitValue | uxBitsToSet );
216 |
217 | /* Rendezvous always clear the bits. They will have been cleared
218 | already unless this is the only task in the rendezvous. */
219 | pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
220 |
221 | xTicksToWait = 0;
222 | }
223 | else
224 | {
225 | if( xTicksToWait != ( TickType_t ) 0 )
226 | {
227 | traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor );
228 |
229 | /* Store the bits that the calling task is waiting for in the
230 | task's event list item so the kernel knows when a match is
231 | found. Then enter the blocked state. */
232 | vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait );
233 |
234 | /* This assignment is obsolete as uxReturn will get set after
235 | the task unblocks, but some compilers mistakenly generate a
236 | warning about uxReturn being returned without being set if the
237 | assignment is omitted. */
238 | uxReturn = 0;
239 | }
240 | else
241 | {
242 | /* The rendezvous bits were not set, but no block time was
243 | specified - just return the current event bit value. */
244 | uxReturn = pxEventBits->uxEventBits;
245 | xTimeoutOccurred = pdTRUE;
246 | }
247 | }
248 | }
249 | xAlreadyYielded = xTaskResumeAll();
250 |
251 | if( xTicksToWait != ( TickType_t ) 0 )
252 | {
253 | if( xAlreadyYielded == pdFALSE )
254 | {
255 | portYIELD_WITHIN_API();
256 | }
257 | else
258 | {
259 | mtCOVERAGE_TEST_MARKER();
260 | }
261 |
262 | /* The task blocked to wait for its required bits to be set - at this
263 | point either the required bits were set or the block time expired. If
264 | the required bits were set they will have been stored in the task's
265 | event list item, and they should now be retrieved then cleared. */
266 | uxReturn = uxTaskResetEventItemValue();
267 |
268 | if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
269 | {
270 | /* The task timed out, just return the current event bit value. */
271 | taskENTER_CRITICAL();
272 | {
273 | uxReturn = pxEventBits->uxEventBits;
274 |
275 | /* Although the task got here because it timed out before the
276 | bits it was waiting for were set, it is possible that since it
277 | unblocked another task has set the bits. If this is the case
278 | then it needs to clear the bits before exiting. */
279 | if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor )
280 | {
281 | pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
282 | }
283 | else
284 | {
285 | mtCOVERAGE_TEST_MARKER();
286 | }
287 | }
288 | taskEXIT_CRITICAL();
289 |
290 | xTimeoutOccurred = pdTRUE;
291 | }
292 | else
293 | {
294 | /* The task unblocked because the bits were set. */
295 | }
296 |
297 | /* Control bits might be set as the task had blocked should not be
298 | returned. */
299 | uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
300 | }
301 |
302 | traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred );
303 |
304 | /* Prevent compiler warnings when trace macros are not used. */
305 | ( void ) xTimeoutOccurred;
306 |
307 | return uxReturn;
308 | }
309 | /*-----------------------------------------------------------*/
310 |
311 | EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
312 | {
313 | EventGroup_t *pxEventBits = xEventGroup;
314 | EventBits_t uxReturn, uxControlBits = 0;
315 | BaseType_t xWaitConditionMet, xAlreadyYielded;
316 | BaseType_t xTimeoutOccurred = pdFALSE;
317 |
318 | /* Check the user is not attempting to wait on the bits used by the kernel
319 | itself, and that at least one bit is being requested. */
320 | configASSERT( xEventGroup );
321 | configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
322 | configASSERT( uxBitsToWaitFor != 0 );
323 | #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
324 | {
325 | configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
326 | }
327 | #endif
328 |
329 | vTaskSuspendAll();
330 | {
331 | const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;
332 |
333 | /* Check to see if the wait condition is already met or not. */
334 | xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );
335 |
336 | if( xWaitConditionMet != pdFALSE )
337 | {
338 | /* The wait condition has already been met so there is no need to
339 | block. */
340 | uxReturn = uxCurrentEventBits;
341 | xTicksToWait = ( TickType_t ) 0;
342 |
343 | /* Clear the wait bits if requested to do so. */
344 | if( xClearOnExit != pdFALSE )
345 | {
346 | pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
347 | }
348 | else
349 | {
350 | mtCOVERAGE_TEST_MARKER();
351 | }
352 | }
353 | else if( xTicksToWait == ( TickType_t ) 0 )
354 | {
355 | /* The wait condition has not been met, but no block time was
356 | specified, so just return the current value. */
357 | uxReturn = uxCurrentEventBits;
358 | xTimeoutOccurred = pdTRUE;
359 | }
360 | else
361 | {
362 | /* The task is going to block to wait for its required bits to be
363 | set. uxControlBits are used to remember the specified behaviour of
364 | this call to xEventGroupWaitBits() - for use when the event bits
365 | unblock the task. */
366 | if( xClearOnExit != pdFALSE )
367 | {
368 | uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
369 | }
370 | else
371 | {
372 | mtCOVERAGE_TEST_MARKER();
373 | }
374 |
375 | if( xWaitForAllBits != pdFALSE )
376 | {
377 | uxControlBits |= eventWAIT_FOR_ALL_BITS;
378 | }
379 | else
380 | {
381 | mtCOVERAGE_TEST_MARKER();
382 | }
383 |
384 | /* Store the bits that the calling task is waiting for in the
385 | task's event list item so the kernel knows when a match is
386 | found. Then enter the blocked state. */
387 | vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
388 |
389 | /* This is obsolete as it will get set after the task unblocks, but
390 | some compilers mistakenly generate a warning about the variable
391 | being returned without being set if it is not done. */
392 | uxReturn = 0;
393 |
394 | traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
395 | }
396 | }
397 | xAlreadyYielded = xTaskResumeAll();
398 |
399 | if( xTicksToWait != ( TickType_t ) 0 )
400 | {
401 | if( xAlreadyYielded == pdFALSE )
402 | {
403 | portYIELD_WITHIN_API();
404 | }
405 | else
406 | {
407 | mtCOVERAGE_TEST_MARKER();
408 | }
409 |
410 | /* The task blocked to wait for its required bits to be set - at this
411 | point either the required bits were set or the block time expired. If
412 | the required bits were set they will have been stored in the task's
413 | event list item, and they should now be retrieved then cleared. */
414 | uxReturn = uxTaskResetEventItemValue();
415 |
416 | if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
417 | {
418 | taskENTER_CRITICAL();
419 | {
420 | /* The task timed out, just return the current event bit value. */
421 | uxReturn = pxEventBits->uxEventBits;
422 |
423 | /* It is possible that the event bits were updated between this
424 | task leaving the Blocked state and running again. */
425 | if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
426 | {
427 | if( xClearOnExit != pdFALSE )
428 | {
429 | pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
430 | }
431 | else
432 | {
433 | mtCOVERAGE_TEST_MARKER();
434 | }
435 | }
436 | else
437 | {
438 | mtCOVERAGE_TEST_MARKER();
439 | }
440 | xTimeoutOccurred = pdTRUE;
441 | }
442 | taskEXIT_CRITICAL();
443 | }
444 | else
445 | {
446 | /* The task unblocked because the bits were set. */
447 | }
448 |
449 | /* The task blocked so control bits may have been set. */
450 | uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
451 | }
452 | traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );
453 |
454 | /* Prevent compiler warnings when trace macros are not used. */
455 | ( void ) xTimeoutOccurred;
456 |
457 | return uxReturn;
458 | }
459 | /*-----------------------------------------------------------*/
460 |
461 | EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
462 | {
463 | EventGroup_t *pxEventBits = xEventGroup;
464 | EventBits_t uxReturn;
465 |
466 | /* Check the user is not attempting to clear the bits used by the kernel
467 | itself. */
468 | configASSERT( xEventGroup );
469 | configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
470 |
471 | taskENTER_CRITICAL();
472 | {
473 | traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear );
474 |
475 | /* The value returned is the event group value prior to the bits being
476 | cleared. */
477 | uxReturn = pxEventBits->uxEventBits;
478 |
479 | /* Clear the bits. */
480 | pxEventBits->uxEventBits &= ~uxBitsToClear;
481 | }
482 | taskEXIT_CRITICAL();
483 |
484 | return uxReturn;
485 | }
486 | /*-----------------------------------------------------------*/
487 |
488 | #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
489 |
490 | BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
491 | {
492 | BaseType_t xReturn;
493 |
494 | traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear );
495 | xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */
496 |
497 | return xReturn;
498 | }
499 |
500 | #endif
501 | /*-----------------------------------------------------------*/
502 |
503 | EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
504 | {
505 | UBaseType_t uxSavedInterruptStatus;
506 | EventGroup_t const * const pxEventBits = xEventGroup;
507 | EventBits_t uxReturn;
508 |
509 | uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
510 | {
511 | uxReturn = pxEventBits->uxEventBits;
512 | }
513 | portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
514 |
515 | return uxReturn;
516 | } /*lint !e818 EventGroupHandle_t is a typedef used in other functions to so can't be pointer to const. */
517 | /*-----------------------------------------------------------*/
518 |
519 | EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
520 | {
521 | ListItem_t *pxListItem, *pxNext;
522 | ListItem_t const *pxListEnd;
523 | List_t const * pxList;
524 | EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;
525 | EventGroup_t *pxEventBits = xEventGroup;
526 | BaseType_t xMatchFound = pdFALSE;
527 |
528 | /* Check the user is not attempting to set the bits used by the kernel
529 | itself. */
530 | configASSERT( xEventGroup );
531 | configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
532 |
533 | pxList = &( pxEventBits->xTasksWaitingForBits );
534 | pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
535 | vTaskSuspendAll();
536 | {
537 | traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );
538 |
539 | pxListItem = listGET_HEAD_ENTRY( pxList );
540 |
541 | /* Set the bits. */
542 | pxEventBits->uxEventBits |= uxBitsToSet;
543 |
544 | /* See if the new bit value should unblock any tasks. */
545 | while( pxListItem != pxListEnd )
546 | {
547 | pxNext = listGET_NEXT( pxListItem );
548 | uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
549 | xMatchFound = pdFALSE;
550 |
551 | /* Split the bits waited for from the control bits. */
552 | uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
553 | uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;
554 |
555 | if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
556 | {
557 | /* Just looking for single bit being set. */
558 | if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
559 | {
560 | xMatchFound = pdTRUE;
561 | }
562 | else
563 | {
564 | mtCOVERAGE_TEST_MARKER();
565 | }
566 | }
567 | else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
568 | {
569 | /* All bits are set. */
570 | xMatchFound = pdTRUE;
571 | }
572 | else
573 | {
574 | /* Need all bits to be set, but not all the bits were set. */
575 | }
576 |
577 | if( xMatchFound != pdFALSE )
578 | {
579 | /* The bits match. Should the bits be cleared on exit? */
580 | if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )
581 | {
582 | uxBitsToClear |= uxBitsWaitedFor;
583 | }
584 | else
585 | {
586 | mtCOVERAGE_TEST_MARKER();
587 | }
588 |
589 | /* Store the actual event flag value in the task's event list
590 | item before removing the task from the event list. The
591 | eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows
592 | that is was unblocked due to its required bits matching, rather
593 | than because it timed out. */
594 | vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
595 | }
596 |
597 | /* Move onto the next list item. Note pxListItem->pxNext is not
598 | used here as the list item may have been removed from the event list
599 | and inserted into the ready/pending reading list. */
600 | pxListItem = pxNext;
601 | }
602 |
603 | /* Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT
604 | bit was set in the control word. */
605 | pxEventBits->uxEventBits &= ~uxBitsToClear;
606 | }
607 | ( void ) xTaskResumeAll();
608 |
609 | return pxEventBits->uxEventBits;
610 | }
611 | /*-----------------------------------------------------------*/
612 |
613 | void vEventGroupDelete( EventGroupHandle_t xEventGroup )
614 | {
615 | EventGroup_t *pxEventBits = xEventGroup;
616 | const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits );
617 |
618 | vTaskSuspendAll();
619 | {
620 | traceEVENT_GROUP_DELETE( xEventGroup );
621 |
622 | while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 )
623 | {
624 | /* Unblock the task, returning 0 as the event list is being deleted
625 | and cannot therefore have any bits set. */
626 | configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) );
627 | vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET );
628 | }
629 |
630 | #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
631 | {
632 | /* The event group can only have been allocated dynamically - free
633 | it again. */
634 | vPortFree( pxEventBits );
635 | }
636 | #elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
637 | {
638 | /* The event group could have been allocated statically or
639 | dynamically, so check before attempting to free the memory. */
640 | if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
641 | {
642 | vPortFree( pxEventBits );
643 | }
644 | else
645 | {
646 | mtCOVERAGE_TEST_MARKER();
647 | }
648 | }
649 | #endif /* configSUPPORT_DYNAMIC_ALLOCATION */
650 | }
651 | ( void ) xTaskResumeAll();
652 | }
653 | /*-----------------------------------------------------------*/
654 |
655 | /* For internal use only - execute a 'set bits' command that was pended from
656 | an interrupt. */
657 | void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet )
658 | {
659 | ( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */
660 | }
661 | /*-----------------------------------------------------------*/
662 |
663 | /* For internal use only - execute a 'clear bits' command that was pended from
664 | an interrupt. */
665 | void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear )
666 | {
667 | ( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */
668 | }
669 | /*-----------------------------------------------------------*/
670 |
671 | static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits )
672 | {
673 | BaseType_t xWaitConditionMet = pdFALSE;
674 |
675 | if( xWaitForAllBits == pdFALSE )
676 | {
677 | /* Task only has to wait for one bit within uxBitsToWaitFor to be
678 | set. Is one already set? */
679 | if( ( uxCurrentEventBits & uxBitsToWaitFor ) != ( EventBits_t ) 0 )
680 | {
681 | xWaitConditionMet = pdTRUE;
682 | }
683 | else
684 | {
685 | mtCOVERAGE_TEST_MARKER();
686 | }
687 | }
688 | else
689 | {
690 | /* Task has to wait for all the bits in uxBitsToWaitFor to be set.
691 | Are they set already? */
692 | if( ( uxCurrentEventBits & uxBitsToWaitFor ) == uxBitsToWaitFor )
693 | {
694 | xWaitConditionMet = pdTRUE;
695 | }
696 | else
697 | {
698 | mtCOVERAGE_TEST_MARKER();
699 | }
700 | }
701 |
702 | return xWaitConditionMet;
703 | }
704 | /*-----------------------------------------------------------*/
705 |
706 | #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
707 |
708 | BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken )
709 | {
710 | BaseType_t xReturn;
711 |
712 | traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet );
713 | xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */
714 |
715 | return xReturn;
716 | }
717 |
718 | #endif
719 | /*-----------------------------------------------------------*/
720 |
721 | #if (configUSE_TRACE_FACILITY == 1)
722 |
723 | UBaseType_t uxEventGroupGetNumber( void* xEventGroup )
724 | {
725 | UBaseType_t xReturn;
726 | EventGroup_t const *pxEventBits = ( EventGroup_t * ) xEventGroup; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */
727 |
728 | if( xEventGroup == NULL )
729 | {
730 | xReturn = 0;
731 | }
732 | else
733 | {
734 | xReturn = pxEventBits->uxEventGroupNumber;
735 | }
736 |
737 | return xReturn;
738 | }
739 |
740 | #endif /* configUSE_TRACE_FACILITY */
741 | /*-----------------------------------------------------------*/
742 |
743 | #if ( configUSE_TRACE_FACILITY == 1 )
744 |
745 | void vEventGroupSetNumber( void * xEventGroup, UBaseType_t uxEventGroupNumber )
746 | {
747 | ( ( EventGroup_t * ) xEventGroup )->uxEventGroupNumber = uxEventGroupNumber; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */
748 | }
749 |
750 | #endif /* configUSE_TRACE_FACILITY */
751 | /*-----------------------------------------------------------*/
752 |
753 |
754 |
--------------------------------------------------------------------------------
/src/croutine.h:
--------------------------------------------------------------------------------
1 | /*
2 | * FreeRTOS Kernel V10.0.1
3 | * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | * this software and associated documentation files (the "Software"), to deal in
7 | * the Software without restriction, including without limitation the rights to
8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | * the Software, and to permit persons to whom the Software is furnished to do so,
10 | * subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in all
13 | * copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 | *
22 | * http://www.FreeRTOS.org
23 | * http://aws.amazon.com/freertos
24 | *
25 | * 1 tab == 4 spaces!
26 | */
27 |
28 | #ifndef CO_ROUTINE_H
29 | #define CO_ROUTINE_H
30 |
31 | #ifndef INC_FREERTOS_H
32 | #error "include FreeRTOS.h must appear in source files before include croutine.h"
33 | #endif
34 |
35 | #include "list.h"
36 |
37 | #ifdef __cplusplus
38 | extern "C" {
39 | #endif
40 |
41 | /* Used to hide the implementation of the co-routine control block. The
42 | control block structure however has to be included in the header due to
43 | the macro implementation of the co-routine functionality. */
44 | typedef void * CoRoutineHandle_t;
45 |
46 | /* Defines the prototype to which co-routine functions must conform. */
47 | typedef void (*crCOROUTINE_CODE)( CoRoutineHandle_t, UBaseType_t );
48 |
49 | typedef struct corCoRoutineControlBlock
50 | {
51 | crCOROUTINE_CODE pxCoRoutineFunction;
52 | ListItem_t xGenericListItem; /*< List item used to place the CRCB in ready and blocked queues. */
53 | ListItem_t xEventListItem; /*< List item used to place the CRCB in event lists. */
54 | UBaseType_t uxPriority; /*< The priority of the co-routine in relation to other co-routines. */
55 | UBaseType_t uxIndex; /*< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */
56 | uint16_t uxState; /*< Used internally by the co-routine implementation. */
57 | } CRCB_t; /* Co-routine control block. Note must be identical in size down to uxPriority with TCB_t. */
58 |
59 | /**
60 | * croutine. h
61 | *
62 | BaseType_t xCoRoutineCreate(
63 | crCOROUTINE_CODE pxCoRoutineCode,
64 | UBaseType_t uxPriority,
65 | UBaseType_t uxIndex
66 | );
67 | *
68 | * Create a new co-routine and add it to the list of co-routines that are
69 | * ready to run.
70 | *
71 | * @param pxCoRoutineCode Pointer to the co-routine function. Co-routine
72 | * functions require special syntax - see the co-routine section of the WEB
73 | * documentation for more information.
74 | *
75 | * @param uxPriority The priority with respect to other co-routines at which
76 | * the co-routine will run.
77 | *
78 | * @param uxIndex Used to distinguish between different co-routines that
79 | * execute the same function. See the example below and the co-routine section
80 | * of the WEB documentation for further information.
81 | *
82 | * @return pdPASS if the co-routine was successfully created and added to a ready
83 | * list, otherwise an error code defined with ProjDefs.h.
84 | *
85 | * Example usage:
86 |
87 | // Co-routine to be created.
88 | void vFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
89 | {
90 | // Variables in co-routines must be declared static if they must maintain value across a blocking call.
91 | // This may not be necessary for const variables.
92 | static const char cLedToFlash[ 2 ] = { 5, 6 };
93 | static const TickType_t uxFlashRates[ 2 ] = { 200, 400 };
94 |
95 | // Must start every co-routine with a call to crSTART();
96 | crSTART( xHandle );
97 |
98 | for( ;; )
99 | {
100 | // This co-routine just delays for a fixed period, then toggles
101 | // an LED. Two co-routines are created using this function, so
102 | // the uxIndex parameter is used to tell the co-routine which
103 | // LED to flash and how int32_t to delay. This assumes xQueue has
104 | // already been created.
105 | vParTestToggleLED( cLedToFlash[ uxIndex ] );
106 | crDELAY( xHandle, uxFlashRates[ uxIndex ] );
107 | }
108 |
109 | // Must end every co-routine with a call to crEND();
110 | crEND();
111 | }
112 |
113 | // Function that creates two co-routines.
114 | void vOtherFunction( void )
115 | {
116 | uint8_t ucParameterToPass;
117 | TaskHandle_t xHandle;
118 |
119 | // Create two co-routines at priority 0. The first is given index 0
120 | // so (from the code above) toggles LED 5 every 200 ticks. The second
121 | // is given index 1 so toggles LED 6 every 400 ticks.
122 | for( uxIndex = 0; uxIndex < 2; uxIndex++ )
123 | {
124 | xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
125 | }
126 | }
127 |
128 | * \defgroup xCoRoutineCreate xCoRoutineCreate
129 | * \ingroup Tasks
130 | */
131 | BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPriority, UBaseType_t uxIndex );
132 |
133 |
134 | /**
135 | * croutine. h
136 | *
137 | void vCoRoutineSchedule( void );
138 | *
139 | * Run a co-routine.
140 | *
141 | * vCoRoutineSchedule() executes the highest priority co-routine that is able
142 | * to run. The co-routine will execute until it either blocks, yields or is
143 | * preempted by a task. Co-routines execute cooperatively so one
144 | * co-routine cannot be preempted by another, but can be preempted by a task.
145 | *
146 | * If an application comprises of both tasks and co-routines then
147 | * vCoRoutineSchedule should be called from the idle task (in an idle task
148 | * hook).
149 | *
150 | * Example usage:
151 |
152 | // This idle task hook will schedule a co-routine each time it is called.
153 | // The rest of the idle task will execute between co-routine calls.
154 | void vApplicationIdleHook( void )
155 | {
156 | vCoRoutineSchedule();
157 | }
158 |
159 | // Alternatively, if you do not require any other part of the idle task to
160 | // execute, the idle task hook can call vCoRoutineScheduler() within an
161 | // infinite loop.
162 | void vApplicationIdleHook( void )
163 | {
164 | for( ;; )
165 | {
166 | vCoRoutineSchedule();
167 | }
168 | }
169 |
170 | * \defgroup vCoRoutineSchedule vCoRoutineSchedule
171 | * \ingroup Tasks
172 | */
173 | void vCoRoutineSchedule( void );
174 |
175 | /**
176 | * croutine. h
177 | *
178 | crSTART( CoRoutineHandle_t xHandle );
179 | *
180 | * This macro MUST always be called at the start of a co-routine function.
181 | *
182 | * Example usage:
183 |
184 | // Co-routine to be created.
185 | void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
186 | {
187 | // Variables in co-routines must be declared static if they must maintain value across a blocking call.
188 | static int32_t ulAVariable;
189 |
190 | // Must start every co-routine with a call to crSTART();
191 | crSTART( xHandle );
192 |
193 | for( ;; )
194 | {
195 | // Co-routine functionality goes here.
196 | }
197 |
198 | // Must end every co-routine with a call to crEND();
199 | crEND();
200 | }
201 | * \defgroup crSTART crSTART
202 | * \ingroup Tasks
203 | */
204 | #define crSTART( pxCRCB ) switch( ( ( CRCB_t * )( pxCRCB ) )->uxState ) { case 0:
205 |
206 | /**
207 | * croutine. h
208 | *
209 | crEND();
210 | *
211 | * This macro MUST always be called at the end of a co-routine function.
212 | *
213 | * Example usage:
214 |
215 | // Co-routine to be created.
216 | void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
217 | {
218 | // Variables in co-routines must be declared static if they must maintain value across a blocking call.
219 | static int32_t ulAVariable;
220 |
221 | // Must start every co-routine with a call to crSTART();
222 | crSTART( xHandle );
223 |
224 | for( ;; )
225 | {
226 | // Co-routine functionality goes here.
227 | }
228 |
229 | // Must end every co-routine with a call to crEND();
230 | crEND();
231 | }
232 | * \defgroup crSTART crSTART
233 | * \ingroup Tasks
234 | */
235 | #define crEND() }
236 |
237 | /*
238 | * These macros are intended for internal use by the co-routine implementation
239 | * only. The macros should not be used directly by application writers.
240 | */
241 | #define crSET_STATE0( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2):
242 | #define crSET_STATE1( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1):
243 |
244 | /**
245 | * croutine. h
246 | *
247 | crDELAY( CoRoutineHandle_t xHandle, TickType_t xTicksToDelay );
248 | *
249 | * Delay a co-routine for a fixed period of time.
250 | *
251 | * crDELAY can only be called from the co-routine function itself - not
252 | * from within a function called by the co-routine function. This is because
253 | * co-routines do not maintain their own stack.
254 | *
255 | * @param xHandle The handle of the co-routine to delay. This is the xHandle
256 | * parameter of the co-routine function.
257 | *
258 | * @param xTickToDelay The number of ticks that the co-routine should delay
259 | * for. The actual amount of time this equates to is defined by
260 | * configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant portTICK_PERIOD_MS
261 | * can be used to convert ticks to milliseconds.
262 | *
263 | * Example usage:
264 |
265 | // Co-routine to be created.
266 | void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
267 | {
268 | // Variables in co-routines must be declared static if they must maintain value across a blocking call.
269 | // This may not be necessary for const variables.
270 | // We are to delay for 200ms.
271 | static const xTickType xDelayTime = 200 / portTICK_PERIOD_MS;
272 |
273 | // Must start every co-routine with a call to crSTART();
274 | crSTART( xHandle );
275 |
276 | for( ;; )
277 | {
278 | // Delay for 200ms.
279 | crDELAY( xHandle, xDelayTime );
280 |
281 | // Do something here.
282 | }
283 |
284 | // Must end every co-routine with a call to crEND();
285 | crEND();
286 | }
287 | * \defgroup crDELAY crDELAY
288 | * \ingroup Tasks
289 | */
290 | #define crDELAY( xHandle, xTicksToDelay ) \
291 | if( ( xTicksToDelay ) > 0 ) \
292 | { \
293 | vCoRoutineAddToDelayedList( ( xTicksToDelay ), NULL ); \
294 | } \
295 | crSET_STATE0( ( xHandle ) );
296 |
297 | /**
298 | *
299 | crQUEUE_SEND(
300 | CoRoutineHandle_t xHandle,
301 | QueueHandle_t pxQueue,
302 | void *pvItemToQueue,
303 | TickType_t xTicksToWait,
304 | BaseType_t *pxResult
305 | )
306 | *
307 | * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine
308 | * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks.
309 | *
310 | * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas
311 | * xQueueSend() and xQueueReceive() can only be used from tasks.
312 | *
313 | * crQUEUE_SEND can only be called from the co-routine function itself - not
314 | * from within a function called by the co-routine function. This is because
315 | * co-routines do not maintain their own stack.
316 | *
317 | * See the co-routine section of the WEB documentation for information on
318 | * passing data between tasks and co-routines and between ISR's and
319 | * co-routines.
320 | *
321 | * @param xHandle The handle of the calling co-routine. This is the xHandle
322 | * parameter of the co-routine function.
323 | *
324 | * @param pxQueue The handle of the queue on which the data will be posted.
325 | * The handle is obtained as the return value when the queue is created using
326 | * the xQueueCreate() API function.
327 | *
328 | * @param pvItemToQueue A pointer to the data being posted onto the queue.
329 | * The number of bytes of each queued item is specified when the queue is
330 | * created. This number of bytes is copied from pvItemToQueue into the queue
331 | * itself.
332 | *
333 | * @param xTickToDelay The number of ticks that the co-routine should block
334 | * to wait for space to become available on the queue, should space not be
335 | * available immediately. The actual amount of time this equates to is defined
336 | * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant
337 | * portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see example
338 | * below).
339 | *
340 | * @param pxResult The variable pointed to by pxResult will be set to pdPASS if
341 | * data was successfully posted onto the queue, otherwise it will be set to an
342 | * error defined within ProjDefs.h.
343 | *
344 | * Example usage:
345 |
346 | // Co-routine function that blocks for a fixed period then posts a number onto
347 | // a queue.
348 | static void prvCoRoutineFlashTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
349 | {
350 | // Variables in co-routines must be declared static if they must maintain value across a blocking call.
351 | static BaseType_t xNumberToPost = 0;
352 | static BaseType_t xResult;
353 |
354 | // Co-routines must begin with a call to crSTART().
355 | crSTART( xHandle );
356 |
357 | for( ;; )
358 | {
359 | // This assumes the queue has already been created.
360 | crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
361 |
362 | if( xResult != pdPASS )
363 | {
364 | // The message was not posted!
365 | }
366 |
367 | // Increment the number to be posted onto the queue.
368 | xNumberToPost++;
369 |
370 | // Delay for 100 ticks.
371 | crDELAY( xHandle, 100 );
372 | }
373 |
374 | // Co-routines must end with a call to crEND().
375 | crEND();
376 | }
377 | * \defgroup crQUEUE_SEND crQUEUE_SEND
378 | * \ingroup Tasks
379 | */
380 | #define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \
381 | { \
382 | *( pxResult ) = xQueueCRSend( ( pxQueue) , ( pvItemToQueue) , ( xTicksToWait ) ); \
383 | if( *( pxResult ) == errQUEUE_BLOCKED ) \
384 | { \
385 | crSET_STATE0( ( xHandle ) ); \
386 | *pxResult = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), 0 ); \
387 | } \
388 | if( *pxResult == errQUEUE_YIELD ) \
389 | { \
390 | crSET_STATE1( ( xHandle ) ); \
391 | *pxResult = pdPASS; \
392 | } \
393 | }
394 |
395 | /**
396 | * croutine. h
397 | *
398 | crQUEUE_RECEIVE(
399 | CoRoutineHandle_t xHandle,
400 | QueueHandle_t pxQueue,
401 | void *pvBuffer,
402 | TickType_t xTicksToWait,
403 | BaseType_t *pxResult
404 | )
405 | *
406 | * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine
407 | * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks.
408 | *
409 | * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas
410 | * xQueueSend() and xQueueReceive() can only be used from tasks.
411 | *
412 | * crQUEUE_RECEIVE can only be called from the co-routine function itself - not
413 | * from within a function called by the co-routine function. This is because
414 | * co-routines do not maintain their own stack.
415 | *
416 | * See the co-routine section of the WEB documentation for information on
417 | * passing data between tasks and co-routines and between ISR's and
418 | * co-routines.
419 | *
420 | * @param xHandle The handle of the calling co-routine. This is the xHandle
421 | * parameter of the co-routine function.
422 | *
423 | * @param pxQueue The handle of the queue from which the data will be received.
424 | * The handle is obtained as the return value when the queue is created using
425 | * the xQueueCreate() API function.
426 | *
427 | * @param pvBuffer The buffer into which the received item is to be copied.
428 | * The number of bytes of each queued item is specified when the queue is
429 | * created. This number of bytes is copied into pvBuffer.
430 | *
431 | * @param xTickToDelay The number of ticks that the co-routine should block
432 | * to wait for data to become available from the queue, should data not be
433 | * available immediately. The actual amount of time this equates to is defined
434 | * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant
435 | * portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see the
436 | * crQUEUE_SEND example).
437 | *
438 | * @param pxResult The variable pointed to by pxResult will be set to pdPASS if
439 | * data was successfully retrieved from the queue, otherwise it will be set to
440 | * an error code as defined within ProjDefs.h.
441 | *
442 | * Example usage:
443 |
444 | // A co-routine receives the number of an LED to flash from a queue. It
445 | // blocks on the queue until the number is received.
446 | static void prvCoRoutineFlashWorkTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
447 | {
448 | // Variables in co-routines must be declared static if they must maintain value across a blocking call.
449 | static BaseType_t xResult;
450 | static UBaseType_t uxLEDToFlash;
451 |
452 | // All co-routines must start with a call to crSTART().
453 | crSTART( xHandle );
454 |
455 | for( ;; )
456 | {
457 | // Wait for data to become available on the queue.
458 | crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
459 |
460 | if( xResult == pdPASS )
461 | {
462 | // We received the LED to flash - flash it!
463 | vParTestToggleLED( uxLEDToFlash );
464 | }
465 | }
466 |
467 | crEND();
468 | }
469 | * \defgroup crQUEUE_RECEIVE crQUEUE_RECEIVE
470 | * \ingroup Tasks
471 | */
472 | #define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \
473 | { \
474 | *( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), ( xTicksToWait ) ); \
475 | if( *( pxResult ) == errQUEUE_BLOCKED ) \
476 | { \
477 | crSET_STATE0( ( xHandle ) ); \
478 | *( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), 0 ); \
479 | } \
480 | if( *( pxResult ) == errQUEUE_YIELD ) \
481 | { \
482 | crSET_STATE1( ( xHandle ) ); \
483 | *( pxResult ) = pdPASS; \
484 | } \
485 | }
486 |
487 | /**
488 | * croutine. h
489 | *
490 | crQUEUE_SEND_FROM_ISR(
491 | QueueHandle_t pxQueue,
492 | void *pvItemToQueue,
493 | BaseType_t xCoRoutinePreviouslyWoken
494 | )
495 | *
496 | * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the
497 | * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR()
498 | * functions used by tasks.
499 | *
500 | * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to
501 | * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and
502 | * xQueueReceiveFromISR() can only be used to pass data between a task and and
503 | * ISR.
504 | *
505 | * crQUEUE_SEND_FROM_ISR can only be called from an ISR to send data to a queue
506 | * that is being used from within a co-routine.
507 | *
508 | * See the co-routine section of the WEB documentation for information on
509 | * passing data between tasks and co-routines and between ISR's and
510 | * co-routines.
511 | *
512 | * @param xQueue The handle to the queue on which the item is to be posted.
513 | *
514 | * @param pvItemToQueue A pointer to the item that is to be placed on the
515 | * queue. The size of the items the queue will hold was defined when the
516 | * queue was created, so this many bytes will be copied from pvItemToQueue
517 | * into the queue storage area.
518 | *
519 | * @param xCoRoutinePreviouslyWoken This is included so an ISR can post onto
520 | * the same queue multiple times from a single interrupt. The first call
521 | * should always pass in pdFALSE. Subsequent calls should pass in
522 | * the value returned from the previous call.
523 | *
524 | * @return pdTRUE if a co-routine was woken by posting onto the queue. This is
525 | * used by the ISR to determine if a context switch may be required following
526 | * the ISR.
527 | *
528 | * Example usage:
529 |
530 | // A co-routine that blocks on a queue waiting for characters to be received.
531 | static void vReceivingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
532 | {
533 | char cRxedChar;
534 | BaseType_t xResult;
535 |
536 | // All co-routines must start with a call to crSTART().
537 | crSTART( xHandle );
538 |
539 | for( ;; )
540 | {
541 | // Wait for data to become available on the queue. This assumes the
542 | // queue xCommsRxQueue has already been created!
543 | crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
544 |
545 | // Was a character received?
546 | if( xResult == pdPASS )
547 | {
548 | // Process the character here.
549 | }
550 | }
551 |
552 | // All co-routines must end with a call to crEND().
553 | crEND();
554 | }
555 |
556 | // An ISR that uses a queue to send characters received on a serial port to
557 | // a co-routine.
558 | void vUART_ISR( void )
559 | {
560 | char cRxedChar;
561 | BaseType_t xCRWokenByPost = pdFALSE;
562 |
563 | // We loop around reading characters until there are none left in the UART.
564 | while( UART_RX_REG_NOT_EMPTY() )
565 | {
566 | // Obtain the character from the UART.
567 | cRxedChar = UART_RX_REG;
568 |
569 | // Post the character onto a queue. xCRWokenByPost will be pdFALSE
570 | // the first time around the loop. If the post causes a co-routine
571 | // to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
572 | // In this manner we can ensure that if more than one co-routine is
573 | // blocked on the queue only one is woken by this ISR no matter how
574 | // many characters are posted to the queue.
575 | xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
576 | }
577 | }
578 | * \defgroup crQUEUE_SEND_FROM_ISR crQUEUE_SEND_FROM_ISR
579 | * \ingroup Tasks
580 | */
581 | #define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) xQueueCRSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( xCoRoutinePreviouslyWoken ) )
582 |
583 |
584 | /**
585 | * croutine. h
586 | *
587 | crQUEUE_SEND_FROM_ISR(
588 | QueueHandle_t pxQueue,
589 | void *pvBuffer,
590 | BaseType_t * pxCoRoutineWoken
591 | )
592 | *
593 | * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the
594 | * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR()
595 | * functions used by tasks.
596 | *
597 | * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to
598 | * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and
599 | * xQueueReceiveFromISR() can only be used to pass data between a task and and
600 | * ISR.
601 | *
602 | * crQUEUE_RECEIVE_FROM_ISR can only be called from an ISR to receive data
603 | * from a queue that is being used from within a co-routine (a co-routine
604 | * posted to the queue).
605 | *
606 | * See the co-routine section of the WEB documentation for information on
607 | * passing data between tasks and co-routines and between ISR's and
608 | * co-routines.
609 | *
610 | * @param xQueue The handle to the queue on which the item is to be posted.
611 | *
612 | * @param pvBuffer A pointer to a buffer into which the received item will be
613 | * placed. The size of the items the queue will hold was defined when the
614 | * queue was created, so this many bytes will be copied from the queue into
615 | * pvBuffer.
616 | *
617 | * @param pxCoRoutineWoken A co-routine may be blocked waiting for space to become
618 | * available on the queue. If crQUEUE_RECEIVE_FROM_ISR causes such a
619 | * co-routine to unblock *pxCoRoutineWoken will get set to pdTRUE, otherwise
620 | * *pxCoRoutineWoken will remain unchanged.
621 | *
622 | * @return pdTRUE an item was successfully received from the queue, otherwise
623 | * pdFALSE.
624 | *
625 | * Example usage:
626 |
627 | // A co-routine that posts a character to a queue then blocks for a fixed
628 | // period. The character is incremented each time.
629 | static void vSendingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
630 | {
631 | // cChar holds its value while this co-routine is blocked and must therefore
632 | // be declared static.
633 | static char cCharToTx = 'a';
634 | BaseType_t xResult;
635 |
636 | // All co-routines must start with a call to crSTART().
637 | crSTART( xHandle );
638 |
639 | for( ;; )
640 | {
641 | // Send the next character to the queue.
642 | crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
643 |
644 | if( xResult == pdPASS )
645 | {
646 | // The character was successfully posted to the queue.
647 | }
648 | else
649 | {
650 | // Could not post the character to the queue.
651 | }
652 |
653 | // Enable the UART Tx interrupt to cause an interrupt in this
654 | // hypothetical UART. The interrupt will obtain the character
655 | // from the queue and send it.
656 | ENABLE_RX_INTERRUPT();
657 |
658 | // Increment to the next character then block for a fixed period.
659 | // cCharToTx will maintain its value across the delay as it is
660 | // declared static.
661 | cCharToTx++;
662 | if( cCharToTx > 'x' )
663 | {
664 | cCharToTx = 'a';
665 | }
666 | crDELAY( 100 );
667 | }
668 |
669 | // All co-routines must end with a call to crEND().
670 | crEND();
671 | }
672 |
673 | // An ISR that uses a queue to receive characters to send on a UART.
674 | void vUART_ISR( void )
675 | {
676 | char cCharToTx;
677 | BaseType_t xCRWokenByPost = pdFALSE;
678 |
679 | while( UART_TX_REG_EMPTY() )
680 | {
681 | // Are there any characters in the queue waiting to be sent?
682 | // xCRWokenByPost will automatically be set to pdTRUE if a co-routine
683 | // is woken by the post - ensuring that only a single co-routine is
684 | // woken no matter how many times we go around this loop.
685 | if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
686 | {
687 | SEND_CHARACTER( cCharToTx );
688 | }
689 | }
690 | }
691 | * \defgroup crQUEUE_RECEIVE_FROM_ISR crQUEUE_RECEIVE_FROM_ISR
692 | * \ingroup Tasks
693 | */
694 | #define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) xQueueCRReceiveFromISR( ( pxQueue ), ( pvBuffer ), ( pxCoRoutineWoken ) )
695 |
696 | /*
697 | * This function is intended for internal use by the co-routine macros only.
698 | * The macro nature of the co-routine implementation requires that the
699 | * prototype appears here. The function should not be used by application
700 | * writers.
701 | *
702 | * Removes the current co-routine from its ready list and places it in the
703 | * appropriate delayed list.
704 | */
705 | void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, List_t *pxEventList );
706 |
707 | /*
708 | * This function is intended for internal use by the queue implementation only.
709 | * The function should not be used by application writers.
710 | *
711 | * Removes the highest priority co-routine from the event list and places it in
712 | * the pending ready list.
713 | */
714 | BaseType_t xCoRoutineRemoveFromEventList( const List_t *pxEventList );
715 |
716 | #ifdef __cplusplus
717 | }
718 | #endif
719 |
720 | #endif /* CO_ROUTINE_H */
721 |
--------------------------------------------------------------------------------