742 lines
31 KiB
C
742 lines
31 KiB
C
/*
|
|
* FreeRTOS V202212.00
|
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
* the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* https://www.FreeRTOS.org
|
|
* https://github.com/FreeRTOS
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* This file defines one of the more complex set of demo/test tasks. They are
|
|
* designed to stress test the queue implementation though pseudo simultaneous
|
|
* multiple reads and multiple writes from both tasks of varying priority and
|
|
* interrupts. The interrupts are prioritised such to ensure that nesting
|
|
* occurs (for those ports that support it).
|
|
*
|
|
* The test ensures that, while being accessed from three tasks and two
|
|
* interrupts, all the data sent to the queues is also received from
|
|
* the same queue, and that no duplicate items are either sent or received.
|
|
* The tests also ensure that a low priority task is never able to successfully
|
|
* read from or write to a queue when a task of higher priority is attempting
|
|
* the same operation.
|
|
*/
|
|
|
|
/* Standard includes. */
|
|
#include <string.h>
|
|
|
|
/* SafeRTOS includes. */
|
|
#include "FreeRTOS.h"
|
|
#include "queue.h"
|
|
#include "task.h"
|
|
|
|
/* Demo app includes. */
|
|
#include "IntQueue.h"
|
|
#include "IntQueueTimer.h"
|
|
|
|
#if ( INCLUDE_eTaskGetState != 1 )
|
|
#error INCLUDE_eTaskGetState must be set to 1 in FreeRTOSConfig.h to use this demo file.
|
|
#endif
|
|
|
|
/* Priorities used by test tasks. */
|
|
#ifndef intqHIGHER_PRIORITY
|
|
#define intqHIGHER_PRIORITY ( configMAX_PRIORITIES - 2 )
|
|
#endif
|
|
#define intqLOWER_PRIORITY ( tskIDLE_PRIORITY )
|
|
|
|
/* The number of values to send/receive before checking that all values were
|
|
* processed as expected. */
|
|
#define intqNUM_VALUES_TO_LOG ( 200 )
|
|
#define intqSHORT_DELAY ( 140 )
|
|
|
|
/* The value by which the value being sent to or received from a queue should
|
|
* increment past intqNUM_VALUES_TO_LOG before we check that all values have been
|
|
* sent/received correctly. This is done to ensure that all tasks and interrupts
|
|
* accessing the queue have completed their accesses with the
|
|
* intqNUM_VALUES_TO_LOG range. */
|
|
#define intqVALUE_OVERRUN ( 50 )
|
|
|
|
/* The delay used by the polling task. A short delay is used for code
|
|
* coverage. */
|
|
#define intqONE_TICK_DELAY ( 1 )
|
|
|
|
/* Each task and interrupt is given a unique identifier. This value is used to
|
|
* identify which task sent or received each value. The identifier is also used
|
|
* to distinguish between two tasks that are running the same task function. */
|
|
#define intqHIGH_PRIORITY_TASK1 ( ( UBaseType_t ) 1 )
|
|
#define intqHIGH_PRIORITY_TASK2 ( ( UBaseType_t ) 2 )
|
|
#define intqLOW_PRIORITY_TASK ( ( UBaseType_t ) 3 )
|
|
#define intqFIRST_INTERRUPT ( ( UBaseType_t ) 4 )
|
|
#define intqSECOND_INTERRUPT ( ( UBaseType_t ) 5 )
|
|
#define intqQUEUE_LENGTH ( ( UBaseType_t ) 10 )
|
|
|
|
/* At least intqMIN_ACCEPTABLE_TASK_COUNT values should be sent to/received
|
|
* from each queue by each task, otherwise an error is detected. */
|
|
#define intqMIN_ACCEPTABLE_TASK_COUNT ( 5 )
|
|
|
|
/* Send the next value to the queue that is normally empty. This is called
|
|
* from within the interrupts. */
|
|
#define timerNORMALLY_EMPTY_TX() \
|
|
if( xQueueIsQueueFullFromISR( xNormallyEmptyQueue ) != pdTRUE ) \
|
|
{ \
|
|
UBaseType_t uxSavedInterruptStatus; \
|
|
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); \
|
|
{ \
|
|
uxValueForNormallyEmptyQueue++; \
|
|
if( xQueueSendFromISR( xNormallyEmptyQueue, ( void * ) &uxValueForNormallyEmptyQueue, &xHigherPriorityTaskWoken ) != pdPASS ) \
|
|
{ \
|
|
uxValueForNormallyEmptyQueue--; \
|
|
} \
|
|
} \
|
|
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); \
|
|
} \
|
|
|
|
|
|
/* Send the next value to the queue that is normally full. This is called
|
|
* from within the interrupts. */
|
|
#define timerNORMALLY_FULL_TX() \
|
|
if( xQueueIsQueueFullFromISR( xNormallyFullQueue ) != pdTRUE ) \
|
|
{ \
|
|
UBaseType_t uxSavedInterruptStatus; \
|
|
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); \
|
|
{ \
|
|
uxValueForNormallyFullQueue++; \
|
|
if( xQueueSendFromISR( xNormallyFullQueue, ( void * ) &uxValueForNormallyFullQueue, &xHigherPriorityTaskWoken ) != pdPASS ) \
|
|
{ \
|
|
uxValueForNormallyFullQueue--; \
|
|
} \
|
|
} \
|
|
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); \
|
|
} \
|
|
|
|
|
|
/* Receive a value from the normally empty queue. This is called from within
|
|
* an interrupt. */
|
|
#define timerNORMALLY_EMPTY_RX() \
|
|
if( xQueueReceiveFromISR( xNormallyEmptyQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) != pdPASS ) \
|
|
{ \
|
|
prvQueueAccessLogError( __LINE__ ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
prvRecordValue_NormallyEmpty( uxRxedValue, intqSECOND_INTERRUPT ); \
|
|
}
|
|
|
|
/* Receive a value from the normally full queue. This is called from within
|
|
* an interrupt. */
|
|
#define timerNORMALLY_FULL_RX() \
|
|
if( xQueueReceiveFromISR( xNormallyFullQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) == pdPASS ) \
|
|
{ \
|
|
prvRecordValue_NormallyFull( uxRxedValue, intqSECOND_INTERRUPT ); \
|
|
} \
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/* The two queues used by the test. */
|
|
static QueueHandle_t xNormallyEmptyQueue, xNormallyFullQueue;
|
|
|
|
/* Variables used to detect a stall in one of the tasks. */
|
|
static volatile UBaseType_t uxHighPriorityLoops1 = 0, uxHighPriorityLoops2 = 0, uxLowPriorityLoops1 = 0, uxLowPriorityLoops2 = 0;
|
|
|
|
/* Any unexpected behaviour sets xErrorStatus to fail and log the line that
|
|
* caused the error in xErrorLine. */
|
|
static BaseType_t xErrorStatus = pdPASS;
|
|
static volatile UBaseType_t xErrorLine = ( UBaseType_t ) 0;
|
|
|
|
/* Used for sequencing between tasks. */
|
|
static BaseType_t xWasSuspended = pdFALSE;
|
|
|
|
/* The values that are sent to the queues. An incremented value is sent each
|
|
* time to each queue. */
|
|
static volatile UBaseType_t uxValueForNormallyEmptyQueue = 0, uxValueForNormallyFullQueue = 0;
|
|
|
|
/* A handle to some of the tasks is required so they can be suspended/resumed. */
|
|
TaskHandle_t xHighPriorityNormallyEmptyTask1, xHighPriorityNormallyEmptyTask2, xHighPriorityNormallyFullTask1, xHighPriorityNormallyFullTask2;
|
|
|
|
/* When a value is received in a queue the value is ticked off in the array
|
|
* the array position of the value is set to a the identifier of the task or
|
|
* interrupt that accessed the queue. This way missing or duplicate values can be
|
|
* detected. */
|
|
static uint8_t ucNormallyEmptyReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 };
|
|
static uint8_t ucNormallyFullReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 };
|
|
|
|
/* The test tasks themselves. */
|
|
static void prvLowerPriorityNormallyEmptyTask( void * pvParameters );
|
|
static void prvLowerPriorityNormallyFullTask( void * pvParameters );
|
|
static void prvHigherPriorityNormallyEmptyTask( void * pvParameters );
|
|
static void prv1stHigherPriorityNormallyFullTask( void * pvParameters );
|
|
static void prv2ndHigherPriorityNormallyFullTask( void * pvParameters );
|
|
|
|
/* Used to mark the positions within the ucNormallyEmptyReceivedValues and
|
|
* ucNormallyFullReceivedValues arrays, while checking for duplicates. */
|
|
static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue,
|
|
UBaseType_t uxSource );
|
|
static void prvRecordValue_NormallyFull( UBaseType_t uxValue,
|
|
UBaseType_t uxSource );
|
|
|
|
/* Logs the line on which an error occurred. */
|
|
static void prvQueueAccessLogError( UBaseType_t uxLine );
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
void vStartInterruptQueueTasks( void )
|
|
{
|
|
/* Start the test tasks. */
|
|
xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H1QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask1 );
|
|
xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H2QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask2 );
|
|
xTaskCreate( prvLowerPriorityNormallyEmptyTask, "L1QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL );
|
|
xTaskCreate( prv1stHigherPriorityNormallyFullTask, "H1QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask1 );
|
|
xTaskCreate( prv2ndHigherPriorityNormallyFullTask, "H2QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask2 );
|
|
xTaskCreate( prvLowerPriorityNormallyFullTask, "L2QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL );
|
|
|
|
/* Create the queues that are accessed by multiple tasks and multiple
|
|
* interrupts. */
|
|
xNormallyFullQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) );
|
|
xNormallyEmptyQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) );
|
|
|
|
/* vQueueAddToRegistry() adds the queue to the queue registry, if one is
|
|
* in use. The queue registry is provided as a means for kernel aware
|
|
* debuggers to locate queues and has no purpose if a kernel aware debugger
|
|
* is not being used. The call to vQueueAddToRegistry() will be removed
|
|
* by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
|
|
* defined to be less than 1. */
|
|
vQueueAddToRegistry( xNormallyFullQueue, "NormallyFull" );
|
|
vQueueAddToRegistry( xNormallyEmptyQueue, "NormallyEmpty" );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prvRecordValue_NormallyFull( UBaseType_t uxValue,
|
|
UBaseType_t uxSource )
|
|
{
|
|
if( uxValue < intqNUM_VALUES_TO_LOG )
|
|
{
|
|
/* We don't expect to receive the same value twice, so if the value
|
|
* has already been marked as received an error has occurred. */
|
|
if( ucNormallyFullReceivedValues[ uxValue ] != 0x00 )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
/* Log that this value has been received. */
|
|
ucNormallyFullReceivedValues[ uxValue ] = ( uint8_t ) uxSource;
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue,
|
|
UBaseType_t uxSource )
|
|
{
|
|
if( uxValue < intqNUM_VALUES_TO_LOG )
|
|
{
|
|
/* We don't expect to receive the same value twice, so if the value
|
|
* has already been marked as received an error has occurred. */
|
|
if( ucNormallyEmptyReceivedValues[ uxValue ] != 0x00 )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
/* Log that this value has been received. */
|
|
ucNormallyEmptyReceivedValues[ uxValue ] = ( uint8_t ) uxSource;
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prvQueueAccessLogError( UBaseType_t uxLine )
|
|
{
|
|
/* Latch the line number that caused the error. */
|
|
xErrorLine = uxLine;
|
|
xErrorStatus = pdFAIL;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prvHigherPriorityNormallyEmptyTask( void * pvParameters )
|
|
{
|
|
UBaseType_t uxRxed, ux, uxTask1, uxTask2, uxInterrupts, uxErrorCount1 = 0, uxErrorCount2 = 0;
|
|
|
|
/* The timer should not be started until after the scheduler has started.
|
|
* More than one task is running this code so we check the parameter value
|
|
* to determine which task should start the timer. */
|
|
if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 )
|
|
{
|
|
vInitialiseTimerForIntQueueTest();
|
|
}
|
|
|
|
for( ; ; )
|
|
{
|
|
/* Block waiting to receive a value from the normally empty queue.
|
|
* Interrupts will write to the queue so we should receive a value. */
|
|
if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqSHORT_DELAY ) != pdPASS )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
else
|
|
{
|
|
/* Note which value was received so we can check all expected
|
|
* values are received and no values are duplicated. */
|
|
prvRecordValue_NormallyEmpty( uxRxed, ( UBaseType_t ) pvParameters );
|
|
}
|
|
|
|
/* Ensure the other task running this code gets a chance to execute. */
|
|
taskYIELD();
|
|
|
|
if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 )
|
|
{
|
|
/* Have we received all the expected values? */
|
|
if( uxValueForNormallyEmptyQueue > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) )
|
|
{
|
|
vTaskSuspend( xHighPriorityNormallyEmptyTask2 );
|
|
|
|
uxTask1 = 0;
|
|
uxTask2 = 0;
|
|
uxInterrupts = 0;
|
|
|
|
/* Loop through the array, checking that both tasks have
|
|
* placed values into the array, and that no values are missing.
|
|
* Start at 1 as we expect position 0 to be unused. */
|
|
for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ )
|
|
{
|
|
if( ucNormallyEmptyReceivedValues[ ux ] == 0 )
|
|
{
|
|
/* A value is missing. */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
else
|
|
{
|
|
if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK1 )
|
|
{
|
|
/* Value was placed into the array by task 1. */
|
|
uxTask1++;
|
|
}
|
|
else if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK2 )
|
|
{
|
|
/* Value was placed into the array by task 2. */
|
|
uxTask2++;
|
|
}
|
|
else if( ucNormallyEmptyReceivedValues[ ux ] == intqSECOND_INTERRUPT )
|
|
{
|
|
uxInterrupts++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( uxTask1 < intqMIN_ACCEPTABLE_TASK_COUNT )
|
|
{
|
|
/* Only task 2 seemed to log any values. */
|
|
uxErrorCount1++;
|
|
|
|
if( uxErrorCount1 > 2 )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uxErrorCount1 = 0;
|
|
}
|
|
|
|
if( uxTask2 < intqMIN_ACCEPTABLE_TASK_COUNT )
|
|
{
|
|
/* Only task 1 seemed to log any values. */
|
|
uxErrorCount2++;
|
|
|
|
if( uxErrorCount2 > 2 )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uxErrorCount2 = 0;
|
|
}
|
|
|
|
if( uxInterrupts == 0 )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
/* Clear the array again, ready to start a new cycle. */
|
|
memset( ucNormallyEmptyReceivedValues, 0x00, sizeof( ucNormallyEmptyReceivedValues ) );
|
|
|
|
uxHighPriorityLoops1++;
|
|
portENTER_CRITICAL();
|
|
{
|
|
uxValueForNormallyEmptyQueue = 0;
|
|
}
|
|
portEXIT_CRITICAL();
|
|
|
|
/* Suspend ourselves, allowing the lower priority task to
|
|
* actually receive something from the queue. Until now it
|
|
* will have been prevented from doing so by the higher
|
|
* priority tasks. The lower priority task will resume us
|
|
* if it receives something. We will then resume the other
|
|
* higher priority task. */
|
|
vTaskSuspend( NULL );
|
|
vTaskResume( xHighPriorityNormallyEmptyTask2 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prvLowerPriorityNormallyEmptyTask( void * pvParameters )
|
|
{
|
|
UBaseType_t uxValue, uxRxed;
|
|
|
|
/* The parameters are not being used so avoid compiler warnings. */
|
|
( void ) pvParameters;
|
|
|
|
for( ; ; )
|
|
{
|
|
if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqONE_TICK_DELAY ) != errQUEUE_EMPTY )
|
|
{
|
|
/* A value should only be obtained when the high priority task is
|
|
* suspended. */
|
|
if( eTaskGetState( xHighPriorityNormallyEmptyTask1 ) != eSuspended )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
prvRecordValue_NormallyEmpty( uxRxed, intqLOW_PRIORITY_TASK );
|
|
|
|
/* Wake the higher priority task again. */
|
|
vTaskResume( xHighPriorityNormallyEmptyTask1 );
|
|
uxLowPriorityLoops1++;
|
|
}
|
|
else
|
|
{
|
|
/* Raise our priority while we send so we can preempt the higher
|
|
* priority task, and ensure we get the Tx value into the queue. */
|
|
vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 );
|
|
|
|
portENTER_CRITICAL();
|
|
{
|
|
uxValueForNormallyEmptyQueue++;
|
|
uxValue = uxValueForNormallyEmptyQueue;
|
|
}
|
|
portEXIT_CRITICAL();
|
|
|
|
if( xQueueSend( xNormallyEmptyQueue, &uxValue, portMAX_DELAY ) != pdPASS )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
vTaskPrioritySet( NULL, intqLOWER_PRIORITY );
|
|
}
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prv1stHigherPriorityNormallyFullTask( void * pvParameters )
|
|
{
|
|
UBaseType_t uxValueToTx, ux, uxInterrupts;
|
|
|
|
/* The parameters are not being used so avoid compiler warnings. */
|
|
( void ) pvParameters;
|
|
|
|
/* Make sure the queue starts full or near full. >> 1 as there are two
|
|
* high priority tasks. */
|
|
for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ )
|
|
{
|
|
portENTER_CRITICAL();
|
|
{
|
|
uxValueForNormallyFullQueue++;
|
|
uxValueToTx = uxValueForNormallyFullQueue;
|
|
}
|
|
portEXIT_CRITICAL();
|
|
|
|
xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY );
|
|
}
|
|
|
|
for( ; ; )
|
|
{
|
|
portENTER_CRITICAL();
|
|
{
|
|
uxValueForNormallyFullQueue++;
|
|
uxValueToTx = uxValueForNormallyFullQueue;
|
|
}
|
|
portEXIT_CRITICAL();
|
|
|
|
if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS )
|
|
{
|
|
/* intqHIGH_PRIORITY_TASK2 is never suspended so we would not
|
|
* expect it to ever time out. */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
/* Allow the other task running this code to run. */
|
|
taskYIELD();
|
|
|
|
/* Have all the expected values been sent to the queue? */
|
|
if( uxValueToTx > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) )
|
|
{
|
|
/* Make sure the other high priority task completes its send of
|
|
* any values below intqNUM_VALUE_TO_LOG. */
|
|
vTaskDelay( intqSHORT_DELAY );
|
|
|
|
vTaskSuspend( xHighPriorityNormallyFullTask2 );
|
|
|
|
if( xWasSuspended == pdTRUE )
|
|
{
|
|
/* We would have expected the other high priority task to have
|
|
* set this back to false by now. */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
/* Set the suspended flag so an error is not logged if the other
|
|
* task recognises a time out when it is unsuspended. */
|
|
xWasSuspended = pdTRUE;
|
|
|
|
/* Check interrupts are also sending. */
|
|
uxInterrupts = 0U;
|
|
|
|
/* Start at 1 as we expect position 0 to be unused. */
|
|
for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ )
|
|
{
|
|
if( ucNormallyFullReceivedValues[ ux ] == 0 )
|
|
{
|
|
/* A value was missing. */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
else if( ucNormallyFullReceivedValues[ ux ] == intqSECOND_INTERRUPT )
|
|
{
|
|
uxInterrupts++;
|
|
}
|
|
}
|
|
|
|
if( uxInterrupts == 0 )
|
|
{
|
|
/* No writes from interrupts were found. Are interrupts
|
|
* actually running? */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
/* Reset the array ready for the next cycle. */
|
|
memset( ucNormallyFullReceivedValues, 0x00, sizeof( ucNormallyFullReceivedValues ) );
|
|
|
|
uxHighPriorityLoops2++;
|
|
portENTER_CRITICAL();
|
|
{
|
|
uxValueForNormallyFullQueue = 0;
|
|
}
|
|
portEXIT_CRITICAL();
|
|
|
|
/* Suspend ourselves, allowing the lower priority task to
|
|
* actually receive something from the queue. Until now it
|
|
* will have been prevented from doing so by the higher
|
|
* priority tasks. The lower priority task will resume us
|
|
* if it receives something. We will then resume the other
|
|
* higher priority task. */
|
|
vTaskSuspend( NULL );
|
|
vTaskResume( xHighPriorityNormallyFullTask2 );
|
|
}
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prv2ndHigherPriorityNormallyFullTask( void * pvParameters )
|
|
{
|
|
UBaseType_t uxValueToTx, ux;
|
|
|
|
/* The parameters are not being used so avoid compiler warnings. */
|
|
( void ) pvParameters;
|
|
|
|
/* Make sure the queue starts full or near full. >> 1 as there are two
|
|
* high priority tasks. */
|
|
for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ )
|
|
{
|
|
portENTER_CRITICAL();
|
|
{
|
|
uxValueForNormallyFullQueue++;
|
|
uxValueToTx = uxValueForNormallyFullQueue;
|
|
}
|
|
portEXIT_CRITICAL();
|
|
|
|
xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY );
|
|
}
|
|
|
|
for( ; ; )
|
|
{
|
|
portENTER_CRITICAL();
|
|
{
|
|
uxValueForNormallyFullQueue++;
|
|
uxValueToTx = uxValueForNormallyFullQueue;
|
|
}
|
|
portEXIT_CRITICAL();
|
|
|
|
if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS )
|
|
{
|
|
if( xWasSuspended != pdTRUE )
|
|
{
|
|
/* It is ok to time out if the task has been suspended. */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
}
|
|
|
|
xWasSuspended = pdFALSE;
|
|
|
|
taskYIELD();
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prvLowerPriorityNormallyFullTask( void * pvParameters )
|
|
{
|
|
UBaseType_t uxValue, uxTxed = 9999;
|
|
|
|
/* The parameters are not being used so avoid compiler warnings. */
|
|
( void ) pvParameters;
|
|
|
|
for( ; ; )
|
|
{
|
|
if( xQueueSend( xNormallyFullQueue, &uxTxed, intqONE_TICK_DELAY ) != errQUEUE_FULL )
|
|
{
|
|
/* Should only succeed when the higher priority task is suspended */
|
|
if( eTaskGetState( xHighPriorityNormallyFullTask1 ) != eSuspended )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
vTaskResume( xHighPriorityNormallyFullTask1 );
|
|
uxLowPriorityLoops2++;
|
|
}
|
|
else
|
|
{
|
|
/* Raise our priority while we receive so we can preempt the higher
|
|
* priority task, and ensure we get the value from the queue. */
|
|
vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 );
|
|
|
|
if( xQueueReceive( xNormallyFullQueue, &uxValue, portMAX_DELAY ) != pdPASS )
|
|
{
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
else
|
|
{
|
|
prvRecordValue_NormallyFull( uxValue, intqLOW_PRIORITY_TASK );
|
|
}
|
|
|
|
vTaskPrioritySet( NULL, intqLOWER_PRIORITY );
|
|
}
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
BaseType_t xFirstTimerHandler( void )
|
|
{
|
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
UBaseType_t uxRxedValue;
|
|
static UBaseType_t uxNextOperation = 0;
|
|
|
|
/* Called from a timer interrupt. Perform various read and write
|
|
* accesses on the queues. */
|
|
|
|
uxNextOperation++;
|
|
|
|
if( uxNextOperation & ( UBaseType_t ) 0x01 )
|
|
{
|
|
timerNORMALLY_EMPTY_TX();
|
|
timerNORMALLY_EMPTY_TX();
|
|
timerNORMALLY_EMPTY_TX();
|
|
}
|
|
else
|
|
{
|
|
timerNORMALLY_FULL_RX();
|
|
timerNORMALLY_FULL_RX();
|
|
timerNORMALLY_FULL_RX();
|
|
}
|
|
|
|
return xHigherPriorityTaskWoken;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
BaseType_t xSecondTimerHandler( void )
|
|
{
|
|
UBaseType_t uxRxedValue;
|
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
static UBaseType_t uxNextOperation = 0;
|
|
|
|
/* Called from a timer interrupt. Perform various read and write
|
|
* accesses on the queues. */
|
|
|
|
uxNextOperation++;
|
|
|
|
if( uxNextOperation & ( UBaseType_t ) 0x01 )
|
|
{
|
|
timerNORMALLY_EMPTY_TX();
|
|
timerNORMALLY_EMPTY_TX();
|
|
|
|
timerNORMALLY_EMPTY_RX();
|
|
timerNORMALLY_EMPTY_RX();
|
|
}
|
|
else
|
|
{
|
|
timerNORMALLY_FULL_RX();
|
|
timerNORMALLY_FULL_TX();
|
|
timerNORMALLY_FULL_TX();
|
|
timerNORMALLY_FULL_TX();
|
|
}
|
|
|
|
return xHigherPriorityTaskWoken;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
BaseType_t xAreIntQueueTasksStillRunning( void )
|
|
{
|
|
static UBaseType_t uxLastHighPriorityLoops1 = 0, uxLastHighPriorityLoops2 = 0, uxLastLowPriorityLoops1 = 0, uxLastLowPriorityLoops2 = 0;
|
|
|
|
/* xErrorStatus can be set outside of this function. This function just
|
|
* checks that all the tasks are still cycling. */
|
|
|
|
if( uxHighPriorityLoops1 == uxLastHighPriorityLoops1 )
|
|
{
|
|
/* The high priority 1 task has stalled. */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
uxLastHighPriorityLoops1 = uxHighPriorityLoops1;
|
|
|
|
if( uxHighPriorityLoops2 == uxLastHighPriorityLoops2 )
|
|
{
|
|
/* The high priority 2 task has stalled. */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
uxLastHighPriorityLoops2 = uxHighPriorityLoops2;
|
|
|
|
if( uxLowPriorityLoops1 == uxLastLowPriorityLoops1 )
|
|
{
|
|
/* The low priority 1 task has stalled. */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
uxLastLowPriorityLoops1 = uxLowPriorityLoops1;
|
|
|
|
if( uxLowPriorityLoops2 == uxLastLowPriorityLoops2 )
|
|
{
|
|
/* The low priority 2 task has stalled. */
|
|
prvQueueAccessLogError( __LINE__ );
|
|
}
|
|
|
|
uxLastLowPriorityLoops2 = uxLowPriorityLoops2;
|
|
|
|
return xErrorStatus;
|
|
}
|