EBIKE-FreeRTOS/Common/Minimal/StreamBufferDemo.c
2024-04-14 18:38:39 +08:00

1248 lines
58 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
*
*/
/* Standard includes. */
#include "stdio.h"
#include "string.h"
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "stream_buffer.h"
/* Demo app includes. */
#include "StreamBufferDemo.h"
/* The number of bytes of storage in the stream buffers used in this test. */
#define sbSTREAM_BUFFER_LENGTH_BYTES ( ( size_t ) 30 )
/* Stream buffer length one. */
#define sbSTREAM_BUFFER_LENGTH_ONE ( ( size_t ) 1 )
/* Start and end ASCII characters used in data sent to the buffers. */
#define sbASCII_SPACE 32
#define sbASCII_TILDA 126
/* Defines the number of tasks to create in this test and demo. */
#define sbNUMBER_OF_ECHO_CLIENTS ( 2 )
#define sbNUMBER_OF_SENDER_TASKS ( 2 )
/* Priority of the test tasks. The send and receive go from low to high
* priority tasks, and from high to low priority tasks. */
#define sbLOWER_PRIORITY ( tskIDLE_PRIORITY )
#define sbHIGHER_PRIORITY ( tskIDLE_PRIORITY + 1 )
/* Block times used when sending and receiving from the stream buffers. */
#define sbRX_TX_BLOCK_TIME pdMS_TO_TICKS( 125UL )
/* A block time of 0 means "don't block". */
#define sbDONT_BLOCK ( 0 )
/* The trigger level sets the number of bytes that must be present in the
* stream buffer before a task that is blocked on the stream buffer is moved out of
* the Blocked state so it can read the bytes. */
#define sbTRIGGER_LEVEL_1 ( 1 )
/* The size of the stack allocated to the tasks that run as part of this demo/
* test. The stack size is over generous in most cases. */
#ifndef configSTREAM_BUFFER_SENDER_TASK_STACK_SIZE
#define sbSTACK_SIZE ( configMINIMAL_STACK_SIZE + ( configMINIMAL_STACK_SIZE >> 1 ) )
#else
#define sbSTACK_SIZE configSTREAM_BUFFER_SENDER_TASK_STACK_SIZE
#endif
#ifndef configSTREAM_BUFFER_SMALLER_TASK_STACK_SIZE
#define sbSMALLER_STACK_SIZE sbSTACK_SIZE
#else
#define sbSMALLER_STACK_SIZE configSTREAM_BUFFER_SMALLER_TASK_STACK_SIZE
#endif
/*-----------------------------------------------------------*/
/*
* Performs various tests that do not require multiple tasks to interact.
*/
static void prvSingleTaskTests( StreamBufferHandle_t xStreamBuffer );
/*
* Tests sending and receiving various lengths of data via a stream buffer.
* The echo client sends the data to the echo server, which then sends the
* data back to the echo client, which checks it receives exactly what it
* sent.
*/
static void prvEchoClient( void * pvParameters );
static void prvEchoServer( void * pvParameters );
/*
* Tasks that send and receive to a stream buffer at a low priority and without
* blocking, so the send and receive functions interleave in time as the tasks
* are switched in and out.
*/
static void prvNonBlockingReceiverTask( void * pvParameters );
static void prvNonBlockingSenderTask( void * pvParameters );
/* Performs an assert() like check in a way that won't get removed when
* performing a code coverage analysis. */
static void prvCheckExpectedState( BaseType_t xState );
/*
* A task that creates a stream buffer with a specific trigger level, then
* receives a string from an interrupt (the RTOS tick hook) byte by byte to
* check it is only unblocked when the specified trigger level is reached.
*/
static void prvInterruptTriggerLevelTest( void * pvParameters );
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
/* This file tests both statically and dynamically allocated stream buffers.
* Allocate the structures and buffers to be used by the statically allocated
* objects, which get used in the echo tests. */
static void prvReceiverTask( void * pvParameters );
static void prvSenderTask( void * pvParameters );
static StaticStreamBuffer_t xStaticStreamBuffers[ sbNUMBER_OF_ECHO_CLIENTS ];
static uint32_t ulSenderLoopCounters[ sbNUMBER_OF_SENDER_TASKS ] = { 0 };
#endif /* configSUPPORT_STATIC_ALLOCATION */
/* The +1 is to make the test logic easier as the function that calculates the
* free space will return one less than the actual free space - adding a 1 to the
* actual length makes it appear to the tests as if the free space is returned as
* it might logically be expected. Returning 1 less than the actual free space is
* fine as it can never result in an overrun. */
static uint8_t ucBufferStorage[ sbNUMBER_OF_SENDER_TASKS ][ sbSTREAM_BUFFER_LENGTH_BYTES + 1 ];
/*-----------------------------------------------------------*/
/* The buffers used by the echo client and server tasks. */
typedef struct ECHO_STREAM_BUFFERS
{
/* Handles to the data structures that describe the stream buffers. */
StreamBufferHandle_t xEchoClientBuffer;
StreamBufferHandle_t xEchoServerBuffer;
} EchoStreamBuffers_t;
static volatile uint32_t ulEchoLoopCounters[ sbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
/* The non-blocking tasks monitor their operation, and if no errors have been
* found, increment ulNonBlockingRxCounter. xAreStreamBufferTasksStillRunning()
* then checks ulNonBlockingRxCounter and only returns pdPASS if
* ulNonBlockingRxCounter is still incrementing. */
static volatile uint32_t ulNonBlockingRxCounter = 0;
/* The task that receives characters from the tick interrupt in order to test
* different trigger levels monitors its own behaviour. If it has not detected any
* error then it increments ulInterruptTriggerCounter to indicate to the check task
* that it is still operating correctly. */
static volatile uint32_t ulInterruptTriggerCounter = 0UL;
/* The stream buffer used from the tick interrupt. This sends one byte at a time
* to a test task to test the trigger level operation. The variable is set to NULL
* in between test runs. */
static volatile StreamBufferHandle_t xInterruptStreamBuffer = NULL;
/* The data sent from the tick interrupt to the task that tests the trigger
* level functionality. */
static const char * pcDataSentFromInterrupt = "0123456789";
/* Data that is longer than the buffer that is sent to the buffers as a stream
* of bytes. Parts of which are written to the stream buffer to test writing
* different lengths at different offsets, to many bytes, part streams, streams
* that wrap, etc.. Two messages are defined to ensure left over data is not
* accidentally read out of the buffer. */
static const char * pc55ByteString = "One two three four five six seven eight nine ten eleven";
static const char * pc54ByteString = "01234567891abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ";
/* Used to log the status of the tests contained within this file for reporting
* to a monitoring task ('check' task). */
static BaseType_t xErrorStatus = pdPASS;
/*-----------------------------------------------------------*/
void vStartStreamBufferTasks( void )
{
StreamBufferHandle_t xStreamBuffer;
/* The echo servers sets up the stream buffers before creating the echo
* client tasks. One set of tasks has the server as the higher priority, and
* the other has the client as the higher priority. */
xTaskCreate( prvEchoServer, "1StrEchoServer", sbSMALLER_STACK_SIZE, NULL, sbHIGHER_PRIORITY, NULL );
xTaskCreate( prvEchoServer, "2StrEchoServer", sbSMALLER_STACK_SIZE, NULL, sbLOWER_PRIORITY, NULL );
/* The non blocking tasks run continuously and will interleave with each
* other, so must be created at the lowest priority. The stream buffer they
* use is created and passed in using the task's parameter. */
xStreamBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
xTaskCreate( prvNonBlockingReceiverTask, "StrNonBlkRx", configMINIMAL_STACK_SIZE, ( void * ) xStreamBuffer, tskIDLE_PRIORITY, NULL );
xTaskCreate( prvNonBlockingSenderTask, "StrNonBlkTx", configMINIMAL_STACK_SIZE, ( void * ) xStreamBuffer, tskIDLE_PRIORITY, NULL );
/* The task that receives bytes from an interrupt to test that it unblocks
* at a specific trigger level must run at a high priority to minimise the risk
* of it receiving more characters before it can execute again after being
* unblocked. */
xTaskCreate( prvInterruptTriggerLevelTest, "StrTrig", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL );
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
/* The sender tasks set up the stream buffers before creating the
* receiver tasks. Priorities must be 0 and 1 as the priority is used to
* index into the xStaticStreamBuffers and ucBufferStorage arrays. */
xTaskCreate( prvSenderTask, "Str1Sender", sbSMALLER_STACK_SIZE, NULL, sbHIGHER_PRIORITY, NULL );
xTaskCreate( prvSenderTask, "Str2Sender", sbSMALLER_STACK_SIZE, NULL, sbLOWER_PRIORITY, NULL );
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
}
/*-----------------------------------------------------------*/
static void prvCheckExpectedState( BaseType_t xState )
{
configASSERT( xState );
if( xState == pdFAIL )
{
xErrorStatus = pdFAIL;
}
}
/*-----------------------------------------------------------*/
static void prvSingleTaskTests( StreamBufferHandle_t xStreamBuffer )
{
size_t xReturned, xItem, xExpected, xExpectedSpaces, xExpectedBytes;
const size_t xMax6ByteMessages = sbSTREAM_BUFFER_LENGTH_BYTES / 6;
const size_t xTrueSize = ( sizeof( ucBufferStorage ) / sbNUMBER_OF_SENDER_TASKS );
const size_t x6ByteLength = 6, x17ByteLength = 17, xFullBufferSize = sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2;
uint8_t * pucFullBuffer, * pucData, * pucReadData;
TickType_t xTimeBeforeCall, xTimeAfterCall;
const TickType_t xBlockTime = pdMS_TO_TICKS( 15 ), xAllowableMargin = pdMS_TO_TICKS( 3 ), xMinimalBlockTime = 2;
UBaseType_t uxOriginalPriority;
/* Remove warning in case configASSERT() is not defined. */
( void ) xAllowableMargin;
/* To minimise stack and heap usage a full size buffer is allocated from the
* heap, then buffers which hold smaller amounts of data are overlayed with the
* larger buffer - just make sure not to use both at once! */
pucFullBuffer = pvPortMalloc( xFullBufferSize );
configASSERT( pucFullBuffer );
pucData = pucFullBuffer;
pucReadData = pucData + x17ByteLength;
/* Nothing has been added or removed yet, so expect the free space to be
* exactly as created. Head and tail are both at 0. */
xExpectedSpaces = sbSTREAM_BUFFER_LENGTH_BYTES;
xExpectedBytes = 0;
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedSpaces );
xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedBytes );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
/* Add a single item - number of bytes available should go up by one and spaces
* available down by one. Head is in front of tail. */
xExpectedSpaces--;
xExpectedBytes++;
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
prvCheckExpectedState( xReturned == sizeof( *pucData ) );
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedSpaces );
xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedBytes );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
/* Now fill the buffer by adding another 29 bytes. Head is 30 tail is at 0. */
xExpectedSpaces -= 29;
xExpectedBytes += 29;
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, ( sbSTREAM_BUFFER_LENGTH_BYTES - 1 ), ( TickType_t ) 0 );
prvCheckExpectedState( xReturned == ( sbSTREAM_BUFFER_LENGTH_BYTES - 1 ) );
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedSpaces );
xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedBytes );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
/* Should not be able to add another byte now. */
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
prvCheckExpectedState( xReturned == ( size_t ) 0 );
/* Remove a byte so the tail pointer moves off 0. Head pointer remains at the
* end of the buffer. */
xExpectedSpaces += 1;
xExpectedBytes -= 1;
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
prvCheckExpectedState( xReturned == sizeof( *pucData ) );
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedSpaces );
xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedBytes );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
/* Should be able to add another byte to fill the buffer again now. */
xExpectedSpaces -= 1;
xExpectedBytes += 1;
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
prvCheckExpectedState( xReturned == sizeof( *pucData ) );
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedSpaces );
xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedBytes );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
/* Now the head pointer is behind the tail pointer. Read another 29 bytes so
* the tail pointer moves to the end of the buffer. */
xExpectedSpaces += 29;
xExpectedBytes -= 29;
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucData, ( size_t ) 29, ( TickType_t ) 0 );
prvCheckExpectedState( xReturned == ( size_t ) 29 );
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedSpaces );
xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedBytes );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
/* Read out one more byte to wrap the tail back around to the start, to get back
* to where we started. */
xExpectedSpaces += 1;
xExpectedBytes -= 1;
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucData, sizeof( *pucData ), ( TickType_t ) 0 );
prvCheckExpectedState( xReturned == sizeof( *pucData ) );
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedSpaces );
xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedBytes );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
/* Try filling the message buffer in one write, blocking indefinitely. Expect to
* have written one byte less. */
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, xTrueSize, portMAX_DELAY );
xExpectedSpaces = ( size_t ) 0;
prvCheckExpectedState( xReturned == ( xTrueSize - ( size_t ) 1 ) );
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == xExpectedSpaces );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
/* Empty the buffer again ready for the rest of the tests. Again block
* indefinitely to ensure reading more than there can possible be won't lock this
* task up, so expect to actually receive one byte less than requested. */
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucData, xTrueSize, portMAX_DELAY );
prvCheckExpectedState( xReturned == ( xTrueSize - ( size_t ) 1 ) );
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == sbSTREAM_BUFFER_LENGTH_BYTES );
xExpected = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == ( size_t ) 0 );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
/* The buffer is 30 bytes long. 6 5 byte messages should fit before the
* buffer is completely full. */
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
for( xItem = 0; xItem < xMax6ByteMessages; xItem++ )
{
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
/* Generate recognizable data to write to the buffer. This is just
* ascii characters that shows which loop iteration the data was written
* in. The 'FromISR' version is used to give it some exercise as a block
* time is not used, so the call must be inside a critical section so it
* runs with ports that don't support interrupt nesting (and therefore
* don't have interrupt safe critical sections). */
memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x6ByteLength );
taskENTER_CRITICAL();
{
xReturned = xStreamBufferSendFromISR( xStreamBuffer, ( void * ) pucData, x6ByteLength, NULL );
}
taskEXIT_CRITICAL();
prvCheckExpectedState( xReturned == x6ByteLength );
/* The space in the buffer will have reduced by the amount of user data
* written into the buffer. */
xExpected -= x6ByteLength;
xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xReturned == xExpected );
xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
/* +1 as it is zero indexed. */
prvCheckExpectedState( xReturned == ( ( xItem + 1 ) * x6ByteLength ) );
}
/* Now the buffer should be full, and attempting to add anything should fail. */
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( pucData[ 0 ] ), sbDONT_BLOCK );
prvCheckExpectedState( xReturned == 0 );
/* Adding with a timeout should also fail after the appropriate time. The
* priority is temporarily boosted in this part of the test to keep the
* allowable margin to a minimum. */
uxOriginalPriority = uxTaskPriorityGet( NULL );
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
xTimeBeforeCall = xTaskGetTickCount();
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, sizeof( pucData[ 0 ] ), xBlockTime );
xTimeAfterCall = xTaskGetTickCount();
vTaskPrioritySet( NULL, uxOriginalPriority );
prvCheckExpectedState( ( ( TickType_t ) ( xTimeAfterCall - xTimeBeforeCall ) ) >= xBlockTime );
prvCheckExpectedState( ( ( TickType_t ) ( xTimeAfterCall - xTimeBeforeCall ) ) < ( xBlockTime + xAllowableMargin ) );
prvCheckExpectedState( xReturned == 0 );
/* The buffer is now full of data in the form "000000", "111111", etc. Make
* sure the data is read out as expected. */
for( xItem = 0; xItem < xMax6ByteMessages; xItem++ )
{
/* Generate the data that is expected to be read out for this loop
* iteration. */
memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x6ByteLength );
/* Read the next 6 bytes out. The 'FromISR' version is used to give it
* some exercise as a block time is not used, so a it must be called from
* a critical section so this will work on ports that don't support
* interrupt nesting (so don't have interrupt safe critical sections). */
taskENTER_CRITICAL();
{
xReturned = xStreamBufferReceiveFromISR( xStreamBuffer, ( void * ) pucReadData, x6ByteLength, NULL );
}
taskEXIT_CRITICAL();
prvCheckExpectedState( xReturned == x6ByteLength );
/* Does the data read out match that expected? */
prvCheckExpectedState( memcmp( ( void * ) pucData, ( void * ) pucReadData, x6ByteLength ) == 0 );
/* The space in the buffer will have increased by the amount of user
* data removed from the buffer. */
xExpected += x6ByteLength;
xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xReturned == xExpected );
xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xReturned == ( sbSTREAM_BUFFER_LENGTH_BYTES - xExpected ) );
}
/* The buffer should be empty again. */
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
xExpected = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xExpected == sbSTREAM_BUFFER_LENGTH_BYTES );
/* Reading with a timeout should also fail after the appropriate time. The
* priority is temporarily boosted in this part of the test to keep the
* allowable margin to a minimum. */
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
xTimeBeforeCall = xTaskGetTickCount();
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucReadData, x6ByteLength, xBlockTime );
xTimeAfterCall = xTaskGetTickCount();
vTaskPrioritySet( NULL, uxOriginalPriority );
prvCheckExpectedState( ( ( TickType_t ) ( xTimeAfterCall - xTimeBeforeCall ) ) >= xBlockTime );
prvCheckExpectedState( ( ( TickType_t ) ( xTimeAfterCall - xTimeBeforeCall ) ) < ( xBlockTime + xAllowableMargin ) );
prvCheckExpectedState( xReturned == 0 );
/* In the next loop 17 bytes are written to then read out on each
* iteration. As 30 is not divisible by 17 the data will wrap around. */
xExpected = sbSTREAM_BUFFER_LENGTH_BYTES - x17ByteLength;
for( xItem = 0; xItem < 100; xItem++ )
{
/* Generate recognizable data to write to the queue. This is just
* ascii characters that shows which loop iteration the data was written
* in. */
memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x17ByteLength );
xReturned = xStreamBufferSend( xStreamBuffer, ( void * ) pucData, x17ByteLength, sbDONT_BLOCK );
prvCheckExpectedState( xReturned == x17ByteLength );
/* The space in the buffer will have reduced by the amount of user data
* written into the buffer. */
xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xReturned == xExpected );
xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xReturned == x17ByteLength );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
/* Read the 17 bytes out again. */
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucReadData, x17ByteLength, sbDONT_BLOCK );
prvCheckExpectedState( xReturned == x17ByteLength );
/* Does the data read out match that expected? */
prvCheckExpectedState( memcmp( ( void * ) pucData, ( void * ) pucReadData, x17ByteLength ) == 0 );
/* Full buffer space available again. */
xReturned = xStreamBufferSpacesAvailable( xStreamBuffer );
prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
xReturned = xStreamBufferBytesAvailable( xStreamBuffer );
prvCheckExpectedState( xReturned == 0 );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
}
/* Fill the buffer with one message, check it is full, then read it back
* again and check the correct data is received. */
xStreamBufferSend( xStreamBuffer, ( const void * ) pc55ByteString, sbSTREAM_BUFFER_LENGTH_BYTES, sbDONT_BLOCK );
xStreamBufferReceive( xStreamBuffer, ( void * ) pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES, sbDONT_BLOCK );
prvCheckExpectedState( memcmp( pc55ByteString, pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES ) == 0 );
/* Fill the buffer one bytes at a time. */
for( xItem = 0; xItem < sbSTREAM_BUFFER_LENGTH_BYTES; xItem++ )
{
/* Block time is only for test coverage, the task should never actually
* block here. */
xStreamBufferSend( xStreamBuffer, ( const void * ) &( pc54ByteString[ xItem ] ), sizeof( char ), sbRX_TX_BLOCK_TIME );
}
/* The buffer should now be full. */
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
/* Read the message out in one go, even though it was written in individual
* bytes. Try reading much more data than is actually available to ensure only
* the available bytes are returned (otherwise this read will write outside of
* the memory allocated anyway!). */
xReturned = xStreamBufferReceive( xStreamBuffer, pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2, sbRX_TX_BLOCK_TIME );
prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
prvCheckExpectedState( memcmp( ( const void * ) pc54ByteString, ( const void * ) pucFullBuffer, sbSTREAM_BUFFER_LENGTH_BYTES ) == 0 );
/* Now do the opposite, write in one go and read out in single bytes. */
xReturned = xStreamBufferSend( xStreamBuffer, ( const void * ) pc55ByteString, sbSTREAM_BUFFER_LENGTH_BYTES, sbRX_TX_BLOCK_TIME );
prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferBytesAvailable( xStreamBuffer ) == sbSTREAM_BUFFER_LENGTH_BYTES );
prvCheckExpectedState( xStreamBufferSpacesAvailable( xStreamBuffer ) == 0 );
/* Read from the buffer one byte at a time. */
for( xItem = 0; xItem < sbSTREAM_BUFFER_LENGTH_BYTES; xItem++ )
{
/* Block time is only for test coverage, the task should never actually
* block here. */
xStreamBufferReceive( xStreamBuffer, ( void * ) pucFullBuffer, sizeof( char ), sbRX_TX_BLOCK_TIME );
prvCheckExpectedState( pc55ByteString[ xItem ] == pucFullBuffer[ 0 ] );
}
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
/* Try writing more bytes than there is space. */
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
xReturned = xStreamBufferSend( xStreamBuffer, ( const void * ) pc54ByteString, sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2, xMinimalBlockTime );
vTaskPrioritySet( NULL, uxOriginalPriority );
prvCheckExpectedState( xReturned == sbSTREAM_BUFFER_LENGTH_BYTES );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdTRUE );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdFALSE );
/* No space now though. */
xReturned = xStreamBufferSend( xStreamBuffer, ( const void * ) pc54ByteString, sbSTREAM_BUFFER_LENGTH_BYTES * ( size_t ) 2, xMinimalBlockTime );
prvCheckExpectedState( xReturned == 0 );
/* Ensure data was written as expected even when there was an attempt to
* write more than was available. This also tries to read more bytes than are
* available. */
xReturned = xStreamBufferReceive( xStreamBuffer, ( void * ) pucFullBuffer, xFullBufferSize, xMinimalBlockTime );
prvCheckExpectedState( memcmp( ( const void * ) pucFullBuffer, ( const void * ) pc54ByteString, sbSTREAM_BUFFER_LENGTH_BYTES ) == 0 );
prvCheckExpectedState( xStreamBufferIsFull( xStreamBuffer ) == pdFALSE );
prvCheckExpectedState( xStreamBufferIsEmpty( xStreamBuffer ) == pdTRUE );
/* Clean up with data in the buffer to ensure the tests that follow don't
* see the data (the data should be discarded). */
( void ) xStreamBufferSend( xStreamBuffer, ( const void * ) pc55ByteString, sbSTREAM_BUFFER_LENGTH_BYTES / ( size_t ) 2, sbDONT_BLOCK );
vPortFree( pucFullBuffer );
xStreamBufferReset( xStreamBuffer );
}
/*-----------------------------------------------------------*/
static void prvNonBlockingSenderTask( void * pvParameters )
{
StreamBufferHandle_t xStreamBuffer;
size_t xNextChar = 0, xBytesToSend, xBytesActuallySent;
const size_t xStringLength = strlen( pc54ByteString );
/* In this case the stream buffer has already been created and is passed
* into the task using the task's parameter. */
xStreamBuffer = ( StreamBufferHandle_t ) pvParameters;
/* Keep sending the string to the stream buffer as many bytes as possible in
* each go. Doesn't block so calls can interleave with the non-blocking
* receives performed by prvNonBlockingReceiverTask(). */
for( ; ; )
{
/* The whole string cannot be sent at once, so xNextChar is an index to
* the position within the string that has been sent so far. How many
* bytes are there left to send before the end of the string? */
xBytesToSend = xStringLength - xNextChar;
/* Attempt to send right up to the end of the string. */
xBytesActuallySent = xStreamBufferSend( xStreamBuffer, ( const void * ) &( pc54ByteString[ xNextChar ] ), xBytesToSend, sbDONT_BLOCK );
prvCheckExpectedState( xBytesActuallySent <= xBytesToSend );
/* Move the index up the string to the next character to be sent,
* wrapping if the end of the string has been reached. */
xNextChar += xBytesActuallySent;
prvCheckExpectedState( xNextChar <= xStringLength );
if( xNextChar == xStringLength )
{
xNextChar = 0;
}
}
}
/*-----------------------------------------------------------*/
static void prvNonBlockingReceiverTask( void * pvParameters )
{
StreamBufferHandle_t xStreamBuffer;
size_t xNextChar = 0, xReceiveLength, xBytesToTest, xStartIndex;
const size_t xStringLength = strlen( pc54ByteString );
char cRxString[ 12 ]; /* Holds received characters. */
BaseType_t xNonBlockingReceiveError = pdFALSE;
/* In this case the stream buffer has already been created and is passed
* into the task using the task's parameter. */
xStreamBuffer = ( StreamBufferHandle_t ) pvParameters;
/* Expects to receive the pc54ByteString over and over again. Sends and
* receives are not blocking so will interleave. */
for( ; ; )
{
/* Attempt to receive as many bytes as possible, up to the limit of the
* Rx buffer size. */
xReceiveLength = xStreamBufferReceive( xStreamBuffer, ( void * ) cRxString, sizeof( cRxString ), sbDONT_BLOCK );
if( xReceiveLength > 0 )
{
/* xNextChar is the index into pc54ByteString that has been received
* already. If xReceiveLength bytes are added to that, will it go off
* the end of the string? If so, then first test up to the end of the
* string, then go back to the start of pc54ByteString to test the
* remains of the received data. */
xBytesToTest = xReceiveLength;
if( ( xNextChar + xBytesToTest ) > xStringLength )
{
/* Cap to test the received data to the end of the string. */
xBytesToTest = xStringLength - xNextChar;
if( memcmp( ( const void * ) &( pc54ByteString[ xNextChar ] ), ( const void * ) cRxString, xBytesToTest ) != 0 )
{
xNonBlockingReceiveError = pdTRUE;
}
/* Then move back to the start of the string to test the
* remaining received bytes. */
xNextChar = 0;
xStartIndex = xBytesToTest;
xBytesToTest = xReceiveLength - xBytesToTest;
}
else
{
/* The string didn't wrap in the buffer, so start comparing from
* the start of the received data. */
xStartIndex = 0;
}
/* Test the received bytes are as expected, then move the index
* along the string to the next expected char to receive. */
if( memcmp( ( const void * ) &( pc54ByteString[ xNextChar ] ), ( const void * ) &( cRxString[ xStartIndex ] ), xBytesToTest ) != 0 )
{
xNonBlockingReceiveError = pdTRUE;
}
if( xNonBlockingReceiveError == pdFALSE )
{
/* No errors detected so increment the counter that lets the
* check task know this test is still functioning correctly. */
ulNonBlockingRxCounter++;
}
xNextChar += xBytesToTest;
if( xNextChar >= xStringLength )
{
xNextChar = 0;
}
}
}
}
/*-----------------------------------------------------------*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
static void prvSenderTask( void * pvParameters )
{
StreamBufferHandle_t xStreamBuffer, xTempStreamBuffer;
static uint8_t ucTempBuffer[ 10 ]; /* Just used to exercise stream buffer creating and deletion. */
const TickType_t xTicksToWait = sbRX_TX_BLOCK_TIME, xShortDelay = pdMS_TO_TICKS( 50 );
StaticStreamBuffer_t xStaticStreamBuffer;
size_t xNextChar = 0, xBytesToSend, xBytesActuallySent;
const size_t xStringLength = strlen( pc55ByteString );
/* The task's priority is used as an index into the loop counters used to
* indicate this task is still running. */
UBaseType_t uxIndex = uxTaskPriorityGet( NULL );
/* Make sure a change in priority does not inadvertently result in an
* invalid array index. */
prvCheckExpectedState( uxIndex < sbNUMBER_OF_ECHO_CLIENTS );
/* Avoid compiler warnings about unused parameters. */
( void ) pvParameters;
xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucBufferStorage ) / sbNUMBER_OF_SENDER_TASKS, /* The number of bytes in each buffer in the array. */
sbTRIGGER_LEVEL_1, /* The number of bytes to be in the buffer before a task blocked to wait for data is unblocked. */
&( ucBufferStorage[ uxIndex ][ 0 ] ), /* The address of the buffer to use within the array. */
&( xStaticStreamBuffers[ uxIndex ] ) ); /* The static stream buffer structure to use within the array. */
/* Now the stream buffer has been created the receiver task can be
* created. If this sender task has the higher priority then the receiver
* task is created at the lower priority - if this sender task has the
* lower priority then the receiver task is created at the higher
* priority. */
if( uxTaskPriorityGet( NULL ) == sbLOWER_PRIORITY )
{
/* Here prvSingleTaskTests() performs various tests on a stream buffer
* that was created statically. */
prvSingleTaskTests( xStreamBuffer );
xTaskCreate( prvReceiverTask, "StrReceiver", sbSMALLER_STACK_SIZE, ( void * ) xStreamBuffer, sbHIGHER_PRIORITY, NULL );
}
else
{
xTaskCreate( prvReceiverTask, "StrReceiver", sbSMALLER_STACK_SIZE, ( void * ) xStreamBuffer, sbLOWER_PRIORITY, NULL );
}
for( ; ; )
{
/* The whole string cannot be sent at once, so xNextChar is an index
* to the position within the string that has been sent so far. How
* many bytes are there left to send before the end of the string? */
xBytesToSend = xStringLength - xNextChar;
/* Attempt to send right up to the end of the string. */
xBytesActuallySent = xStreamBufferSend( xStreamBuffer, ( const void * ) &( pc55ByteString[ xNextChar ] ), xBytesToSend, xTicksToWait );
prvCheckExpectedState( xBytesActuallySent <= xBytesToSend );
/* Move the index up the string to the next character to be sent,
* wrapping if the end of the string has been reached. */
xNextChar += xBytesActuallySent;
prvCheckExpectedState( xNextChar <= xStringLength );
if( xNextChar == xStringLength )
{
xNextChar = 0;
}
/* Increment a loop counter so a check task can tell this task is
* still running as expected. */
ulSenderLoopCounters[ uxIndex ]++;
if( uxTaskPriorityGet( NULL ) == sbHIGHER_PRIORITY )
{
/* Allow other tasks to run. */
vTaskDelay( xShortDelay );
}
/* This stream buffer is just created and deleted to ensure no
* issues when attempting to delete a stream buffer that was
* created using statically allocated memory. To save stack space
* the buffer is set to point to the pc55ByteString, which is a const
* string, but no data is written into the buffer so any valid address
* will do. */
xTempStreamBuffer = xStreamBufferCreateStatic( sizeof( ucTempBuffer ), sbTRIGGER_LEVEL_1, ucTempBuffer, &xStaticStreamBuffer );
xStreamBufferReset( xTempStreamBuffer );
vStreamBufferDelete( xTempStreamBuffer );
}
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
static void prvReceiverTask( void * pvParameters )
{
StreamBufferHandle_t const pxStreamBuffer = ( StreamBufferHandle_t ) pvParameters;
char cRxString[ 12 ]; /* Large enough to hold a 32-bit number in ASCII. */
const TickType_t xTicksToWait = pdMS_TO_TICKS( 5UL );
const size_t xStringLength = strlen( pc55ByteString );
size_t xNextChar = 0, xReceivedLength, xBytesToReceive;
for( ; ; )
{
/* Attempt to receive the number of bytes to the end of the string,
* or the number of byte that can be placed into the rx buffer,
* whichever is smallest. */
xBytesToReceive = configMIN( ( xStringLength - xNextChar ), sizeof( cRxString ) );
do
{
xReceivedLength = xStreamBufferReceive( pxStreamBuffer, ( void * ) cRxString, xBytesToReceive, xTicksToWait );
} while( xReceivedLength == 0 );
/* Ensure the received string matches the expected string. */
prvCheckExpectedState( memcmp( ( void * ) cRxString, ( const void * ) &( pc55ByteString[ xNextChar ] ), xReceivedLength ) == 0 );
/* Move the index into the string up to the end of the bytes
* received so far - wrapping if the end of the string has been
* reached. */
xNextChar += xReceivedLength;
if( xNextChar >= xStringLength )
{
xNextChar = 0;
}
}
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/
static void prvEchoClient( void * pvParameters )
{
size_t xSendLength = 0, ux;
char * pcStringToSend, * pcStringReceived, cNextChar = sbASCII_SPACE;
const TickType_t xTicksToWait = pdMS_TO_TICKS( 50 );
StreamBufferHandle_t xTempStreamBuffer;
/* The task's priority is used as an index into the loop counters used to
* indicate this task is still running. */
UBaseType_t uxIndex = uxTaskPriorityGet( NULL );
/* Pointers to the client and server stream buffers are passed into this task
* using the task's parameter. */
EchoStreamBuffers_t * pxStreamBuffers = ( EchoStreamBuffers_t * ) pvParameters;
/* Prevent compiler warnings. */
( void ) pvParameters;
/* Create the buffer into which strings to send to the server will be
* created, and the buffer into which strings echoed back from the server will
* be copied. */
pcStringToSend = ( char * ) pvPortMalloc( sbSTREAM_BUFFER_LENGTH_BYTES );
pcStringReceived = ( char * ) pvPortMalloc( sbSTREAM_BUFFER_LENGTH_BYTES );
configASSERT( pcStringToSend );
configASSERT( pcStringReceived );
for( ; ; )
{
/* Generate the length of the next string to send. */
xSendLength++;
/* The stream buffer is being used to hold variable length data, so
* each data item requires sizeof( size_t ) bytes to hold the data's
* length, hence the sizeof() in the if() condition below. */
if( xSendLength > ( sbSTREAM_BUFFER_LENGTH_BYTES - sizeof( size_t ) ) )
{
/* Back to a string length of 1. */
xSendLength = sizeof( char );
}
memset( pcStringToSend, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
for( ux = 0; ux < xSendLength; ux++ )
{
pcStringToSend[ ux ] = cNextChar;
cNextChar++;
if( cNextChar > sbASCII_TILDA )
{
cNextChar = sbASCII_SPACE;
}
}
/* Send the generated string to the buffer. */
do
{
ux = xStreamBufferSend( pxStreamBuffers->xEchoClientBuffer, ( void * ) pcStringToSend, xSendLength, xTicksToWait );
} while( ux == 0 );
/* Wait for the string to be echoed back. */
memset( pcStringReceived, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
xStreamBufferReceive( pxStreamBuffers->xEchoServerBuffer, ( void * ) pcStringReceived, xSendLength, portMAX_DELAY );
prvCheckExpectedState( strcmp( pcStringToSend, pcStringReceived ) == 0 );
/* Maintain a count of the number of times this code executes so a
* check task can determine if this task is still functioning as
* expected or not. As there are two client tasks, and the priorities
* used are 0 and 1, the task's priority is used as an index into the
* loop count array. */
ulEchoLoopCounters[ uxIndex ]++;
/* This stream buffer is just created and deleted to ensure no memory
* leaks. */
xTempStreamBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
vStreamBufferDelete( xTempStreamBuffer );
/* The following are tests for a stream buffer of size one. */
/* Create a buffer of size one. */
xTempStreamBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_ONE, sbTRIGGER_LEVEL_1 );
/* Ensure that the buffer was created successfully. */
configASSERT( xTempStreamBuffer );
/* Send one byte to the buffer. */
ux = xStreamBufferSend( xTempStreamBuffer, ( void * ) pcStringToSend, ( size_t ) 1, sbDONT_BLOCK );
/* Ensure that the byte was sent successfully. */
configASSERT( ux == 1 );
/* Try sending another byte to the buffer. */
ux = xStreamBufferSend( xTempStreamBuffer, ( void * ) pcStringToSend, ( size_t ) 1, sbDONT_BLOCK );
/* Make sure that send failed as the buffer is full. */
configASSERT( ux == 0 );
/* Receive one byte from the buffer. */
memset( pcStringReceived, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
ux = xStreamBufferReceive( xTempStreamBuffer, ( void * ) pcStringReceived, ( size_t ) 1, sbDONT_BLOCK );
/* Ensure that the receive was successful. */
configASSERT( ux == 1 );
/* Ensure that the correct data was received. */
configASSERT( pcStringToSend[ 0 ] == pcStringReceived[ 0 ] );
/* Try receiving another byte from the buffer. */
ux = xStreamBufferReceive( xTempStreamBuffer, ( void * ) pcStringReceived, ( size_t ) 1, sbDONT_BLOCK );
/* Ensure that the receive failed as the buffer is empty. */
configASSERT( ux == 0 );
/* Try sending two bytes to the buffer. Since the size of the
* buffer is one, we must not be able to send more than one. */
ux = xStreamBufferSend( xTempStreamBuffer, ( void * ) pcStringToSend, ( size_t ) 2, sbDONT_BLOCK );
/* Ensure that only one byte was sent. */
configASSERT( ux == 1 );
/* Try receiving two bytes from the buffer. Since the size of the
* buffer is one, we must not be able to get more than one. */
memset( pcStringReceived, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
ux = xStreamBufferReceive( xTempStreamBuffer, ( void * ) pcStringReceived, ( size_t ) 2, sbDONT_BLOCK );
/* Ensure that only one byte was received. */
configASSERT( ux == 1 );
/* Ensure that the correct data was received. */
configASSERT( pcStringToSend[ 0 ] == pcStringReceived[ 0 ] );
/* Delete the buffer. */
vStreamBufferDelete( xTempStreamBuffer );
}
}
/*-----------------------------------------------------------*/
static void prvEchoServer( void * pvParameters )
{
size_t xReceivedLength;
char * pcReceivedString;
EchoStreamBuffers_t xStreamBuffers;
TickType_t xTimeOnEntering;
const TickType_t xTicksToBlock = pdMS_TO_TICKS( 350UL );
/* Prevent compiler warnings about unused parameters. */
( void ) pvParameters;
/* Create the stream buffer used to send data from the client to the server,
* and the stream buffer used to echo the data from the server back to the
* client. */
xStreamBuffers.xEchoClientBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
xStreamBuffers.xEchoServerBuffer = xStreamBufferCreate( sbSTREAM_BUFFER_LENGTH_BYTES, sbTRIGGER_LEVEL_1 );
configASSERT( xStreamBuffers.xEchoClientBuffer );
configASSERT( xStreamBuffers.xEchoServerBuffer );
/* Create the buffer into which received strings will be copied. */
pcReceivedString = ( char * ) pvPortMalloc( sbSTREAM_BUFFER_LENGTH_BYTES );
configASSERT( pcReceivedString );
/* Don't expect to receive anything yet! */
xTimeOnEntering = xTaskGetTickCount();
xReceivedLength = xStreamBufferReceive( xStreamBuffers.xEchoClientBuffer, ( void * ) pcReceivedString, sbSTREAM_BUFFER_LENGTH_BYTES, xTicksToBlock );
prvCheckExpectedState( ( ( TickType_t ) ( xTaskGetTickCount() - xTimeOnEntering ) ) >= xTicksToBlock );
prvCheckExpectedState( xReceivedLength == 0 );
/* Now the stream buffers have been created the echo client task can be
* created. If this server task has the higher priority then the client task
* is created at the lower priority - if this server task has the lower
* priority then the client task is created at the higher priority. */
if( uxTaskPriorityGet( NULL ) == sbLOWER_PRIORITY )
{
xTaskCreate( prvEchoClient, "EchoClient", sbSMALLER_STACK_SIZE, ( void * ) &xStreamBuffers, sbHIGHER_PRIORITY, NULL );
}
else
{
/* Here prvSingleTaskTests() performs various tests on a stream buffer
* that was created dynamically. */
prvSingleTaskTests( xStreamBuffers.xEchoClientBuffer );
xTaskCreate( prvEchoClient, "EchoClient", sbSMALLER_STACK_SIZE, ( void * ) &xStreamBuffers, sbLOWER_PRIORITY, NULL );
}
for( ; ; )
{
memset( pcReceivedString, 0x00, sbSTREAM_BUFFER_LENGTH_BYTES );
/* Has any data been sent by the client? */
xReceivedLength = xStreamBufferReceive( xStreamBuffers.xEchoClientBuffer, ( void * ) pcReceivedString, sbSTREAM_BUFFER_LENGTH_BYTES, portMAX_DELAY );
/* Should always receive data as max delay was used. */
prvCheckExpectedState( xReceivedLength > 0 );
/* Echo the received data back to the client. */
xStreamBufferSend( xStreamBuffers.xEchoServerBuffer, ( void * ) pcReceivedString, xReceivedLength, portMAX_DELAY );
}
}
/*-----------------------------------------------------------*/
void vPeriodicStreamBufferProcessing( void )
{
static size_t xNextChar = 0;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* Called from the tick interrupt hook. If the global stream buffer
* variable is not NULL then the prvInterruptTriggerTest() task expects a byte
* to be sent to the stream buffer on each tick interrupt. */
if( xInterruptStreamBuffer != NULL )
{
/* One character from the pcDataSentFromInterrupt string is sent on each
* interrupt. The task blocked on the stream buffer should not be
* unblocked until the defined trigger level is hit. */
xStreamBufferSendFromISR( xInterruptStreamBuffer, ( const void * ) &( pcDataSentFromInterrupt[ xNextChar ] ), sizeof( char ), &xHigherPriorityTaskWoken );
if( xNextChar < strlen( pcDataSentFromInterrupt ) )
{
xNextChar++;
}
}
else
{
/* Start at the beginning of the string being sent again. */
xNextChar = 0;
}
}
/*-----------------------------------------------------------*/
static void prvInterruptTriggerLevelTest( void * pvParameters )
{
StreamBufferHandle_t xStreamBuffer;
size_t xTriggerLevel = 1, xBytesReceived;
const size_t xStreamBufferSizeBytes = ( size_t ) 9, xMaxTriggerLevel = ( size_t ) 7, xMinTriggerLevel = ( size_t ) 2;
const TickType_t xReadBlockTime = 5, xCycleBlockTime = pdMS_TO_TICKS( 100 );
uint8_t ucRxData[ 9 ];
BaseType_t xErrorDetected = pdFALSE;
#ifndef configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN
const size_t xAllowableMargin = ( size_t ) 0;
#else
const size_t xAllowableMargin = ( size_t ) configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN;
#endif
/* Remove compiler warning about unused parameter. */
( void ) pvParameters;
for( ; ; )
{
for( xTriggerLevel = xMinTriggerLevel; xTriggerLevel < xMaxTriggerLevel; xTriggerLevel++ )
{
/* This test is very time sensitive so delay at the beginning to ensure
* the rest of the system is up and running before starting. Delay between
* each loop to ensure the interrupt that sends to the stream buffer
* detects it needs to start sending from the start of the string again.. */
vTaskDelay( xCycleBlockTime );
/* Create the stream buffer that will be used from inside the tick
* interrupt. */
memset( ucRxData, 0x00, sizeof( ucRxData ) );
xStreamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel );
configASSERT( xStreamBuffer );
/* Now the stream buffer has been created it can be assigned to the
* file scope variable, which will allow the tick interrupt to start
* using it. */
taskENTER_CRITICAL();
{
xInterruptStreamBuffer = xStreamBuffer;
}
taskEXIT_CRITICAL();
xBytesReceived = xStreamBufferReceive( xStreamBuffer, ( void * ) ucRxData, sizeof( ucRxData ), xReadBlockTime );
/* Set the file scope variable back to NULL so the interrupt doesn't
* try to use it again. */
taskENTER_CRITICAL();
{
xInterruptStreamBuffer = NULL;
}
taskEXIT_CRITICAL();
/* Now check the number of bytes received equals the trigger level,
* except in the case that the read timed out before the trigger level
* was reached. */
if( xTriggerLevel > xReadBlockTime )
{
/* Trigger level was greater than the block time so expect to
* time out having received xReadBlockTime bytes. */
if( xBytesReceived > xReadBlockTime )
{
/* Received more bytes than expected. That could happen if
* this task unblocked at the right time, but an interrupt
* added another byte to the stream buffer before this task was
* able to run. */
if( ( xBytesReceived - xReadBlockTime ) > xAllowableMargin )
{
xErrorDetected = pdTRUE;
}
}
else if( xReadBlockTime != xBytesReceived )
{
/* It is possible the interrupt placed an item in the stream
* buffer before this task called xStreamBufferReceive(), but
* if that is the case then xBytesReceived will only every be
* 0 as the interrupt will only have executed once. */
if( xBytesReceived != 1 )
{
xErrorDetected = pdTRUE;
}
}
}
else if( xTriggerLevel < xReadBlockTime )
{
/* Trigger level was less than the block time so we expect to
* have received the trigger level number of bytes - could be more
* though depending on other activity between the task being
* unblocked and the task reading the number of bytes received. It
* could also be less if the interrupt already put something in the
* stream buffer before this task attempted to read it - in which
* case the task would have returned the available bytes immediately
* without ever blocking - in that case the bytes received will
* only ever be 1 as the interrupt would not have executed more
* than one in that time unless this task has too low a priority. */
if( xBytesReceived < xTriggerLevel )
{
if( xBytesReceived != 1 )
{
xErrorDetected = pdTRUE;
}
}
else if( ( xBytesReceived - xTriggerLevel ) > xAllowableMargin )
{
xErrorDetected = pdTRUE;
}
}
else
{
/* The trigger level equalled the block time, so expect to
* receive no greater than the block time. It could also be less
* if the interrupt already put something in the stream buffer
* before this task attempted to read it - in which case the task
* would have returned the available bytes immediately without ever
* blocking - in that case the bytes received would only ever be 1
* because the interrupt is not going to execute twice in that time
* unless this task is running a too low a priority. */
if( xBytesReceived < xReadBlockTime )
{
if( xBytesReceived != 1 )
{
xErrorDetected = pdTRUE;
}
}
else if( ( xBytesReceived - xReadBlockTime ) > xAllowableMargin )
{
xErrorDetected = pdTRUE;
}
}
if( xBytesReceived > sizeof( ucRxData ) )
{
xErrorDetected = pdTRUE;
}
else if( memcmp( ( void * ) ucRxData, ( const void * ) pcDataSentFromInterrupt, xBytesReceived ) != 0 )
{
/* Received data didn't match that expected. */
xErrorDetected = pdTRUE;
}
if( xErrorDetected == pdFALSE )
{
/* Increment the cycle counter so the 'check' task knows this test
* is still running without error. */
ulInterruptTriggerCounter++;
}
/* Tidy up ready for the next loop. */
vStreamBufferDelete( xStreamBuffer );
}
}
}
/*-----------------------------------------------------------*/
BaseType_t xAreStreamBufferTasksStillRunning( void )
{
static uint32_t ulLastEchoLoopCounters[ sbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
static uint32_t ulLastNonBlockingRxCounter = 0;
static uint32_t ulLastInterruptTriggerCounter = 0;
BaseType_t x;
for( x = 0; x < sbNUMBER_OF_ECHO_CLIENTS; x++ )
{
if( ulLastEchoLoopCounters[ x ] == ulEchoLoopCounters[ x ] )
{
xErrorStatus = pdFAIL;
}
else
{
ulLastEchoLoopCounters[ x ] = ulEchoLoopCounters[ x ];
}
}
if( ulNonBlockingRxCounter == ulLastNonBlockingRxCounter )
{
xErrorStatus = pdFAIL;
}
else
{
ulLastNonBlockingRxCounter = ulNonBlockingRxCounter;
}
if( ulLastInterruptTriggerCounter == ulInterruptTriggerCounter )
{
xErrorStatus = pdFAIL;
}
else
{
ulLastInterruptTriggerCounter = ulInterruptTriggerCounter;
}
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
static uint32_t ulLastSenderLoopCounters[ sbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
for( x = 0; x < sbNUMBER_OF_SENDER_TASKS; x++ )
{
if( ulLastSenderLoopCounters[ x ] == ulSenderLoopCounters[ x ] )
{
xErrorStatus = pdFAIL;
}
else
{
ulLastSenderLoopCounters[ x ] = ulSenderLoopCounters[ x ];
}
}
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
return xErrorStatus;
}
/*-----------------------------------------------------------*/