Danger-alarm/SOFTWARE-FreeRTOS/Common/Minimal/comtest_strings.c
2024-06-03 16:27:41 +08:00

317 lines
12 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
*
*/
/*
* 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 <stdlib.h>
#include <string.h>
#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;
}