]> begriffs open source - cmsis-freertos/blob - Demo/Common/Minimal/IntQueue.c
Updated pack to FreeRTOS 10.4.6
[cmsis-freertos] / Demo / Common / Minimal / IntQueue.c
1 /*
2  * FreeRTOS V202111.00
3  * Copyright (C) 2020 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 /*
29  * This file defines one of the more complex set of demo/test tasks.  They are
30  * designed to stress test the queue implementation though pseudo simultaneous
31  * multiple reads and multiple writes from both tasks of varying priority and
32  * interrupts.  The interrupts are prioritised such to ensure that nesting
33  * occurs (for those ports that support it).
34  *
35  * The test ensures that, while being accessed from three tasks and two
36  * interrupts, all the data sent to the queues is also received from
37  * the same queue, and that no duplicate items are either sent or received.
38  * The tests also ensure that a low priority task is never able to successfully
39  * read from or write to a queue when a task of higher priority is attempting
40  * the same operation.
41  */
42
43 /* Standard includes. */
44 #include <string.h>
45
46 /* SafeRTOS includes. */
47 #include "FreeRTOS.h"
48 #include "queue.h"
49 #include "task.h"
50
51 /* Demo app includes. */
52 #include "IntQueue.h"
53 #include "IntQueueTimer.h"
54
55 #if ( INCLUDE_eTaskGetState != 1 )
56     #error INCLUDE_eTaskGetState must be set to 1 in FreeRTOSConfig.h to use this demo file.
57 #endif
58
59 /* Priorities used by test tasks. */
60 #ifndef intqHIGHER_PRIORITY
61     #define intqHIGHER_PRIORITY          ( configMAX_PRIORITIES - 2 )
62 #endif
63 #define intqLOWER_PRIORITY               ( tskIDLE_PRIORITY )
64
65 /* The number of values to send/receive before checking that all values were
66  * processed as expected. */
67 #define intqNUM_VALUES_TO_LOG            ( 200 )
68 #define intqSHORT_DELAY                  ( 140 )
69
70 /* The value by which the value being sent to or received from a queue should
71  * increment past intqNUM_VALUES_TO_LOG before we check that all values have been
72  * sent/received correctly.  This is done to ensure that all tasks and interrupts
73  * accessing the queue have completed their accesses with the
74  * intqNUM_VALUES_TO_LOG range. */
75 #define intqVALUE_OVERRUN                ( 50 )
76
77 /* The delay used by the polling task.  A short delay is used for code
78  * coverage. */
79 #define intqONE_TICK_DELAY               ( 1 )
80
81 /* Each task and interrupt is given a unique identifier.  This value is used to
82  * identify which task sent or received each value.  The identifier is also used
83  * to distinguish between two tasks that are running the same task function. */
84 #define intqHIGH_PRIORITY_TASK1          ( ( UBaseType_t ) 1 )
85 #define intqHIGH_PRIORITY_TASK2          ( ( UBaseType_t ) 2 )
86 #define intqLOW_PRIORITY_TASK            ( ( UBaseType_t ) 3 )
87 #define intqFIRST_INTERRUPT              ( ( UBaseType_t ) 4 )
88 #define intqSECOND_INTERRUPT             ( ( UBaseType_t ) 5 )
89 #define intqQUEUE_LENGTH                 ( ( UBaseType_t ) 10 )
90
91 /* At least intqMIN_ACCEPTABLE_TASK_COUNT values should be sent to/received
92  * from each queue by each task, otherwise an error is detected. */
93 #define intqMIN_ACCEPTABLE_TASK_COUNT    ( 5 )
94
95 /* Send the next value to the queue that is normally empty.  This is called
96  * from within the interrupts. */
97 #define timerNORMALLY_EMPTY_TX()                                                                                                          \
98     if( xQueueIsQueueFullFromISR( xNormallyEmptyQueue ) != pdTRUE )                                                                       \
99     {                                                                                                                                     \
100         UBaseType_t uxSavedInterruptStatus;                                                                                               \
101         uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();                                                                       \
102         {                                                                                                                                 \
103             uxValueForNormallyEmptyQueue++;                                                                                               \
104             if( xQueueSendFromISR( xNormallyEmptyQueue, ( void * ) &uxValueForNormallyEmptyQueue, &xHigherPriorityTaskWoken ) != pdPASS ) \
105             {                                                                                                                             \
106                 uxValueForNormallyEmptyQueue--;                                                                                           \
107             }                                                                                                                             \
108         }                                                                                                                                 \
109         portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );                                                                      \
110     }                                                                                                                                     \
111
112
113 /* Send the next value to the queue that is normally full.  This is called
114  * from within the interrupts. */
115 #define timerNORMALLY_FULL_TX()                                                                                                         \
116     if( xQueueIsQueueFullFromISR( xNormallyFullQueue ) != pdTRUE )                                                                      \
117     {                                                                                                                                   \
118         UBaseType_t uxSavedInterruptStatus;                                                                                             \
119         uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();                                                                     \
120         {                                                                                                                               \
121             uxValueForNormallyFullQueue++;                                                                                              \
122             if( xQueueSendFromISR( xNormallyFullQueue, ( void * ) &uxValueForNormallyFullQueue, &xHigherPriorityTaskWoken ) != pdPASS ) \
123             {                                                                                                                           \
124                 uxValueForNormallyFullQueue--;                                                                                          \
125             }                                                                                                                           \
126         }                                                                                                                               \
127         portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );                                                                    \
128     }                                                                                                                                   \
129
130
131 /* Receive a value from the normally empty queue.  This is called from within
132  * an interrupt. */
133 #define timerNORMALLY_EMPTY_RX()                                                                         \
134     if( xQueueReceiveFromISR( xNormallyEmptyQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) != pdPASS ) \
135     {                                                                                                    \
136         prvQueueAccessLogError( __LINE__ );                                                              \
137     }                                                                                                    \
138     else                                                                                                 \
139     {                                                                                                    \
140         prvRecordValue_NormallyEmpty( uxRxedValue, intqSECOND_INTERRUPT );                               \
141     }
142
143 /* Receive a value from the normally full queue.  This is called from within
144  * an interrupt. */
145 #define timerNORMALLY_FULL_RX()                                                                         \
146     if( xQueueReceiveFromISR( xNormallyFullQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) == pdPASS ) \
147     {                                                                                                   \
148         prvRecordValue_NormallyFull( uxRxedValue, intqSECOND_INTERRUPT );                               \
149     }                                                                                                   \
150
151
152 /*-----------------------------------------------------------*/
153
154 /* The two queues used by the test. */
155 static QueueHandle_t xNormallyEmptyQueue, xNormallyFullQueue;
156
157 /* Variables used to detect a stall in one of the tasks. */
158 static volatile UBaseType_t uxHighPriorityLoops1 = 0, uxHighPriorityLoops2 = 0, uxLowPriorityLoops1 = 0, uxLowPriorityLoops2 = 0;
159
160 /* Any unexpected behaviour sets xErrorStatus to fail and log the line that
161  * caused the error in xErrorLine. */
162 static BaseType_t xErrorStatus = pdPASS;
163 static volatile UBaseType_t xErrorLine = ( UBaseType_t ) 0;
164
165 /* Used for sequencing between tasks. */
166 static BaseType_t xWasSuspended = pdFALSE;
167
168 /* The values that are sent to the queues.  An incremented value is sent each
169  * time to each queue. */
170 static volatile UBaseType_t uxValueForNormallyEmptyQueue = 0, uxValueForNormallyFullQueue = 0;
171
172 /* A handle to some of the tasks is required so they can be suspended/resumed. */
173 TaskHandle_t xHighPriorityNormallyEmptyTask1, xHighPriorityNormallyEmptyTask2, xHighPriorityNormallyFullTask1, xHighPriorityNormallyFullTask2;
174
175 /* When a value is received in a queue the value is ticked off in the array
176  * the array position of the value is set to a the identifier of the task or
177  * interrupt that accessed the queue.  This way missing or duplicate values can be
178  * detected. */
179 static uint8_t ucNormallyEmptyReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 };
180 static uint8_t ucNormallyFullReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 };
181
182 /* The test tasks themselves. */
183 static void prvLowerPriorityNormallyEmptyTask( void * pvParameters );
184 static void prvLowerPriorityNormallyFullTask( void * pvParameters );
185 static void prvHigherPriorityNormallyEmptyTask( void * pvParameters );
186 static void prv1stHigherPriorityNormallyFullTask( void * pvParameters );
187 static void prv2ndHigherPriorityNormallyFullTask( void * pvParameters );
188
189 /* Used to mark the positions within the ucNormallyEmptyReceivedValues and
190  * ucNormallyFullReceivedValues arrays, while checking for duplicates. */
191 static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue,
192                                           UBaseType_t uxSource );
193 static void prvRecordValue_NormallyFull( UBaseType_t uxValue,
194                                          UBaseType_t uxSource );
195
196 /* Logs the line on which an error occurred. */
197 static void prvQueueAccessLogError( UBaseType_t uxLine );
198
199 /*-----------------------------------------------------------*/
200
201 void vStartInterruptQueueTasks( void )
202 {
203     /* Start the test tasks. */
204     xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H1QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask1 );
205     xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H2QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask2 );
206     xTaskCreate( prvLowerPriorityNormallyEmptyTask, "L1QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL );
207     xTaskCreate( prv1stHigherPriorityNormallyFullTask, "H1QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask1 );
208     xTaskCreate( prv2ndHigherPriorityNormallyFullTask, "H2QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask2 );
209     xTaskCreate( prvLowerPriorityNormallyFullTask, "L2QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL );
210
211     /* Create the queues that are accessed by multiple tasks and multiple
212      * interrupts. */
213     xNormallyFullQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) );
214     xNormallyEmptyQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) );
215
216     /* vQueueAddToRegistry() adds the queue to the queue registry, if one is
217      * in use.  The queue registry is provided as a means for kernel aware
218      * debuggers to locate queues and has no purpose if a kernel aware debugger
219      * is not being used.  The call to vQueueAddToRegistry() will be removed
220      * by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
221      * defined to be less than 1. */
222     vQueueAddToRegistry( xNormallyFullQueue, "NormallyFull" );
223     vQueueAddToRegistry( xNormallyEmptyQueue, "NormallyEmpty" );
224 }
225 /*-----------------------------------------------------------*/
226
227 static void prvRecordValue_NormallyFull( UBaseType_t uxValue,
228                                          UBaseType_t uxSource )
229 {
230     if( uxValue < intqNUM_VALUES_TO_LOG )
231     {
232         /* We don't expect to receive the same value twice, so if the value
233          * has already been marked as received an error has occurred. */
234         if( ucNormallyFullReceivedValues[ uxValue ] != 0x00 )
235         {
236             prvQueueAccessLogError( __LINE__ );
237         }
238
239         /* Log that this value has been received. */
240         ucNormallyFullReceivedValues[ uxValue ] = ( uint8_t ) uxSource;
241     }
242 }
243 /*-----------------------------------------------------------*/
244
245 static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue,
246                                           UBaseType_t uxSource )
247 {
248     if( uxValue < intqNUM_VALUES_TO_LOG )
249     {
250         /* We don't expect to receive the same value twice, so if the value
251          * has already been marked as received an error has occurred. */
252         if( ucNormallyEmptyReceivedValues[ uxValue ] != 0x00 )
253         {
254             prvQueueAccessLogError( __LINE__ );
255         }
256
257         /* Log that this value has been received. */
258         ucNormallyEmptyReceivedValues[ uxValue ] = ( uint8_t ) uxSource;
259     }
260 }
261 /*-----------------------------------------------------------*/
262
263 static void prvQueueAccessLogError( UBaseType_t uxLine )
264 {
265     /* Latch the line number that caused the error. */
266     xErrorLine = uxLine;
267     xErrorStatus = pdFAIL;
268 }
269 /*-----------------------------------------------------------*/
270
271 static void prvHigherPriorityNormallyEmptyTask( void * pvParameters )
272 {
273     UBaseType_t uxRxed, ux, uxTask1, uxTask2, uxInterrupts, uxErrorCount1 = 0, uxErrorCount2 = 0;
274
275     /* The timer should not be started until after the scheduler has started.
276      * More than one task is running this code so we check the parameter value
277      * to determine which task should start the timer. */
278     if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 )
279     {
280         vInitialiseTimerForIntQueueTest();
281     }
282
283     for( ; ; )
284     {
285         /* Block waiting to receive a value from the normally empty queue.
286          * Interrupts will write to the queue so we should receive a value. */
287         if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqSHORT_DELAY ) != pdPASS )
288         {
289             prvQueueAccessLogError( __LINE__ );
290         }
291         else
292         {
293             /* Note which value was received so we can check all expected
294              * values are received and no values are duplicated. */
295             prvRecordValue_NormallyEmpty( uxRxed, ( UBaseType_t ) pvParameters );
296         }
297
298         /* Ensure the other task running this code gets a chance to execute. */
299         taskYIELD();
300
301         if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 )
302         {
303             /* Have we received all the expected values? */
304             if( uxValueForNormallyEmptyQueue > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) )
305             {
306                 vTaskSuspend( xHighPriorityNormallyEmptyTask2 );
307
308                 uxTask1 = 0;
309                 uxTask2 = 0;
310                 uxInterrupts = 0;
311
312                 /* Loop through the array, checking that both tasks have
313                  * placed values into the array, and that no values are missing.
314                  * Start at 1 as we expect position 0 to be unused. */
315                 for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ )
316                 {
317                     if( ucNormallyEmptyReceivedValues[ ux ] == 0 )
318                     {
319                         /* A value is missing. */
320                         prvQueueAccessLogError( __LINE__ );
321                     }
322                     else
323                     {
324                         if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK1 )
325                         {
326                             /* Value was placed into the array by task 1. */
327                             uxTask1++;
328                         }
329                         else if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK2 )
330                         {
331                             /* Value was placed into the array by task 2. */
332                             uxTask2++;
333                         }
334                         else if( ucNormallyEmptyReceivedValues[ ux ] == intqSECOND_INTERRUPT )
335                         {
336                             uxInterrupts++;
337                         }
338                     }
339                 }
340
341                 if( uxTask1 < intqMIN_ACCEPTABLE_TASK_COUNT )
342                 {
343                     /* Only task 2 seemed to log any values. */
344                     uxErrorCount1++;
345
346                     if( uxErrorCount1 > 2 )
347                     {
348                         prvQueueAccessLogError( __LINE__ );
349                     }
350                 }
351                 else
352                 {
353                     uxErrorCount1 = 0;
354                 }
355
356                 if( uxTask2 < intqMIN_ACCEPTABLE_TASK_COUNT )
357                 {
358                     /* Only task 1 seemed to log any values. */
359                     uxErrorCount2++;
360
361                     if( uxErrorCount2 > 2 )
362                     {
363                         prvQueueAccessLogError( __LINE__ );
364                     }
365                 }
366                 else
367                 {
368                     uxErrorCount2 = 0;
369                 }
370
371                 if( uxInterrupts == 0 )
372                 {
373                     prvQueueAccessLogError( __LINE__ );
374                 }
375
376                 /* Clear the array again, ready to start a new cycle. */
377                 memset( ucNormallyEmptyReceivedValues, 0x00, sizeof( ucNormallyEmptyReceivedValues ) );
378
379                 uxHighPriorityLoops1++;
380                 uxValueForNormallyEmptyQueue = 0;
381
382                 /* Suspend ourselves, allowing the lower priority task to
383                  * actually receive something from the queue.  Until now it
384                  * will have been prevented from doing so by the higher
385                  * priority tasks.  The lower priority task will resume us
386                  * if it receives something.  We will then resume the other
387                  * higher priority task. */
388                 vTaskSuspend( NULL );
389                 vTaskResume( xHighPriorityNormallyEmptyTask2 );
390             }
391         }
392     }
393 }
394 /*-----------------------------------------------------------*/
395
396 static void prvLowerPriorityNormallyEmptyTask( void * pvParameters )
397 {
398     UBaseType_t uxValue, uxRxed;
399
400     /* The parameters are not being used so avoid compiler warnings. */
401     ( void ) pvParameters;
402
403     for( ; ; )
404     {
405         if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqONE_TICK_DELAY ) != errQUEUE_EMPTY )
406         {
407             /* A value should only be obtained when the high priority task is
408              * suspended. */
409             if( eTaskGetState( xHighPriorityNormallyEmptyTask1 ) != eSuspended )
410             {
411                 prvQueueAccessLogError( __LINE__ );
412             }
413
414             prvRecordValue_NormallyEmpty( uxRxed, intqLOW_PRIORITY_TASK );
415
416             /* Wake the higher priority task again. */
417             vTaskResume( xHighPriorityNormallyEmptyTask1 );
418             uxLowPriorityLoops1++;
419         }
420         else
421         {
422             /* Raise our priority while we send so we can preempt the higher
423              *  priority task, and ensure we get the Tx value into the queue. */
424             vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 );
425
426             portENTER_CRITICAL();
427             {
428                 uxValueForNormallyEmptyQueue++;
429                 uxValue = uxValueForNormallyEmptyQueue;
430             }
431             portEXIT_CRITICAL();
432
433             if( xQueueSend( xNormallyEmptyQueue, &uxValue, portMAX_DELAY ) != pdPASS )
434             {
435                 prvQueueAccessLogError( __LINE__ );
436             }
437
438             vTaskPrioritySet( NULL, intqLOWER_PRIORITY );
439         }
440     }
441 }
442 /*-----------------------------------------------------------*/
443
444 static void prv1stHigherPriorityNormallyFullTask( void * pvParameters )
445 {
446     UBaseType_t uxValueToTx, ux, uxInterrupts;
447
448     /* The parameters are not being used so avoid compiler warnings. */
449     ( void ) pvParameters;
450
451     /* Make sure the queue starts full or near full.  >> 1 as there are two
452      * high priority tasks. */
453     for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ )
454     {
455         portENTER_CRITICAL();
456         {
457             uxValueForNormallyFullQueue++;
458             uxValueToTx = uxValueForNormallyFullQueue;
459         }
460         portEXIT_CRITICAL();
461
462         xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY );
463     }
464
465     for( ; ; )
466     {
467         portENTER_CRITICAL();
468         {
469             uxValueForNormallyFullQueue++;
470             uxValueToTx = uxValueForNormallyFullQueue;
471         }
472         portEXIT_CRITICAL();
473
474         if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS )
475         {
476             /* intqHIGH_PRIORITY_TASK2 is never suspended so we would not
477              * expect it to ever time out. */
478             prvQueueAccessLogError( __LINE__ );
479         }
480
481         /* Allow the other task running this code to run. */
482         taskYIELD();
483
484         /* Have all the expected values been sent to the queue? */
485         if( uxValueToTx > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) )
486         {
487             /* Make sure the other high priority task completes its send of
488              * any values below intqNUM_VALUE_TO_LOG. */
489             vTaskDelay( intqSHORT_DELAY );
490
491             vTaskSuspend( xHighPriorityNormallyFullTask2 );
492
493             if( xWasSuspended == pdTRUE )
494             {
495                 /* We would have expected the other high priority task to have
496                  * set this back to false by now. */
497                 prvQueueAccessLogError( __LINE__ );
498             }
499
500             /* Set the suspended flag so an error is not logged if the other
501              * task recognises a time out when it is unsuspended. */
502             xWasSuspended = pdTRUE;
503
504             /* Check interrupts are also sending. */
505             uxInterrupts = 0U;
506
507             /* Start at 1 as we expect position 0 to be unused. */
508             for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ )
509             {
510                 if( ucNormallyFullReceivedValues[ ux ] == 0 )
511                 {
512                     /* A value was missing. */
513                     prvQueueAccessLogError( __LINE__ );
514                 }
515                 else if( ucNormallyFullReceivedValues[ ux ] == intqSECOND_INTERRUPT )
516                 {
517                     uxInterrupts++;
518                 }
519             }
520
521             if( uxInterrupts == 0 )
522             {
523                 /* No writes from interrupts were found.  Are interrupts
524                  * actually running? */
525                 prvQueueAccessLogError( __LINE__ );
526             }
527
528             /* Reset the array ready for the next cycle. */
529             memset( ucNormallyFullReceivedValues, 0x00, sizeof( ucNormallyFullReceivedValues ) );
530
531             uxHighPriorityLoops2++;
532             uxValueForNormallyFullQueue = 0;
533
534             /* Suspend ourselves, allowing the lower priority task to
535              * actually receive something from the queue.  Until now it
536              * will have been prevented from doing so by the higher
537              * priority tasks.  The lower priority task will resume us
538              * if it receives something.  We will then resume the other
539              * higher priority task. */
540             vTaskSuspend( NULL );
541             vTaskResume( xHighPriorityNormallyFullTask2 );
542         }
543     }
544 }
545 /*-----------------------------------------------------------*/
546
547 static void prv2ndHigherPriorityNormallyFullTask( void * pvParameters )
548 {
549     UBaseType_t uxValueToTx, ux;
550
551     /* The parameters are not being used so avoid compiler warnings. */
552     ( void ) pvParameters;
553
554     /* Make sure the queue starts full or near full.  >> 1 as there are two
555      * high priority tasks. */
556     for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ )
557     {
558         portENTER_CRITICAL();
559         {
560             uxValueForNormallyFullQueue++;
561             uxValueToTx = uxValueForNormallyFullQueue;
562         }
563         portEXIT_CRITICAL();
564
565         xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY );
566     }
567
568     for( ; ; )
569     {
570         portENTER_CRITICAL();
571         {
572             uxValueForNormallyFullQueue++;
573             uxValueToTx = uxValueForNormallyFullQueue;
574         }
575         portEXIT_CRITICAL();
576
577         if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS )
578         {
579             if( xWasSuspended != pdTRUE )
580             {
581                 /* It is ok to time out if the task has been suspended. */
582                 prvQueueAccessLogError( __LINE__ );
583             }
584         }
585
586         xWasSuspended = pdFALSE;
587
588         taskYIELD();
589     }
590 }
591 /*-----------------------------------------------------------*/
592
593 static void prvLowerPriorityNormallyFullTask( void * pvParameters )
594 {
595     UBaseType_t uxValue, uxTxed = 9999;
596
597     /* The parameters are not being used so avoid compiler warnings. */
598     ( void ) pvParameters;
599
600     for( ; ; )
601     {
602         if( xQueueSend( xNormallyFullQueue, &uxTxed, intqONE_TICK_DELAY ) != errQUEUE_FULL )
603         {
604             /* Should only succeed when the higher priority task is suspended */
605             if( eTaskGetState( xHighPriorityNormallyFullTask1 ) != eSuspended )
606             {
607                 prvQueueAccessLogError( __LINE__ );
608             }
609
610             vTaskResume( xHighPriorityNormallyFullTask1 );
611             uxLowPriorityLoops2++;
612         }
613         else
614         {
615             /* Raise our priority while we receive so we can preempt the higher
616              * priority task, and ensure we get the value from the queue. */
617             vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 );
618
619             if( xQueueReceive( xNormallyFullQueue, &uxValue, portMAX_DELAY ) != pdPASS )
620             {
621                 prvQueueAccessLogError( __LINE__ );
622             }
623             else
624             {
625                 prvRecordValue_NormallyFull( uxValue, intqLOW_PRIORITY_TASK );
626             }
627
628             vTaskPrioritySet( NULL, intqLOWER_PRIORITY );
629         }
630     }
631 }
632 /*-----------------------------------------------------------*/
633
634 BaseType_t xFirstTimerHandler( void )
635 {
636     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
637     UBaseType_t uxRxedValue;
638     static UBaseType_t uxNextOperation = 0;
639
640     /* Called from a timer interrupt.  Perform various read and write
641      * accesses on the queues. */
642
643     uxNextOperation++;
644
645     if( uxNextOperation & ( UBaseType_t ) 0x01 )
646     {
647         timerNORMALLY_EMPTY_TX();
648         timerNORMALLY_EMPTY_TX();
649         timerNORMALLY_EMPTY_TX();
650     }
651     else
652     {
653         timerNORMALLY_FULL_RX();
654         timerNORMALLY_FULL_RX();
655         timerNORMALLY_FULL_RX();
656     }
657
658     return xHigherPriorityTaskWoken;
659 }
660 /*-----------------------------------------------------------*/
661
662 BaseType_t xSecondTimerHandler( void )
663 {
664     UBaseType_t uxRxedValue;
665     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
666     static UBaseType_t uxNextOperation = 0;
667
668     /* Called from a timer interrupt.  Perform various read and write
669      * accesses on the queues. */
670
671     uxNextOperation++;
672
673     if( uxNextOperation & ( UBaseType_t ) 0x01 )
674     {
675         timerNORMALLY_EMPTY_TX();
676         timerNORMALLY_EMPTY_TX();
677
678         timerNORMALLY_EMPTY_RX();
679         timerNORMALLY_EMPTY_RX();
680     }
681     else
682     {
683         timerNORMALLY_FULL_RX();
684         timerNORMALLY_FULL_TX();
685         timerNORMALLY_FULL_TX();
686         timerNORMALLY_FULL_TX();
687     }
688
689     return xHigherPriorityTaskWoken;
690 }
691 /*-----------------------------------------------------------*/
692
693
694 BaseType_t xAreIntQueueTasksStillRunning( void )
695 {
696     static UBaseType_t uxLastHighPriorityLoops1 = 0, uxLastHighPriorityLoops2 = 0, uxLastLowPriorityLoops1 = 0, uxLastLowPriorityLoops2 = 0;
697
698     /* xErrorStatus can be set outside of this function.  This function just
699      * checks that all the tasks are still cycling. */
700
701     if( uxHighPriorityLoops1 == uxLastHighPriorityLoops1 )
702     {
703         /* The high priority 1 task has stalled. */
704         prvQueueAccessLogError( __LINE__ );
705     }
706
707     uxLastHighPriorityLoops1 = uxHighPriorityLoops1;
708
709     if( uxHighPriorityLoops2 == uxLastHighPriorityLoops2 )
710     {
711         /* The high priority 2 task has stalled. */
712         prvQueueAccessLogError( __LINE__ );
713     }
714
715     uxLastHighPriorityLoops2 = uxHighPriorityLoops2;
716
717     if( uxLowPriorityLoops1 == uxLastLowPriorityLoops1 )
718     {
719         /* The low priority 1 task has stalled. */
720         prvQueueAccessLogError( __LINE__ );
721     }
722
723     uxLastLowPriorityLoops1 = uxLowPriorityLoops1;
724
725     if( uxLowPriorityLoops2 == uxLastLowPriorityLoops2 )
726     {
727         /* The low priority 2 task has stalled. */
728         prvQueueAccessLogError( __LINE__ );
729     }
730
731     uxLastLowPriorityLoops2 = uxLowPriorityLoops2;
732
733     return xErrorStatus;
734 }