3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 * https://www.FreeRTOS.org
23 * https://github.com/FreeRTOS
29 * Creates a task and a timer that operate on an interrupt driven serial port.
30 * This demo assumes that the characters transmitted on a port will also be
31 * received on the same port. Therefore, the UART must either be connected to
32 * an echo server, or the uart connector must have a loopback connector fitted.
33 * See http://www.serialporttool.com/CommEcho.htm for a suitable echo server
36 * The timer sends a string to the UART, toggles an LED, then resets itself by
37 * changing its own period. The period is calculated as a pseudo random number
38 * between comTX_MAX_BLOCK_TIME and comTX_MIN_BLOCK_TIME.
40 * The task blocks on an Rx queue waiting for a character to become available.
41 * Received characters are checked to ensure they match those transmitted by the
42 * Tx timer. An error is latched if characters are missing, incorrect, or
45 * How characters are actually transmitted and received is port specific. Demos
46 * that include this test/demo file will provide example drivers. The Tx timer
47 * executes in the context of the timer service (daemon) task, and must
48 * therefore never attempt to block.
52 /* Scheduler include files. */
59 #ifndef configUSE_TIMERS
60 #error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h.
63 #if configUSE_TIMERS != 1
64 #error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h.
68 /* Demo program include files. */
70 #include "comtest_strings.h"
73 /* The size of the stack given to the Rx task. */
74 #define comSTACK_SIZE configMINIMAL_STACK_SIZE
76 /* See the comment above the declaration of the uxBaseLED variable. */
77 #define comTX_LED_OFFSET ( 0 )
78 #define comRX_LED_OFFSET ( 1 )
80 /* The Tx timer transmits the sequence of characters at a pseudo random
81 * interval that is capped between comTX_MAX_BLOCK_TIME and
82 * comTX_MIN_BLOCK_TIME. */
83 #define comTX_MAX_BLOCK_TIME ( ( TickType_t ) 0x96 )
84 #define comTX_MIN_BLOCK_TIME ( ( TickType_t ) 0x32 )
85 #define comOFFSET_TIME ( ( TickType_t ) 3 )
87 /* States for the simple state machine implemented in the Rx task. */
88 #define comtstWAITING_START_OF_STRING 0
89 #define comtstWAITING_END_OF_STRING 1
91 /* A short delay in ticks - this delay is used to allow the Rx queue to fill up
92 * a bit so more than one character can be processed at a time. This is relative
93 * to comTX_MIN_BLOCK_TIME to ensure it is never longer than the shortest gap
94 * between transmissions. It could be worked out more scientifically from the
95 * baud rate being used. */
96 #define comSHORT_DELAY ( comTX_MIN_BLOCK_TIME >> ( TickType_t ) 2 )
98 /* The string that is transmitted and received. */
99 #define comTRANSACTED_STRING "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
101 /* A block time of 0 simply means "don't block". */
102 #define comtstDONT_BLOCK ( TickType_t ) 0
104 /* Handle to the com port used by both tasks. */
105 static xComPortHandle xPort = NULL;
107 /* The callback function allocated to the transmit timer, as described in the
108 * comments at the top of this file. */
109 static void prvComTxTimerCallback( TimerHandle_t xTimer );
111 /* The receive task as described in the comments at the top of this file. */
112 static void vComRxTask( void * pvParameters );
114 /* The Rx task will toggle LED ( uxBaseLED + comRX_LED_OFFSET). The Tx task
115 * will toggle LED ( uxBaseLED + comTX_LED_OFFSET ). */
116 static UBaseType_t uxBaseLED = 0;
118 /* The Rx task toggles uxRxLoops on each successful iteration of its defined
119 * function - provided no errors have ever been latched. If this variable stops
120 * incrementing, then an error has occurred. */
121 static volatile UBaseType_t uxRxLoops = 0UL;
123 /* The timer used to periodically transmit the string. This is the timer that
124 * has prvComTxTimerCallback allocated to it as its callback function. */
125 static TimerHandle_t xTxTimer = NULL;
127 /* The string length is held at file scope so the Tx timer does not need to
128 * calculate it each time it executes. */
129 static size_t xStringLength = 0U;
131 /*-----------------------------------------------------------*/
133 void vStartComTestStringsTasks( UBaseType_t uxPriority,
137 /* Store values that are used at run time. */
140 /* Calculate the string length here, rather than each time the Tx timer
142 xStringLength = strlen( comTRANSACTED_STRING );
144 /* Include the null terminator in the string length as this is used to
145 * detect the end of the string in the Rx task. */
148 /* Initialise the com port, then spawn the Rx task and create the Tx
150 xSerialPortInitMinimal( ulBaudRate, ( xStringLength * 2U ) );
152 /* Create the Rx task and the Tx timer. The timer is started from the
154 xTaskCreate( vComRxTask, "COMRx", comSTACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL );
155 xTxTimer = xTimerCreate( "TxTimer", comTX_MIN_BLOCK_TIME, pdFALSE, NULL, prvComTxTimerCallback );
156 configASSERT( xTxTimer );
158 /*-----------------------------------------------------------*/
160 static void prvComTxTimerCallback( TimerHandle_t xTimer )
162 TickType_t xTimeToWait;
164 /* The parameter is not used in this case. */
167 /* Send the string. How this is actually performed depends on the
168 * sample driver provided with this demo. However - as this is a timer,
169 * it executes in the context of the timer task and therefore must not
171 vSerialPutString( xPort, comTRANSACTED_STRING, xStringLength );
173 /* Toggle an LED to give a visible indication that another transmission
174 * has been performed. */
175 vParTestToggleLED( uxBaseLED + comTX_LED_OFFSET );
177 /* Wait a pseudo random time before sending the string again. */
178 xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME;
180 /* Ensure the time to wait is not greater than comTX_MAX_BLOCK_TIME. */
181 xTimeToWait %= comTX_MAX_BLOCK_TIME;
183 /* Ensure the time to wait is not less than comTX_MIN_BLOCK_TIME. */
184 if( xTimeToWait < comTX_MIN_BLOCK_TIME )
186 xTimeToWait = comTX_MIN_BLOCK_TIME;
189 /* Reset the timer to run again xTimeToWait ticks from now. This function
190 * is called from the context of the timer task, so the block time must not
191 * be anything other than zero. */
192 xTimerChangePeriod( xTxTimer, xTimeToWait, comtstDONT_BLOCK );
194 /*-----------------------------------------------------------*/
196 static void vComRxTask( void * pvParameters )
198 BaseType_t xState = comtstWAITING_START_OF_STRING, xErrorOccurred = pdFALSE;
199 char * pcExpectedByte, cRxedChar;
200 const xComPortHandle xPort = NULL;
202 /* The parameter is not used in this example. */
203 ( void ) pvParameters;
205 /* Start the Tx timer. This only needs to be started once, as it will
206 * reset itself thereafter. */
207 xTimerStart( xTxTimer, portMAX_DELAY );
209 /* The first expected Rx character is the first in the string that is
211 pcExpectedByte = comTRANSACTED_STRING;
215 /* Wait for the next character. */
216 if( xSerialGetChar( xPort, &cRxedChar, ( comTX_MAX_BLOCK_TIME * 2 ) ) == pdFALSE )
218 /* A character definitely should have been received by now. As a
219 * character was not received an error must have occurred (which might
220 * just be that the loopback connector is not fitted). */
221 xErrorOccurred = pdTRUE;
226 case comtstWAITING_START_OF_STRING:
228 if( cRxedChar == *pcExpectedByte )
230 /* The received character was the first character of the
231 * string. Move to the next state to check each character
232 * as it comes in until the entire string has been received. */
233 xState = comtstWAITING_END_OF_STRING;
236 /* Block for a short period. This just allows the Rx queue
237 * to contain more than one character, and therefore prevent
238 * thrashing reads to the queue, and repetitive context
239 * switches as each character is received. */
240 vTaskDelay( comSHORT_DELAY );
245 case comtstWAITING_END_OF_STRING:
247 if( cRxedChar == *pcExpectedByte )
249 /* The received character was the expected character. Was
250 * it the last character in the string - i.e. the null
252 if( cRxedChar == 0x00 )
254 /* The entire string has been received. If no errors
255 * have been latched, then increment the loop counter to
256 * show this task is still healthy. */
257 if( xErrorOccurred == pdFALSE )
261 /* Toggle an LED to give a visible sign that a
262 * complete string has been received. */
263 vParTestToggleLED( uxBaseLED + comRX_LED_OFFSET );
266 /* Go back to wait for the start of the next string. */
267 pcExpectedByte = comTRANSACTED_STRING;
268 xState = comtstWAITING_START_OF_STRING;
272 /* Wait for the next character in the string. */
278 /* The character received was not that expected. */
279 xErrorOccurred = pdTRUE;
286 /* Should not get here. Stop the Rx loop counter from
287 * incrementing to latch the error. */
288 xErrorOccurred = pdTRUE;
293 /*-----------------------------------------------------------*/
295 BaseType_t xAreComTestTasksStillRunning( void )
299 /* If the count of successful reception loops has not changed than at
300 * some time an error occurred (i.e. a character was received out of sequence)
301 * and false is returned. */
302 if( uxRxLoops == 0UL )
311 /* Reset the count of successful Rx loops. When this function is called
312 * again it should have been incremented again. */