]> begriffs open source - cmsis-freertos/blob - Demo/Common/Minimal/comtest_strings.c
Updated pack to FreeRTOS 10.4.4
[cmsis-freertos] / Demo / Common / Minimal / comtest_strings.c
1 /*
2  * FreeRTOS V202107.00
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
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.
21  *
22  * https://www.FreeRTOS.org
23  * https://github.com/FreeRTOS
24  *
25  */
26
27
28 /*
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
34  * for Windows hosts.
35  *
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.
39  *
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
43  * arrive too slowly.
44  *
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.
49  *
50  */
51
52 /* Scheduler include files. */
53 #include <stdlib.h>
54 #include <string.h>
55 #include "FreeRTOS.h"
56 #include "task.h"
57 #include "timers.h"
58
59 #ifndef configUSE_TIMERS
60     #error This demo uses timers.  configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h.
61 #endif
62
63 #if configUSE_TIMERS != 1
64     #error This demo uses timers.  configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h.
65 #endif
66
67
68 /* Demo program include files. */
69 #include "serial.h"
70 #include "comtest_strings.h"
71 #include "partest.h"
72
73 /* The size of the stack given to the Rx task. */
74 #define comSTACK_SIZE                    configMINIMAL_STACK_SIZE
75
76 /* See the comment above the declaration of the uxBaseLED variable. */
77 #define comTX_LED_OFFSET                 ( 0 )
78 #define comRX_LED_OFFSET                 ( 1 )
79
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 )
86
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
90
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 )
97
98 /* The string that is transmitted and received. */
99 #define comTRANSACTED_STRING             "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
100
101 /* A block time of 0 simply means "don't block". */
102 #define comtstDONT_BLOCK                 ( TickType_t ) 0
103
104 /* Handle to the com port used by both tasks. */
105 static xComPortHandle xPort = NULL;
106
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 );
110
111 /* The receive task as described in the comments at the top of this file. */
112 static void vComRxTask( void * pvParameters );
113
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;
117
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;
122
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;
126
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;
130
131 /*-----------------------------------------------------------*/
132
133 void vStartComTestStringsTasks( UBaseType_t uxPriority,
134                                 uint32_t ulBaudRate,
135                                 UBaseType_t uxLED )
136 {
137     /* Store values that are used at run time. */
138     uxBaseLED = uxLED;
139
140     /* Calculate the string length here, rather than each time the Tx timer
141      * executes. */
142     xStringLength = strlen( comTRANSACTED_STRING );
143
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. */
146     xStringLength++;
147
148     /* Initialise the com port, then spawn the Rx task and create the Tx
149      * timer. */
150     xSerialPortInitMinimal( ulBaudRate, ( xStringLength * 2U ) );
151
152     /* Create the Rx task and the Tx timer.  The timer is started from the
153      * Rx task. */
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 );
157 }
158 /*-----------------------------------------------------------*/
159
160 static void prvComTxTimerCallback( TimerHandle_t xTimer )
161 {
162     TickType_t xTimeToWait;
163
164     /* The parameter is not used in this case. */
165     ( void ) xTimer;
166
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
170      * block. */
171     vSerialPutString( xPort, comTRANSACTED_STRING, xStringLength );
172
173     /* Toggle an LED to give a visible indication that another transmission
174      * has been performed. */
175     vParTestToggleLED( uxBaseLED + comTX_LED_OFFSET );
176
177     /* Wait a pseudo random time before sending the string again. */
178     xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME;
179
180     /* Ensure the time to wait is not greater than comTX_MAX_BLOCK_TIME. */
181     xTimeToWait %= comTX_MAX_BLOCK_TIME;
182
183     /* Ensure the time to wait is not less than comTX_MIN_BLOCK_TIME. */
184     if( xTimeToWait < comTX_MIN_BLOCK_TIME )
185     {
186         xTimeToWait = comTX_MIN_BLOCK_TIME;
187     }
188
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 );
193 }
194 /*-----------------------------------------------------------*/
195
196 static void vComRxTask( void * pvParameters )
197 {
198     BaseType_t xState = comtstWAITING_START_OF_STRING, xErrorOccurred = pdFALSE;
199     char * pcExpectedByte, cRxedChar;
200     const xComPortHandle xPort = NULL;
201
202     /* The parameter is not used in this example. */
203     ( void ) pvParameters;
204
205     /* Start the Tx timer.  This only needs to be started once, as it will
206      * reset itself thereafter. */
207     xTimerStart( xTxTimer, portMAX_DELAY );
208
209     /* The first expected Rx character is the first in the string that is
210      * transmitted. */
211     pcExpectedByte = comTRANSACTED_STRING;
212
213     for( ; ; )
214     {
215         /* Wait for the next character. */
216         if( xSerialGetChar( xPort, &cRxedChar, ( comTX_MAX_BLOCK_TIME * 2 ) ) == pdFALSE )
217         {
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;
222         }
223
224         switch( xState )
225         {
226             case comtstWAITING_START_OF_STRING:
227
228                 if( cRxedChar == *pcExpectedByte )
229                 {
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;
234                     pcExpectedByte++;
235
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 );
241                 }
242
243                 break;
244
245             case comtstWAITING_END_OF_STRING:
246
247                 if( cRxedChar == *pcExpectedByte )
248                 {
249                     /* The received character was the expected character.  Was
250                      * it the last character in the string - i.e. the null
251                      * terminator? */
252                     if( cRxedChar == 0x00 )
253                     {
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 )
258                         {
259                             uxRxLoops++;
260
261                             /* Toggle an LED to give a visible sign that a
262                              * complete string has been received. */
263                             vParTestToggleLED( uxBaseLED + comRX_LED_OFFSET );
264                         }
265
266                         /* Go back to wait for the start of the next string. */
267                         pcExpectedByte = comTRANSACTED_STRING;
268                         xState = comtstWAITING_START_OF_STRING;
269                     }
270                     else
271                     {
272                         /* Wait for the next character in the string. */
273                         pcExpectedByte++;
274                     }
275                 }
276                 else
277                 {
278                     /* The character received was not that expected. */
279                     xErrorOccurred = pdTRUE;
280                 }
281
282                 break;
283
284             default:
285
286                 /* Should not get here.  Stop the Rx loop counter from
287                  * incrementing to latch the error. */
288                 xErrorOccurred = pdTRUE;
289                 break;
290         }
291     }
292 }
293 /*-----------------------------------------------------------*/
294
295 BaseType_t xAreComTestTasksStillRunning( void )
296 {
297     BaseType_t xReturn;
298
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 )
303     {
304         xReturn = pdFALSE;
305     }
306     else
307     {
308         xReturn = pdTRUE;
309     }
310
311     /* Reset the count of successful Rx loops.  When this function is called
312      * again it should have been incremented again. */
313     uxRxLoops = 0UL;
314
315     return xReturn;
316 }