Espressif 玩转串口解析(FreeRTOS-Plus-CLI)

最近想用 ESP32-C3 做一个串口解析的应用程序。类似 ESP-AT,但实现的功能比 ESP-AT 少的多。使用 ESP-IDF 的版本为 release/v4.4,本来是想基于 uart_events example 自己来实现串口解析的逻辑,但突然发现 FREERTOS 竟然提供了一个组件 FreeRTOS-Plus-CLI,可以实现串口解析的逻辑,并且和 release/v4.4 完美衔接,省去了自己编写串口解析代码的烦恼,只需要专注于上层功能逻辑的实现即可,简直是太方便了,可以说是开发神器。

FreeRTOS-Plus-CLI 简介

FreeRTOS-Plus-CLI (Command Line Interface) 简单点说就是提供了一种简单、小巧、易于扩展且占用 RAM 资源极少的方法,可以在 FREERTOS 的应用上处理命令行输入。

从 FreeRTOS V10.0.0 开始,FreeRTOS-Plus-CLI 使用和 FreeRTOS 内核相同的 MIT 许可。

FreeRTOS-Plus-CLI 使用步骤

想要将 FreeRTOS-Plus-CLI 使用起来,仅仅需要以下 4 步即可:
在这里插入图片描述

实现命令的行为

命令的实现行为很简单,仅需要实现以下接口即可:

BaseType_t xFunctionName( char *pcWriteBuffer,
                             size_t xWriteBufferLen,
                             const char *pcCommandString );
  • pcWriteBuffer: 输出缓冲区。可以用来存储命令的输出。
  • xWriteBufferLen: 输出缓冲区中的数据长度。单位:字节。
  • pcCommandString: 用户输入的整个命令行字符串。

该接口的实现 FREERTOS 其实已经给了很好的 demo。这里就以可变参数数量的命令行为例,结合 FREERTOS 提供的 demo 简单讲解一下。(这里 FREERTOS 提供的 demo 小小的该动了一下)

BaseType_t prvParameterSwitchCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString)
{
    char *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:\r\n" );

        /* 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 = ( char * ) 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 );
            printf("%s\r\n", pcWriteBuffer);

            /* 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;
}

这里假设我们输入的命令行为 test 1 12 123 1234,那么该接口执行完之后的输出如下:

1: 1
2: 12
3: 123
4: 1234

该接口的返回值决定了该接口是否会被继续调用。

  • 如果命令行的所有参数都已经处理完毕,则返回 pdFALSE
  • 如果命令行需要继续处理参数,则返回 pdTRUE

映射命令到命令实现接口

这步其实很简单,说白了就是将命令和命令的实现接口进行绑定。绑定之后只需注册下命令即可。

CLI_Command_Definition_t

命令由 CLI_Command_Definition_t 类型的结构定义。

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-Plus-CLI help function uses the
    string "help".  If a user types "help" the help command executes. */
    const char * 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 char * 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-Plus-CLI will only
    execute the command if the number of parameters entered on the command line
    matches this number. */
    char cExpectedNumberOfParameters;
} CLI_Command_Definition_t;
  • pcCommand: 具体命令。典型命令就是 “help”。
  • pcHelpString: 命令描述。
  • pxCommandInterpreter: 函数指针。指向命令实现接口。
  • cExpectedNumberOfParameters: 参数个数。设置为 -1 表示参数个数可变。

注册命令

FreeRTOS_CLIRegisterCommand

接口 FreeRTOS_CLIRegisterCommand 用来注册命令。原型如下:

/*
 * Register the command passed in using the pxCommandToRegister parameter.
 * Registering a command adds the command to the list of commands that are
 * handled by the command interpreter.  Once a command has been registered it
 * can be executed from the command line.
 */
BaseType_t FreeRTOS_CLIRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister );

注册成功返回 pdPASS,失败返回 pdFAIL

运行命令解释器

对于 FREERTOS 来说,说白了就是建立一个 task 去一个字节一个字节的从 UART 的输入缓冲区读取数据进行命令解析(前提是命令已经通过 FreeRTOS_CLIRegisterCommand 注册过)。

由于使用的是 ESP32-C3,所以仅需要调用 uart_read_bytes 接口即可,ticks_to_wait 参数可设置为 portMAX_DELAY。这样子可以将 task 进行阻塞,只在有数据的时候进行处理即可,不会占据 CPU 太多的处理时间。

void cmd_parse_task( void *pvParameters )
{
    char cRxedChar = 0; 
    uint32_t 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 ];

    for( ;; ) {
        /* This implementation reads a single character at a time.  Wait in the
        Blocked state until a character is received. */
        uart_read_bytes(COMM_UART_NUM, &cRxedChar, 1, portMAX_DELAY);

        if( cRxedChar == '\r' )
        {
            /* 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
                              (
                                  (const char *)pcInputString,   /* The command string.*/
                                  (char *)pcOutputString,  /* The output buffer. */
                                  MAX_OUTPUT_LENGTH/* The size of the output buffer. */
                              );
            } 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 ] = '\0';
                }
            }
            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++;
                }
            }
        }
    }
}

这段代码中核心语句为

            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
                              (
                                  (const char *)pcInputString,   /* The command string.*/
                                  (char *)pcOutputString,  /* The output buffer. */
                                  MAX_OUTPUT_LENGTH/* The size of the output buffer. */
                              );
            } while( xMoreDataToFollow != pdFALSE );

从 UART 的输入缓冲区中一个字节一个字节将数据放到 pcInputString 所指向的输入命令缓冲区中。然后接口 FreeRTOS_CLIProcessCommand 在内部会不断的将输入的命令和注册命令进行匹配,如果匹配到了就会跳到对应的命令实现接口中去执行。

例子中是以 CR(回车) 作为命令行的结束符。如果要将以 CR/LF (回车/换行) 作为命令行的结束符,可以将这里的 if( cRxedChar == '\r' ) 替换成 if( cRxedChar == '\n' ) 即可。

        if( cRxedChar == '\r' )
        {
            /* 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
                              (
                                  (const char *)pcInputString,   /* The command string.*/
                                  (char *)pcOutputString,  /* The output buffer. */
                                  MAX_OUTPUT_LENGTH/* The size of the output buffer. */
                              );
            } while( xMoreDataToFollow != pdFALSE );

写在最后

以上就是 FreeRTOS-Plus-CLI 的简单使用教程。如果还有不明白的,可以参考我 GitHub 上的 esp32-uart-parse 示例。该示例以一个 test <param_1>, <param_2>[, <param_3>, <param_4>] 命令为例完整的演示了该如何使用 FreeRTOS-Plus-CLI。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式工程狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值