Zynq UltraScale+でFreeRTOS+CLI(Command Line Interface)を動かしてみる

# 概要
Zynq UltraScale+ FreeRTOSにコマンドラインインターフェースを導入するための記事です。
FreeRTOS+でCLI(Command Line Interface)が提供されているのでこちらを使います。

記事としては
・Zynq UltraScale+ で [FreeRTOS+CLI](https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/FreeRTOS_Plus_Command_Line_Interface.html) のデモプログラムを動かす
　※環境
　　　評価ボード：Ultra96 V2
　　　動かすCore : R5
・FreeRTOS+CLIドキュメントの訳

となります。

# とりあえず動かす
FreeRTOS+CLIの動かしたときのイメージのために、この記事で説明するコードを動かす方法と見え方を最初に説明します。

・cloneする
`> git clone git@github.com:azukibar0713/ultra96v2_xsct_tcl.git -b BR_FREERTOS_CLI_DEMO`
・cloneしたディレクトリをXilinx SDK Eclipse ワークスペースとして開く
・ビルドして実行する

実行後は、以下のようなコマンド入力待ちとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/78fd9d9b-124d-ab15-ea6f-cefa608565e5.png)
`help`と入力すると、登録されているコマンドが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/bfc4eb9e-cb99-0558-30e6-f9332d489b35.png)
`task-stats`と入力すると、動いているFreeRTOSタスクのステータスが表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/0a8f46f7-bd47-7638-c505-50bd49067965.png)

**この他にも、自分自身でコマンドを追加していくことができます**

# FreeRTOS+CLIの取り込み方
既存のZynq UltraScale+のプロジェクトへの取り込み方法です。
## FreeRTOS+CLI ドキュメント
https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/FreeRTOS_Plus_Command_Line_Interface.html

## ダウンロード
FreeRTOS本体をcloneすると、FreeRTOS+CLI, デモプログラムも手に入ります。
`git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules`

## ZynqのFreeRTOS+CLIデモファイル
UltraScale+のデモはなかったです。
[Xilinx Zynq-7000 (dual core ARM Cortex-A9) SoC Port](ttps://www.freertos.org/RTOS-Xilinx-Zynq.html) のデモプログラムがあったのでこれをベースにします。

## 自身のプロジェクトにコピーするファイル
必要なファイルをコピーします。
### FreeRTOS+CLI
・FreeRTOS/FreeRTOS-Plus/Source/FreeRTOS-Plus-CLI/FreeRTOS_CLI.h
・FreeRTOS/FreeRTOS-Plus/Source/FreeRTOS-Plus-CLI/FreeRTOS_CLI.c

### CLIコマンド
・FreeRTOS/FreeRTOS-Plus/Demo/Common/FreeRTOS_Plus_CLI_Demos/Sample-CLI-commands.c

### CLI用のタスク
・FreeRTOS/FreeRTOS-Plus/Demo/Common/FreeRTOS_Plus_CLI_Demos/UARTCommandConsole.c

### serial driver
・FreeRTOS/FreeRTOS/Demo/Common/include/serial.h
・FreeRTOS/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/Full_Demo/serial.c

### Zynq UltraScale+用の変更
Zynq ZCU102 A9用なのでUltraScale+用にUARTのレジスタアドレスを変更します。
Ultra96V2では、UART0ではなくUART1です。アドレスも違いますが、xparameters.hに定義されているものを使えば良いです。

|serial.c 変更前|serial.c 変更後|
|:--|:--|
|XPAR_PS7_UART_1_BASEADDR|XPAR_PSU_UART_1_BASEADDR|
|   |   |

```c:psu_cortexr5_0/include/xparameters.h
/* Definitions for peripheral PSU_UART_1 */
#define XPAR_PSU_UART_1_DEVICE_ID 1
#define XPAR_PSU_UART_1_BASEADDR 0xFF010000
#define XPAR_PSU_UART_1_HIGHADDR 0xFF01FFFF
#define XPAR_PSU_UART_1_UART_CLK_FREQ_HZ 100000000
#define XPAR_PSU_UART_1_HAS_MODEM 0
```

※テクニカルリファレンス　システムマップより
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/4d5402f9-52bb-4938-db29-a3f110312bcd.png)


### mainに以下を記載

```c:
/*
 * Register commands that can be used with FreeRTOS+CLI.  The commands are
 * defined in CLI-Commands.c and File-Related-CLI-Command.c respectively.
 */
extern void vRegisterSampleCLICommands( void );

/*
 * The task that manages the FreeRTOS+CLI input and output.
 */
extern void vUARTCommandConsoleStart( uint16_t usStackSize, UBaseType_t uxPriority );

void main( void )
{
...
	/* Start the tasks that implements the command console on the UART, as
	described above. */
	vUARTCommandConsoleStart( mainUART_COMMAND_CONSOLE_STACK_SIZE, mainUART_COMMAND_CONSOLE_TASK_PRIORITY );

	/* Register the standard CLI commands. */
	vRegisterSampleCLICommands();
...
}
```
あとは、実行すると、ターミナルにプロンプトが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/686d1029-0ef0-3862-914c-42fab5f42b28.png)

---
# 本家のドキュメント訳
https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/FreeRTOS_Plus_Command_Line_Interface.html

## Introduction
FreeRTOS+CLI(Command Line Interface)はコマンドライン入力をFreeRTOSアプリケーションを実現する、シンプルで拡張性がありRAM仕様が効率的な方法である。
以下のステップで使える。

## コマンドラインの振る舞いを実装する関数を提供する
https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/FreeRTOS_Plus_CLI_Implementing_A_Command.html
### 入力と出力関数
次のインターフェースで実装する。

```c
BaseType_t xFunctionName( int8_t *pcWriteBuffer,
                             size_t xWriteBufferLen,
                             const int8_t *pcCommandString );
```

#### パラメータ
- pcWriteBuffer
    - 出力バッファ。例えばこの関数が単純に"Hello World"をリターンすると、この文字列はpcWriteBufferに書き込まれる。出力は常にnullターミネートされていること。
- xWriteBufferLen
    - pcWriteBufferのサイズ。xWriteBufferLen以上pcWriteBufferに書き込むとオーバーフローを起こす
- pcCommandString
    - コマンド文字列へのポインタ。すべてのコマンド文字列へのアクセスは、この関数の実装に、コマンドパラメータを取得することを許可する。 FreeRTOS+CLIはコマンド文字列を受けて、コマンドパラメータをreturnする[ヘルパー関数](https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/FreeRTOS_Plus_CLI_Accessing_Command_Line_Parameters.html)が提供されている。そのため、明示的な文字列パースは必要ではない。例はこのページでしめす。

#### 戻り値
いくつかのコマンド実行は、１行以上の結果を生成する。例えば、ファイルシステムの"dir"(or "ls")は、ディレクトリのファイルを出力して、以下のように見える

>file1.txt
file2.txt
file3.txt

RAM使用量を最小化するために、RAM使用は決定的であることを確認する。FreeRTOS+CLIは一度に一行出力するように実装されている。この関数はさらに出力があるのかどうかをreturnする。

pdFALSEの場合は出力が終了
pdTRUEの場合は１つ以上の出力行が残っている

3つのファイルを出力する"dir"コマンドの例は

1. dirコマンドを実装した関数が最初に呼ばれると、file1.txtを出力して、pdTRUEが返る
2. 2回目はfile2.txtを出力して、pdTRUEが返る
3. 3回目はfile3.txtを出力して、pdFALSEが返る

十分なRAMがあれば、xWriteBufferLenで十分な大きさを渡して、3行すべてを一度にreturnすることもできる。

各コマンドが実行されると、FreeRTOS+CLIはpdFALSEを返すまで、コマンドに実装された関数を繰り返し実行する。

### 例
以下の例を提供する

1. A commandは、パラメータなしで、1つの文字列をreturnする。
2. A commandは、パラメータなしで、複数の文字列を1度でreturnする。
3. A commandは、固定の数のパラメータを期待する
4. A commandは、可変の数のパラメータを受け取り、一度に可変の数の文字列をreturnする

#### Example 1: A command with no parameters
FreeRTOS vTaskList() APIは各タスクの状態を生成できる。この状態テーブルは各タスク1行となる。Example1では、すべてのテーブルを一度に出す実装となっている。

```c:
/* This function implements the behaviour of a command, so must have the correct
prototype. */
static BaseType_t prvTaskStatsCommand( int8_t *pcWriteBuffer,
                                          size_t xWriteBufferLen,
                                          const int8_t *pcCommandString )
{
    /* For simplicity, this function assumes the output buffer is large enough
    to hold all the text generated by executing the vTaskList() API function,
    so the xWriteBufferLen parameter is not used. */
    ( void ) xWriteBufferLen;

    /* pcWriteBuffer is used directly as the vTaskList() parameter, so the table
    generated by executing vTaskList() is written directly into the output
    buffer. */
    vTaskList( pcWriteBuffer + strlen( pcHeader ) );

    /* The entire table was written directly to the output buffer.  Execution
    of this command is complete, so return pdFALSE. */
    return pdFALSE;
}
```

#### Example 2: Returning multiple lines one line at a time
FreeRTOS+CLIに登録されるすべてのコマンドは、自身のhelp stringを持っている。このhelp stringはこのコマンドがどのように使われるのかを一行テキストである。FreeRTOS+CLIは`help`コマンドをを含んでいる。helpはすべてのhelp stringを返す。Example 2ではhelp commandの実装を示す。 example 1と違って、出力は一度に生成されずに、一行ごとに出す。この関数はリエントラントでない。

```c:
/* This function implements the behaviour of a command, so must have the correct
prototype. */
static BaseType_t prvHelpCommand( int8_t *pcWriteBuffer,
                                     size_t xWriteBufferLen,
                                     const int8_t *pcCommandString )
{
/* Executing the “help” command will generate multiple lines of text, but this
function will only output a single line at a time.  Therefore, this function is
called multiple times to complete the processing of a single “help” command.  That
means it has to remember which help strings it has already output, and which
still remain to be output.  The static pxCommand variable is used to point to the
next help string that needs outputting. */
static const xCommandLineInputListItem *pxCommand = NULL;
signed BaseType_t xReturn;

    if( pxCommand == NULL )
    {
        /* pxCommand is NULL in between executions of the “help” command, so if
        it is NULL on entry to this function it is the start of a new “help” command
        and the first help string is returned.  The following line points pxCommand
        to the first command registered with FreeRTOS+CLI. */
        pxCommand = &xRegisteredCommands;
    }

    /* Output the help string for the command pointed to by pxCommand, taking
    care not to overflow the output buffer. */
    strncpy( pcWriteBuffer,
             pxCommand->pxCommandLineDefinition->pcHelpString,
             xWriteBufferLen );

    /* Move onto the next command in the list, ready to output the help string
    for that command the next time this function is called. */
    pxCommand = pxCommand->pxNext;

    if( pxCommand == NULL )
    {
        /* If the next command in the list is NULL, then there are no more
        commands to process, and pdFALSE can be returned. */
        xReturn = pdFALSE;
    }
    else
    {
        /* If the next command in the list is not NULL, then there are more
        commands to process and therefore more lines of output to be generated.
        In this case pdTRUE is returned. */
        xReturn = pdTRUE;
    }

    return xReturn;
}
```

#### Example 3: A command with a fixed number of parameters
いくつかのコマンドをパラメータを取る。たとえばファイルシステムの"copy"は、source, destinationのファイル名が必要になる。Example3ではcopy commandのフレームワークで、どのようにパラメータにアクセスして使うのかを示す。
このコマンドが登録時に2つのパラメータを取るよう宣言されたとき、FreeRTOS+CLIはちょうど2つのパラメータが設定されないと、callしない。

```c:
/* This function implements the behaviour of a command, so must have the correct
prototype. */
static BaseType_t prvCopyCommand( int8_t *pcWriteBuffer,
                                     size_t xWriteBufferLen,
                                     const int8_t *pcCommandString )
{
int8_t *pcParameter1, *pcParameter2;
BaseType_t xParameter1StringLength, xParameter2StringLength, xResult;

    /* Obtain the name of the source file, and the length of its name, from
    the command string. The name of the source file is the first parameter. */
    pcParameter1 = FreeRTOS_CLIGetParameter
                        (
                          /* The command string itself. */
                          pcCommandString,
                          /* Return the first parameter. */
                          1,
                          /* Store the parameter string length. */
                          &xParameter1StringLength
                        );

    /* Obtain the name of the destination file, and the length of its name. */
    pcParameter2 = FreeRTOS_CLIGetParameter( pcCommandString,
                                             2,
                                             &xParameter2StringLength );

    /* Terminate both file names. */
    pcParameter1[ xParameter1StringLength ] = 0x00;
    pcParameter2[ xParameter2StringLength ] = 0x00;

    /* Perform the copy operation itself. */
    xResult = prvCopyFile( pcParameter1, pcParameter2 );

    if( xResult == pdPASS )
    {
        /* The copy was successful.  There is nothing to output. */
        *pcWriteBuffer = NULL;
    }
    else
    {
        /* The copy was not successful.  Inform the users. */
        snprintf( pcWriteBuffer, xWriteBufferLen, “Error during copyrnrn” );
    }

    /* There is only a single line of output produced in all cases.  pdFALSE is
    returned because there is no more output to be generated. */
    return pdFALSE;
}
```

#### Example 4: A command with a variable number of parameters
Example4では可変数のパラメータを受けるコマンドを実装する。FreeRTOS+CLIはパラメータ数を確認しない。このコマンドは単純に、パラメータをエコーバックする。もしcommand stringが"echo_parameters"の場合に、ユーザーが"echo_parameters one two three four"と入力すると、出力は

> 1: one
2: two
3: three
4: four

となる。


```c:
static BaseType_t prvParameterEchoCommand(     int8_t *pcWriteBuffer,
                                                size_t xWriteBufferLen, c
                                                onst int8_t *pcCommandString )
{
int8_t *pcParameter;
BaseType_t lParameterStringLength, xReturn;

/* Note that the use of the static parameter means this function is not reentrant. */
static BaseType_t lParameterNumber = 0;

    if( lParameterNumber == 0 )
    {
        /* lParameterNumber is 0, so this is the first time the function has been
        called since the command was entered.  Return the string “The parameters
        were:” before returning any parameter strings. */
        sprintf( pcWriteBuffer, “The parameters were:rn” );

        /* Next time the function is called the first parameter will be echoed
        back. */
        lParameterNumber = 1L;

        /* There is more data to be returned as no parameters have been echoed
        back yet, so set xReturn to pdPASS so the function will be called again. */
        xReturn = pdPASS;
    }
    else
    {
        /* lParameter is not 0, so holds the number of the parameter that should
        be returned.  Obtain the complete parameter string. */
        pcParameter = ( int8_t * ) FreeRTOS_CLIGetParameter
                                    (
                                        /* The command string itself. */
                                        pcCommandString,
                                        /* Return the next parameter. */
                                        lParameterNumber,
                                        /* Store the parameter string length. */
                                        &lParameterStringLength
                                    );

        if( pcParameter != NULL )
        {
            /* There was another parameter to return.  Copy it into pcWriteBuffer.
            in the format “[number]: [Parameter String”. */
            memset( pcWriteBuffer, 0x00, xWriteBufferLen );
            sprintf( pcWriteBuffer, “%d: “, lParameterNumber );
            strncat( pcWriteBuffer, pcParameter, lParameterStringLength );
            strncat( pcWriteBuffer, “rn”, strlen( “rn” ) );

            /* There might be more parameters to return after this one, so again
            set xReturn to pdTRUE. */
            xReturn = pdTRUE;
            lParameterNumber++;
        }
        else
        {
            /* No more parameters were found.  Make sure the write buffer does
            not contain a valid string to prevent junk being printed out. */
            pcWriteBuffer[ 0 ] = 0x00;

            /* There is no more data to return, so this time set xReturn to
            pdFALSE. */
            xReturn = pdFALSE;

            /* Start over the next time this command is executed. */
            lParameterNumber = 0;
        }
    }

    return xReturn;
}
```

## 振る舞いを実装した関数をコマンドにマップする、FreeRTOS+CLIにコマンドを登録する
https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/FreeRTOS_Plus_CLI_Registering_A_Command.html

### FreeRTOS_CLIRegisterComamnd()

```c:FreeRTOS_CLI.h
BaseType_t FreeRTOS_CLIRegisterCommand( CLI_Command_Definition_t *pxCommandToRegister )
```

FreeRTOS+CLIは拡張フレームワークで、アプリケーション開発者に自身のコマンドライン入力コマンドを定義、登録できる。実装するコマンドは特定のインターフェースを持つ必要がある。

ここでは、コマンドをFreeRTOS+CLIにと黒くする、FreeRTOS_CLIRegisterCommandを説明する。コマンドは、文字列でコマンドの振る舞いに関連付けられて登録され、FreeRTOS+CLIにしれを伝える。FreeRTOS+CLIは自動的にコマンド文字列が入力されると、関数を実行する。

Note: FreeRTOS_CLIRegisterCommandプロトタイプは、const構造体 CLI_Command_Definition_tに対するconst pointerを取るようになっている。ここでは読みやすいようにconstを外している。

#### パラメータ
- pxCommandToRegister
    - 登録するコマンドで、CLI_Command_Definition_t型で定義される。構造体は追って説明する。

#### 戻り値
- pdPASS
    - 登録成功
- pdFALSE
    - ヒープ不足で登録失敗

#### CLI_Command_Definition_t

```c:
typedef struct xCLI_COMMAND_DEFINITION
{
    /* The command line input string.  This is the string that the user enters
    to run the command.  For example, the FreeRTOS+CLI help function uses the
    string “help”.  If a user types “help” the help command executes. */
    const int8_t * const pcCommand;                
    
    /* A string that describes the command, and its expected parameters.  This
    is the string that is output when the help command is executed.  The string
    must start with the command itself, and end with “rn”.  For example, the
    help string for the help command itself is:
    “help: Returns a list of all the commandsrn” */
    const int8_t * const pcHelpString;
    
    /* A pointer to the function that implements the command behaviour 
    (effectively the function name). */
    const pdCOMMAND_LINE_CALLBACK pxCommandInterpreter;

    /* The number of parameters required by the command.  FreeRTOS+CLI will only
    execute the command if the number of parameters entered on the command line
    matches this number. */
    int8_t cExpectedNumberOfParameters;
} CLI_Command_Definition_t;
```

#### Example
FreeRTOS+CLIのデモはfile systemのdelを実装している。定義は以下

```c:
static const CLI_Command_Definition_t xDelCommand =
{
    “del”,
    “del <filename>: Deletes <filename> from the diskrn”,
    prvDelCommand,
    1
};
```

このコマンドが登録されると

- prvDelCommandがdelを入力するたびに実行される
- "del<filename>: dEletes <filename> from the diskrn"は、"help"を打つと表示される
- delコマンドは、パラメータ1つを取る。FreeRTOS+CLIはパラメータが1以外の時には、prvDelCommand()を実行する代わりにエラーを出力する。

delコマンドは、FreeRTOS+CLIで以下の関数コールで登録される。

```c:
FreeRTOS_CLIRegisterCommand( &xDelCommand );
```



## command interpreterを実行する
https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/FreeRTOS_Plus_CLI_IO_Interfacing_and_Task.html
FreeRTOS+CLIのハードウェアへのポート方法の説明。I/O routine, FreeRTOS+CLIタスクの提供

### Input and Output
コマンドラインインターフェースは、文字列をinputから受け取り、outputへ出力する。低レベルの実現方法はマイコンのIFに依存する。FreeRTOS+CLIでもでは FreeRTOS+IO [FreeRTOS_read](https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_IO/FreeRTOS_read.html), [FreeRTOS_write](https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_IO/FreeRTOS_write.html)を、UARTのinput/output用に使う。TCP/IP Socketを使うデモもある。

### FreeRTOS CLIタスクの例
下記のソースコードはFreeRTOS+CLIコマンドインターフェースを管理するタスクとなる。FreeRTOS+IO, FreeRTOS_read, FreeRTOS_writeは、IO　Interfaceのために使われている。FreeRTOS+IOのディスクリプタはすでに[open](https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_IO/FreeRTOS_open.html)して、[character queue transfer mode](https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_IO/Character_Queue_Transfer_Mode.html)でコンフィグしているとする。タスクはFreeRTOS_CLIProcessCommandを使う。

```c:
#define MAX_INPUT_LENGTH    50
#define MAX_OUTPUT_LENGTH   100

static const int8_t * const pcWelcomeMessage =
  "FreeRTOS command server.rnType Help to view a list of registered commands.rn";

void vCommandConsoleTask( void *pvParameters )
{
Peripheral_Descriptor_t xConsole;
int8_t cRxedChar, cInputIndex = 0;
BaseType_t xMoreDataToFollow;
/* The input and output buffers are declared static to keep them off the stack. */
static int8_t pcOutputString[ MAX_OUTPUT_LENGTH ], pcInputString[ MAX_INPUT_LENGTH ];

    /* This code assumes the peripheral being used as the console has already
    been opened and configured, and is passed into the task as the task
    parameter.  Cast the task parameter to the correct type. */
    xConsole = ( Peripheral_Descriptor_t ) pvParameters;

    /* Send a welcome message to the user knows they are connected. */
    FreeRTOS_write( xConsole, pcWelcomeMessage, strlen( pcWelcomeMessage ) );

    for( ;; )
    {
        /* This implementation reads a single character at a time.  Wait in the
        Blocked state until a character is received. */
        FreeRTOS_read( xConsole, &cRxedChar, sizeof( cRxedChar ) );

        if( cRxedChar == '\n' )
        {
            /* A newline character was received, so the input command string is
            complete and can be processed.  Transmit a line separator, just to
            make the output easier to read. */
            FreeRTOS_write( xConsole, "\r\n", strlen( "\r\n" );

            /* The command interpreter is called repeatedly until it returns
            pdFALSE.  See the "Implementing a command" documentation for an
            exaplanation of why this is. */
            do
            {
                /* Send the command string to the command interpreter.  Any
                output generated by the command interpreter will be placed in the
                pcOutputString buffer. */
                xMoreDataToFollow = FreeRTOS_CLIProcessCommand
                              (
                                  pcInputString,   /* The command string.*/
                                  pcOutputString,  /* The output buffer. */
                                  MAX_OUTPUT_LENGTH/* The size of the output buffer. */
                              );

                /* Write the output generated by the command interpreter to the
                console. */
                FreeRTOS_write( xConsole, pcOutputString, strlen( pcOutputString ) );

            } while( xMoreDataToFollow != pdFALSE );

            /* All the strings generated by the input command have been sent.
            Processing of the command is complete.  Clear the input string ready
            to receive the next command. */
            cInputIndex = 0;
            memset( pcInputString, 0x00, MAX_INPUT_LENGTH );
        }
        else
        {
            /* The if() clause performs the processing after a newline character
            is received.  This else clause performs the processing if any other
            character is received. */

            if( cRxedChar == '\r' )
            {
                /* Ignore carriage returns. */
            }
            else if( cRxedChar == '\b' )
            {
                /* Backspace was pressed.  Erase the last character in the input
                buffer - if there are any. */
                if( cInputIndex > 0 )
                {
                    cInputIndex--;
                    pcInputString[ cInputIndex ] = '';
                }
            }
            else
            {
                /* A character was entered.  It was not a new line, backspace
                or carriage return, so it is accepted as part of the input and
                placed into the input buffer.  When a n is entered the complete
                string will be passed to the command interpreter. */
                if( cInputIndex < MAX_INPUT_LENGTH )
                {
                    pcInputString[ cInputIndex ] = cRxedChar;
                    cInputIndex++;
                }
            }
        }
    }
}
```