summaryrefslogtreecommitdiff
path: root/Demo/Common/Full/events.c
blob: ce1db2d6e98c0b33bcebf4701cdb308e649e2b75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/*
	FreeRTOS.org V5.1.2 - Copyright (C) 2003-2009 Richard Barry.

	This file is part of the FreeRTOS.org distribution.

	FreeRTOS.org is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	FreeRTOS.org is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with FreeRTOS.org; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	A special exception to the GPL can be applied should you wish to distribute
	a combined work that includes FreeRTOS.org, without being obliged to provide
	the source code for any proprietary components.  See the licensing section 
	of http://www.FreeRTOS.org for full details of how and when the exception
	can be applied.

    ***************************************************************************
    ***************************************************************************
    *                                                                         *
    * Get the FreeRTOS eBook!  See http://www.FreeRTOS.org/Documentation      *
	*                                                                         *
	* This is a concise, step by step, 'hands on' guide that describes both   *
	* general multitasking concepts and FreeRTOS specifics. It presents and   *
	* explains numerous examples that are written using the FreeRTOS API.     *
	* Full source code for all the examples is provided in an accompanying    *
	* .zip file.                                                              *
    *                                                                         *
    ***************************************************************************
    ***************************************************************************

	Please ensure to read the configuration and relevant port sections of the
	online documentation.

	http://www.FreeRTOS.org - Documentation, latest information, license and 
	contact details.

	http://www.SafeRTOS.com - A version that is certified for use in safety 
	critical systems.

	http://www.OpenRTOS.com - Commercial support, development, porting, 
	licensing and training services.
*/

/**
 * This file exercises the event mechanism whereby more than one task is
 * blocked waiting for the same event.
 *
 * The demo creates five tasks - four 'event' tasks, and a controlling task.
 * The event tasks have various different priorities and all block on reading
 * the same queue.  The controlling task writes data to the queue, then checks
 * to see which of the event tasks read the data from the queue.  The
 * controlling task has the lowest priority of all the tasks so is guaranteed
 * to always get preempted immediately upon writing to the queue.
 *
 * By selectively suspending and resuming the event tasks the controlling task
 * can check that the highest priority task that is blocked on the queue is the
 * task that reads the posted data from the queue.
 *
 * Two of the event tasks share the same priority.  When neither of these tasks
 * are suspended they should alternate - one reading one message from the queue,
 * the other the next message, etc.
 */

/* Standard includes. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

/* Demo program include files. */
#include "mevents.h"
#include "print.h"

/* Demo specific constants. */
#define evtSTACK_SIZE		( ( unsigned portBASE_TYPE ) configMINIMAL_STACK_SIZE )
#define evtNUM_TASKS		( 4 )
#define evtQUEUE_LENGTH		( ( unsigned portBASE_TYPE ) 3 )
#define evtNO_DELAY						0

/* Just indexes used to uniquely identify the tasks.  Note that two tasks are
'highest' priority. */
#define evtHIGHEST_PRIORITY_INDEX_2		3
#define evtHIGHEST_PRIORITY_INDEX_1		2
#define evtMEDIUM_PRIORITY_INDEX		1
#define evtLOWEST_PRIORITY_INDEX		0

/* Each event task increments one of these counters each time it reads data
from the queue. */
static volatile portBASE_TYPE xTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 };

/* Each time the controlling task posts onto the queue it increments the 
expected count of the task that it expected to read the data from the queue 
(i.e. the task with the highest priority that should be blocked on the queue).  

xExpectedTaskCounters are incremented from the controlling task, and 
xTaskCounters are incremented from the individual event tasks - therefore
comparing xTaskCounters to xExpectedTaskCounters shows whether or not the 
correct task was unblocked by the post. */
static portBASE_TYPE xExpectedTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 };

/* Handles to the four event tasks.  These are required to suspend and resume
the tasks. */
static xTaskHandle xCreatedTasks[ evtNUM_TASKS ];

/* The single queue onto which the controlling task posts, and the four event
tasks block. */
static xQueueHandle xQueue;

/* Flag used to indicate whether or not an error has occurred at any time.
An error is either the queue being full when not expected, or an unexpected
task reading data from the queue. */
static portBASE_TYPE xHealthStatus = pdPASS;

/*-----------------------------------------------------------*/

/* Function that implements the event task.  This is created four times. */
static void prvMultiEventTask( void *pvParameters );

/* Function that implements the controlling task. */
static void prvEventControllerTask( void *pvParameters );

/* This is a utility function that posts data to the queue, then compares 
xExpectedTaskCounters with xTaskCounters to ensure everything worked as 
expected.

The event tasks all have higher priorities the controlling task.  Therefore
the controlling task will always get preempted between writhing to the queue
and checking the task counters. 

@param xExpectedTask  The index to the task that the controlling task thinks
                      should be the highest priority task waiting for data, and
					  therefore the task that will unblock.
					  
@param	xIncrement    The number of items that should be written to the queue.
*/
static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement );

/* This is just incremented each cycle of the controlling tasks function so
the main application can ensure the test is still running. */
static portBASE_TYPE xCheckVariable = 0;

/*-----------------------------------------------------------*/

void vStartMultiEventTasks( void )
{
	/* Create the queue to be used for all the communications. */
	xQueue = xQueueCreate( evtQUEUE_LENGTH, ( unsigned portBASE_TYPE ) sizeof( unsigned portBASE_TYPE ) );

	/* Start the controlling task.  This has the idle priority to ensure it is
	always preempted by the event tasks. */
	xTaskCreate( prvEventControllerTask, "EvntCTRL", evtSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );

	/* Start the four event tasks.  Note that two have priority 3, one 
	priority 2 and the other priority 1. */
	xTaskCreate( prvMultiEventTask, "Event0", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 0 ] ), 1, &( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] ) );
	xTaskCreate( prvMultiEventTask, "Event1", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 1 ] ), 2, &( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] ) );
	xTaskCreate( prvMultiEventTask, "Event2", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 2 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ) );
	xTaskCreate( prvMultiEventTask, "Event3", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 3 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] ) );
}
/*-----------------------------------------------------------*/

static void prvMultiEventTask( void *pvParameters )
{
portBASE_TYPE *pxCounter;
unsigned portBASE_TYPE uxDummy;
const portCHAR * const pcTaskStartMsg = "Multi event task started.\r\n";

	/* The variable this task will increment is passed in as a parameter. */
	pxCounter = ( portBASE_TYPE * ) pvParameters;

	vPrintDisplayMessage( &pcTaskStartMsg );

	for( ;; )
	{
		/* Block on the queue. */
		if( xQueueReceive( xQueue, &uxDummy, portMAX_DELAY ) )
		{
			/* We unblocked by reading the queue - so simply increment
			the counter specific to this task instance. */
			( *pxCounter )++;
		}
		else
		{
			xHealthStatus = pdFAIL;
		}
	}
}
/*-----------------------------------------------------------*/

static void prvEventControllerTask( void *pvParameters )
{
const portCHAR * const pcTaskStartMsg = "Multi event controller task started.\r\n";
portBASE_TYPE xDummy = 0;

	/* Just to stop warnings. */
	( void ) pvParameters;

	vPrintDisplayMessage( &pcTaskStartMsg );

	for( ;; )
	{
		/* All tasks are blocked on the queue.  When a message is posted one of
		the two tasks that share the highest priority should unblock to read
		the queue.  The next message written should unblock the other task with
		the same high priority, and so on in order.   No other task should 
		unblock to read data as they have lower priorities. */

		prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
		prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 );
		prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
		prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 );
		prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );

		/* For the rest of these tests we don't need the second 'highest' 
		priority task - so it is suspended. */
		vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] );



		/* Now suspend the other highest priority task.  The medium priority 
		task will then be the task with the highest priority that remains 
		blocked on the queue. */
		vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
		
		/* This time, when we post onto the queue we will expect the medium
		priority task to unblock and preempt us. */
		prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 );

		/* Now try resuming the highest priority task while the scheduler is
		suspended.  The task should start executing as soon as the scheduler
		is resumed - therefore when we post to the queue again, the highest
		priority task should again preempt us. */
		vTaskSuspendAll();
			vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
		xTaskResumeAll();
		prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
		
		/* Now we are going to suspend the high and medium priority tasks.  The
		low priority task should then preempt us.  Again the task suspension is 
		done with the whole scheduler suspended just for test purposes. */
		vTaskSuspendAll();
			vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
			vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );
		xTaskResumeAll();
		prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 );
		
		/* Do the same basic test another few times - selectively suspending
		and resuming tasks and each time calling prvCheckTaskCounters() passing
		to the function the number of the task we expected to be unblocked by 
		the	post. */

		vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
		prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
		
		vTaskSuspendAll(); /* Just for test. */
			vTaskSuspendAll(); /* Just for test. */
				vTaskSuspendAll(); /* Just for even more test. */
					vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
				xTaskResumeAll();
			xTaskResumeAll();
		xTaskResumeAll();
		prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 );
		
		vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );
		prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 );
		
		vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
		prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );

		/* Now a slight change, first suspend all tasks. */
		vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
		vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );
		vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );
		
		/* Now when we resume the low priority task and write to the queue 3 
		times.  We expect the low priority task to service the queue three
		times. */
		vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );
		prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, evtQUEUE_LENGTH );
		
		/* Again suspend all tasks (only the low priority task is not suspended
		already). */
		vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );
		
		/* This time we are going to suspend the scheduler, resume the low
		priority task, then resume the high priority task.  In this state we
		will write to the queue three times.  When the scheduler is resumed
		we expect the high priority task to service all three messages. */
		vTaskSuspendAll();
		{
			vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );
			vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
			
			for( xDummy = 0; xDummy < evtQUEUE_LENGTH; xDummy++ )
			{
				if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE )
				{
					xHealthStatus = pdFAIL;
				}
			}			
			
			/* The queue should not have been serviced yet!.  The scheduler
			is still suspended. */
			if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )
			{
				xHealthStatus = pdFAIL;
			}
		}
		xTaskResumeAll();

		/* We should have been preempted by resuming the scheduler - so by the
		time we are running again we expect the high priority task to have 
		removed three items from the queue. */
		xExpectedTaskCounters[ evtHIGHEST_PRIORITY_INDEX_1 ] += evtQUEUE_LENGTH;
		if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )
		{
			xHealthStatus = pdFAIL;
		}
		
		/* The medium priority and second high priority tasks are still 
		suspended.  Make sure to resume them before starting again. */
		vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );
		vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] );

		/* Just keep incrementing to show the task is still executing. */
		xCheckVariable++;
	}
}
/*-----------------------------------------------------------*/

static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement )
{
portBASE_TYPE xDummy = 0;

	/* Write to the queue the requested number of times.  The data written is
	not important. */
	for( xDummy = 0; xDummy < xIncrement; xDummy++ )
	{
		if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE )
		{
			/* Did not expect to ever find the queue full. */
			xHealthStatus = pdFAIL;
		}
	}

	/* All the tasks blocked on the queue have a priority higher than the 
	controlling task.  Writing to the queue will therefore have caused this
	task to be preempted.  By the time this line executes the event task will
	have executed and incremented its counter.  Increment the expected counter
	to the same value. */
	( xExpectedTaskCounters[ xExpectedTask ] ) += xIncrement;

	/* Check the actual counts and expected counts really are the same. */
	if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )
	{
		/* The counters were not the same.  This means a task we did not expect
		to unblock actually did unblock. */
		xHealthStatus = pdFAIL;
	}
}
/*-----------------------------------------------------------*/

portBASE_TYPE xAreMultiEventTasksStillRunning( void )
{
static portBASE_TYPE xPreviousCheckVariable = 0;

	/* Called externally to periodically check that this test is still
	operational. */

	if( xPreviousCheckVariable == xCheckVariable )
	{
		xHealthStatus = pdFAIL;
	}
	
	xPreviousCheckVariable = xCheckVariable;
	
	return xHealthStatus;	
}