/* * 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 * */ /* * Creates a task and a timer that operate on an interrupt driven serial port. * This demo assumes that the characters transmitted on a port will also be * received on the same port. Therefore, the UART must either be connected to * an echo server, or the uart connector must have a loopback connector fitted. * See http://www.serialporttool.com/CommEcho.htm for a suitable echo server * for Windows hosts. * * The timer sends a string to the UART, toggles an LED, then resets itself by * changing its own period. The period is calculated as a pseudo random number * between comTX_MAX_BLOCK_TIME and comTX_MIN_BLOCK_TIME. * * The task blocks on an Rx queue waiting for a character to become available. * Received characters are checked to ensure they match those transmitted by the * Tx timer. An error is latched if characters are missing, incorrect, or * arrive too slowly. * * How characters are actually transmitted and received is port specific. Demos * that include this test/demo file will provide example drivers. The Tx timer * executes in the context of the timer service (daemon) task, and must * therefore never attempt to block. * */ /* Scheduler include files. */ #include #include #include "FreeRTOS.h" #include "task.h" #include "timers.h" #ifndef configUSE_TIMERS #error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h. #endif #if configUSE_TIMERS != 1 #error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h. #endif /* Demo program include files. */ #include "serial.h" #include "comtest_strings.h" #include "partest.h" /* The size of the stack given to the Rx task. */ #define comSTACK_SIZE configMINIMAL_STACK_SIZE /* See the comment above the declaration of the uxBaseLED variable. */ #define comTX_LED_OFFSET ( 0 ) #define comRX_LED_OFFSET ( 1 ) /* The Tx timer transmits the sequence of characters at a pseudo random * interval that is capped between comTX_MAX_BLOCK_TIME and * comTX_MIN_BLOCK_TIME. */ #define comTX_MAX_BLOCK_TIME ( ( TickType_t ) 0x96 ) #define comTX_MIN_BLOCK_TIME ( ( TickType_t ) 0x32 ) #define comOFFSET_TIME ( ( TickType_t ) 3 ) /* States for the simple state machine implemented in the Rx task. */ #define comtstWAITING_START_OF_STRING 0 #define comtstWAITING_END_OF_STRING 1 /* A short delay in ticks - this delay is used to allow the Rx queue to fill up * a bit so more than one character can be processed at a time. This is relative * to comTX_MIN_BLOCK_TIME to ensure it is never longer than the shortest gap * between transmissions. It could be worked out more scientifically from the * baud rate being used. */ #define comSHORT_DELAY ( comTX_MIN_BLOCK_TIME >> ( TickType_t ) 2 ) /* The string that is transmitted and received. */ #define comTRANSACTED_STRING "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" /* A block time of 0 simply means "don't block". */ #define comtstDONT_BLOCK ( TickType_t ) 0 /* Handle to the com port used by both tasks. */ static xComPortHandle xPort = NULL; /* The callback function allocated to the transmit timer, as described in the * comments at the top of this file. */ static void prvComTxTimerCallback( TimerHandle_t xTimer ); /* The receive task as described in the comments at the top of this file. */ static void vComRxTask( void * pvParameters ); /* The Rx task will toggle LED ( uxBaseLED + comRX_LED_OFFSET). The Tx task * will toggle LED ( uxBaseLED + comTX_LED_OFFSET ). */ static UBaseType_t uxBaseLED = 0; /* The Rx task toggles uxRxLoops on each successful iteration of its defined * function - provided no errors have ever been latched. If this variable stops * incrementing, then an error has occurred. */ static volatile UBaseType_t uxRxLoops = 0UL; /* The timer used to periodically transmit the string. This is the timer that * has prvComTxTimerCallback allocated to it as its callback function. */ static TimerHandle_t xTxTimer = NULL; /* The string length is held at file scope so the Tx timer does not need to * calculate it each time it executes. */ static size_t xStringLength = 0U; /*-----------------------------------------------------------*/ void vStartComTestStringsTasks( UBaseType_t uxPriority, uint32_t ulBaudRate, UBaseType_t uxLED ) { /* Store values that are used at run time. */ uxBaseLED = uxLED; /* Calculate the string length here, rather than each time the Tx timer * executes. */ xStringLength = strlen( comTRANSACTED_STRING ); /* Include the null terminator in the string length as this is used to * detect the end of the string in the Rx task. */ xStringLength++; /* Initialise the com port, then spawn the Rx task and create the Tx * timer. */ xSerialPortInitMinimal( ulBaudRate, ( xStringLength * 2U ) ); /* Create the Rx task and the Tx timer. The timer is started from the * Rx task. */ xTaskCreate( vComRxTask, "COMRx", comSTACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL ); xTxTimer = xTimerCreate( "TxTimer", comTX_MIN_BLOCK_TIME, pdFALSE, NULL, prvComTxTimerCallback ); configASSERT( xTxTimer ); } /*-----------------------------------------------------------*/ static void prvComTxTimerCallback( TimerHandle_t xTimer ) { TickType_t xTimeToWait; /* The parameter is not used in this case. */ ( void ) xTimer; /* Send the string. How this is actually performed depends on the * sample driver provided with this demo. However - as this is a timer, * it executes in the context of the timer task and therefore must not * block. */ vSerialPutString( xPort, comTRANSACTED_STRING, xStringLength ); /* Toggle an LED to give a visible indication that another transmission * has been performed. */ vParTestToggleLED( uxBaseLED + comTX_LED_OFFSET ); /* Wait a pseudo random time before sending the string again. */ xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME; /* Ensure the time to wait is not greater than comTX_MAX_BLOCK_TIME. */ xTimeToWait %= comTX_MAX_BLOCK_TIME; /* Ensure the time to wait is not less than comTX_MIN_BLOCK_TIME. */ if( xTimeToWait < comTX_MIN_BLOCK_TIME ) { xTimeToWait = comTX_MIN_BLOCK_TIME; } /* Reset the timer to run again xTimeToWait ticks from now. This function * is called from the context of the timer task, so the block time must not * be anything other than zero. */ xTimerChangePeriod( xTxTimer, xTimeToWait, comtstDONT_BLOCK ); } /*-----------------------------------------------------------*/ static void vComRxTask( void * pvParameters ) { BaseType_t xState = comtstWAITING_START_OF_STRING, xErrorOccurred = pdFALSE; char * pcExpectedByte, cRxedChar; const xComPortHandle xPort = NULL; /* The parameter is not used in this example. */ ( void ) pvParameters; /* Start the Tx timer. This only needs to be started once, as it will * reset itself thereafter. */ xTimerStart( xTxTimer, portMAX_DELAY ); /* The first expected Rx character is the first in the string that is * transmitted. */ pcExpectedByte = comTRANSACTED_STRING; for( ; ; ) { /* Wait for the next character. */ if( xSerialGetChar( xPort, &cRxedChar, ( comTX_MAX_BLOCK_TIME * 2 ) ) == pdFALSE ) { /* A character definitely should have been received by now. As a * character was not received an error must have occurred (which might * just be that the loopback connector is not fitted). */ xErrorOccurred = pdTRUE; } switch( xState ) { case comtstWAITING_START_OF_STRING: if( cRxedChar == *pcExpectedByte ) { /* The received character was the first character of the * string. Move to the next state to check each character * as it comes in until the entire string has been received. */ xState = comtstWAITING_END_OF_STRING; pcExpectedByte++; /* Block for a short period. This just allows the Rx queue * to contain more than one character, and therefore prevent * thrashing reads to the queue, and repetitive context * switches as each character is received. */ vTaskDelay( comSHORT_DELAY ); } break; case comtstWAITING_END_OF_STRING: if( cRxedChar == *pcExpectedByte ) { /* The received character was the expected character. Was * it the last character in the string - i.e. the null * terminator? */ if( cRxedChar == 0x00 ) { /* The entire string has been received. If no errors * have been latched, then increment the loop counter to * show this task is still healthy. */ if( xErrorOccurred == pdFALSE ) { uxRxLoops++; /* Toggle an LED to give a visible sign that a * complete string has been received. */ vParTestToggleLED( uxBaseLED + comRX_LED_OFFSET ); } /* Go back to wait for the start of the next string. */ pcExpectedByte = comTRANSACTED_STRING; xState = comtstWAITING_START_OF_STRING; } else { /* Wait for the next character in the string. */ pcExpectedByte++; } } else { /* The character received was not that expected. */ xErrorOccurred = pdTRUE; } break; default: /* Should not get here. Stop the Rx loop counter from * incrementing to latch the error. */ xErrorOccurred = pdTRUE; break; } } } /*-----------------------------------------------------------*/ BaseType_t xAreComTestTasksStillRunning( void ) { BaseType_t xReturn; /* If the count of successful reception loops has not changed than at * some time an error occurred (i.e. a character was received out of sequence) * and false is returned. */ if( uxRxLoops == 0UL ) { xReturn = pdFALSE; } else { xReturn = pdTRUE; } /* Reset the count of successful Rx loops. When this function is called * again it should have been incremented again. */ uxRxLoops = 0UL; return xReturn; }