手撸 串口交互命令行 及 AT应用层协议解析框架

        在嵌入式系统开发中,命令行接口(CLI)和AT命令解析是常见的需求。CLI提供了方便的调试接口,而AT命令则常用于模块间的通信控制。本文将介绍如何手动实现一个串口交互的命令行及AT应用层协议解析框架,适用于FreeRTOS系统。

流程图:

        这个回调函数 HAL_UART_RxCpltCallback 是用于处理 UART(通用异步收发传输器)接收到的数据。在接收数据的过程中会首先对接收到的字符进行判断,并且判断接收缓冲区是否还有空间,如果已满,则调用 ProcessReceivedFrame 处理数据,并在每次接受满之后重启UART中断。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{// 用于检查当前触发中断的 UART 实例是否是我们期望处理的那个实例 xPort。if (huart->Instance == xPort.Instance){// 如果这是接收到的第一个字节,清空接收缓冲区if (rx_index == 0){//如果当前接收到的数据是本次接收的第一个字节(rx_index 为 0),//则将接收缓冲区清空。memset(rx_buffer, '\0', xPort_RX_BUFFER_SIZE);}// 如果接收到回车符 '\r',目前没有处理逻辑if (received_char == '\r') // || cRxedChar == '\r'{// 这里可以添加处理回车符的逻辑}// 如果接收到换行符 '\n'else if (received_char == '\n') // || cRxedChar == '\r'{if (rx_index != 0){// 处理接收到的一帧数据// 在此处添加处理逻辑,例如将数据复制到另一个缓冲区或进行解析ProcessReceivedFrame(rx_buffer, &rx_index);}}else{// 将接收到的字符存储到接收缓冲区rx_buffer[rx_index++] = (char)received_char;// 检查接收缓冲区是否已满if (rx_index >= xPort_RX_BUFFER_SIZE){// 处理接收到的一帧数据ProcessReceivedFrame(rx_buffer, &rx_index);}}// 重新启动 UART 接收中断,以接收下一个字节HAL_UART_Receive_IT(&xPort, &received_char, 1);}
}

        结合之前对 HAL_UART_RxCpltCallback 函数的理解,现在我们详细解释如何通过ProcessReceivedFrame 函数处理接收到的数据:

        首先通过 osPoolAlloc 从内存池中分配内存,以存储接收到的数据,接着使用 memset 函数将分配的内存初始化为零。然后将当前帧数据的长度存储到 message->len 中,并使用 strncpy 函数将缓冲区的数据拷贝到 message->buff 中,最后将拷贝的数据放入消息队列中,等待处理。重置缓冲区长度指针 plength 为 0,表示缓冲区已处理完毕。使用 memset 函数将缓冲区清空,以准备接收新的数据。

void ProcessReceivedFrame(char *buffer, uint8_t* plength)
{// // 处理接收到的一帧数据// // 例如,打印接收到的数据// buffer[length - 2] = '\0'; // 替换 "\r\n" 为字符串终止符// printf("Received frame: %s\n", buffer);// osMessagePut(uartQueueHandle, (uint32_t)buffer, osWaitForever);USART_Msg_Def *message;message = (USART_Msg_Def *)osPoolAlloc(uartmsgPoolHandle); // 申请内存//osPoolAlloc There is dirt in this memory poolmemset(message->buff, '\0', xPort_RX_BUFFER_SIZE);message->len = *plength;strncpy(message->buff, buffer, message->len);                     // 数据拷贝osMessagePut(uartQueueHandle, (uint32_t)message, osWaitForever); // 写入队列*plength = 0;memset(buffer, '\0', xPort_RX_BUFFER_SIZE);
}

vUARTCommandConsoleStart 函数通过以下步骤来启动 UART 命令控制台任务:

  • 注册 CLI 命令:调用 vRegisterSampleCLICommands 函数,注册一些示例 CLI 命令,这些命令将在命令控制台任务中使用。
  • 创建一个互斥量 xTxMutex,用于保护对 UART 发送操作的访问,确保线程安全。使用 configASSERT 确保互斥量创建成功。如果创建失败,程序将进入断言。
  • 使用 xTaskCreate 创建一个新的任务,该任务将实现 UART 命令控制台。
  • 启动 UART 接收功能,以便命令控制台可以接收来自 UART 的数据。
void vUARTCommandConsoleStart(void)
{uint16_t usStackSize = 512;UBaseType_t uxPriority = 0;// 注册示例 CLI 命令vRegisterSampleCLICommands();// 创建用于访问 UART Tx 的信号量(互斥量)xTxMutex = xSemaphoreCreateMutex();configASSERT(xTxMutex);// 创建处理命令控制台的任务xTaskCreate(prvUARTCommandConsoleTask, // 实现命令控制台的任务函数"CLI",                     // 任务名称,用于调试usStackSize,               // 分配给任务的堆栈大小NULL,                      // 任务参数,这里未使用,传递 NULLuxPriority,                // 任务优先级NULL);                     // 任务句柄,这里未使用,传递 NULL// 启动 UART 接收StartUARTReception();
}

 prvUARTCommandConsoleTask 函数是一个 FreeRTOS 任务,用于处理 UART 命令控制台。这个任务从消息队列中读取接收到的 UART 数据,并将其传递给命令解释器进行处理。

static void prvUARTCommandConsoleTask(void *pvParameters)
{char *pcOutputString;BaseType_t xReturned;(void)pvParameters;/* Obtain the address of the output buffer. Note there is no mutualexclusion on this buffer as it is assumed only one command console interfacewill be used at any one time. */pcOutputString = FreeRTOS_CLIGetOutputBuffer();/* Send the welcome message. */vSerialPutString(pcWelcomeMessage, (uint16_t)strlen((char*)pcWelcomeMessage));for (;;){osEvent eEventTmp;USART_Msg_Def *message;// 等待从消息队列中接收消息eEventTmp = osMessageGet(uartQueueHandle, 0);if (eEventTmp.status == osEventMessage){// 处理接收到的数据帧message = (USART_Msg_Def *)eEventTmp.value.p;char cInputString[cmdMAX_INPUT_SIZE];memcpy(cInputString, message->buff, cmdMAX_INPUT_SIZE);int8_t cInputLen = message->len;osPoolFree(uartmsgPoolHandle, message); // 释放内存#if debugchar tmp[50];sprintf(tmp, "len=%d,strlen=%d\n", cInputLen, strlen(cInputString));HAL_UART_Transmit(&xPort, (const uint8_t *)tmp, strlen(tmp), portMAX_DELAY);
#endifif (xSemaphoreTake(xTxMutex, cmdMAX_MUTEX_WAIT) == pdPASS){vSerialPutString(cInputString, cInputLen);vSerialPutString(pcNewLine, (uint16_t)strlen((char*)pcNewLine));do{/* Get the next output string from the command interpreter. */xReturned = FreeRTOS_CLIProcessCommand(cInputString, pcOutputString, configCOMMAND_INT_MAX_OUTPUT_SIZE);/* Write the generated string to the UART. */vSerialPutString(pcOutputString, (uint16_t)strlen((char*)pcOutputString));} while (xReturned != pdFALSE);vSerialPutString("\r\n>", 3);memset(cInputString, 0x00, cmdMAX_INPUT_SIZE);/* Must ensure to give the mutex back. */xSemaphoreGive(xTxMutex);}}osDelay(1);}
}

这个任务实现了一个简单的 UART 命令控制台,通过消息队列接收 UART 输入数据,使用命令解释器处理输入命令,并将结果输出到 UART。任务使用互斥量确保对 UART 发送操作的线程安全。通过这种方式,系统可以处理并执行来自 UART 的命令,同时确保并发访问的安全性。

 

下面这段代码注册了两个CLI(命令行接口)命令:EchoAT

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"/* Standard includes. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>/* FreeRTOS+CLI includes. */
#include "FreeRTOS_CLI.h"#include "CLI.h"static BaseType_t prvATCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
static const CLI_Command_Definition_t xATCommand ={"AT","\r\nATcmd:\r\n +RST +MODE=0/1 +M=1/2,0~180\r\n",prvATCommand, /* The function to run. */0			  /* The user can enter any number of commands. */
};
static BaseType_t prvATCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString)
{BaseType_t xReturn;char *p = pcCommandString+2;ATcmdAnalyse(p);strncat(pcWriteBuffer, "\r\nOK", strlen("\r\nOK"));// strncpy( pcWriteBuffer, "what can i say, man!!!\r\n", xWriteBufferLen );xReturn = pdFALSE;return xReturn;
}/*-----------------------------------------------------------*//** Implements the task-stats command.*/
static BaseType_t prvParameterEchoCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);/* Structure that defines the "echo_parameters" command line command.  This
takes a variable number of parameters that the command simply echos back one at
a time. */
static const CLI_Command_Definition_t xParameterEcho ={"Echo","\r\nEcho <...>:\r\n Take variable number of parameters, echos each in turn\r\n",prvParameterEchoCommand, /* The function to run. */-1						 /* The user can enter any number of commands. */
};static BaseType_t prvParameterEchoCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString)
{const char *pcParameter;BaseType_t xParameterStringLength, xReturn;static UBaseType_t uxParameterNumber = 0;/* Remove compile time warnings about unused parameters, and check thewrite buffer is not NULL.  NOTE - for simplicity, this example assumes thewrite buffer length is adequate, so does not check for buffer overflows. */(void)pcCommandString;(void)xWriteBufferLen;configASSERT(pcWriteBuffer);if (uxParameterNumber == 0){/* The first time the function is called after the command has beenentered just a header string is returned. */sprintf(pcWriteBuffer, "The parameters were:\r\n");/* Next time the function is called the first parameter will be echoedback. */uxParameterNumber = 1U;/* There is more data to be returned as no parameters have been echoedback yet. */xReturn = pdPASS;}else{/* Obtain the parameter string. */pcParameter = FreeRTOS_CLIGetParameter(pcCommandString,		/* The command string itself. */uxParameterNumber,		/* Return the next parameter. */&xParameterStringLength /* Store the parameter string length. */);if (pcParameter != NULL){/* Return the parameter string. */memset(pcWriteBuffer, 0x00, xWriteBufferLen);sprintf(pcWriteBuffer, "%d: ", (int)uxParameterNumber);strncat(pcWriteBuffer, (char *)pcParameter, (size_t)xParameterStringLength);strncat(pcWriteBuffer, "\r\n", strlen("\r\n"));/* There might be more parameters to return after this one. */xReturn = pdTRUE;uxParameterNumber++;}else{/* No more parameters were found.  Make sure the write buffer doesnot contain a valid string. */pcWriteBuffer[0] = 0x00;/* No more data to return. */xReturn = pdFALSE;/* Start over the next time this command is executed. */uxParameterNumber = 0;}}return xReturn;
}
/*-----------------------------------------------------------*/void vRegisterSampleCLICommands(void)
{/* Register all the command line commands defined immediately above. */FreeRTOS_CLIRegisterCommand(&xParameterEcho);FreeRTOS_CLIRegisterCommand(&xATCommand);}

1. AT 命令

static BaseType_t prvATCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
static const CLI_Command_Definition_t xATCommand =
{"AT","\r\nATcmd:\r\n +RST +MODE=0/1 +M=1/2,0~180\r\n",prvATCommand, /* The function to run. */0              /* The user can enter any number of commands. */
};
  • xATCommand 是一个 CLI_Command_Definition_t 结构体,定义了一个名为 AT 的命令。
  • 第一个参数是命令名称 "AT"
  • 第二个参数是该命令的帮助信息,用于显示在命令行上。
  • 第三个参数是该命令的处理函数 prvATCommand
  • 第四个参数表示用户可以输入任意数量的该命令。

2. AT 命令处理函数

static BaseType_t prvATCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString)
{BaseType_t xReturn;char *p = pcCommandString[2];ATcmdAnalyse(p);strncat(pcWriteBuffer, "\r\nOK", strlen("\r\nOK"));xReturn = pdFALSE;return xReturn;
}
  • prvATCommand 是处理 AT 命令的函数。
  • 它接收三个参数:pcWriteBuffer(写缓冲区),xWriteBufferLen(写缓冲区长度)和 pcCommandString(命令字符串)。
  • 函数将命令字符串的第三个字符传递给 ATcmdAnalyse 函数进行分析处理。
  • 处理完毕后,将字符串 "\r\nOK" 追加到写缓冲区中,并返回 pdFALSE

 3. Echo 命令

static BaseType_t prvParameterEchoCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);static const CLI_Command_Definition_t xParameterEcho =
{"Echo","\r\nEcho <...>:\r\n Take variable number of parameters, echos each in turn\r\n",prvParameterEchoCommand, /* The function to run. */-1                        /* The user can enter any number of commands. */
};
  • xParameterEcho 是一个 CLI_Command_Definition_t 结构体,定义了一个名为 Echo 的命令。
  • 第一个参数是命令名称 "Echo"
  • 第二个参数是该命令的帮助信息,用于显示在命令行上。
  • 第三个参数是该命令的处理函数 prvParameterEchoCommand
  • 第四个参数表示用户可以输入任意数量的该命令。

4. Echo 命令处理函数 

static BaseType_t prvParameterEchoCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString)
{const char *pcParameter;BaseType_t xParameterStringLength, xReturn;static UBaseType_t uxParameterNumber = 0;// 省略部分代码...return xReturn;
}
  • prvParameterEchoCommand 是处理 Echo 命令的函数。
  • 它接收三个参数:pcWriteBuffer(写缓冲区),xWriteBufferLen(写缓冲区长度)和 pcCommandString(命令字符串)。
  • 函数通过调用 FreeRTOS_CLIGetParameter 逐个获取命令参数,并将其逐个写入写缓冲区中。
  • 如果没有更多参数,返回 pdFALSE

5. 注册命令函数

void vRegisterSampleCLICommands(void)
{FreeRTOS_CLIRegisterCommand(&xParameterEcho);FreeRTOS_CLIRegisterCommand(&xATCommand);
}
  • vRegisterSampleCLICommands 函数用于注册以上定义的两个命令。
  • 调用 FreeRTOS_CLIRegisterCommand 函数注册 EchoAT 命令。

这些命令通过 FreeRTOS 的 CLI 模块注册,可以在命令行界面中使用。

这段代码实现了一个简单的命令行接口(CLI),允许通过注册命令的方式来扩展功能。主要包括命令注册、命令处理和帮助命令的实现。

/* Standard includes. */
#include <string.h>
#include <stdint.h>/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"/* Utils includes. */
#include "FreeRTOS_CLI.h"typedef struct xCOMMAND_INPUT_LIST
{const CLI_Command_Definition_t *pxCommandLineDefinition;struct xCOMMAND_INPUT_LIST *pxNext;
} CLI_Definition_List_Item_t;/** The callback function that is executed when "help" is entered.  This is the* only default command that is always present.*/
static BaseType_t prvHelpCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);/** Return the number of parameters that follow the command name.*/
static int8_t prvGetNumberOfParameters(const char *pcCommandString);/* The definition of the "help" command.  This command is always at the front
of the list of registered commands. */
static const CLI_Command_Definition_t xHelpCommand ={"Help","\r\nHelp:\r\n Lists all the registered commands\r\n",prvHelpCommand,0};/* The definition of the list of commands.  Commands that are registered are
added to this list. */
static CLI_Definition_List_Item_t xRegisteredCommands ={&xHelpCommand, /* The first command in the list is always the help command, defined in this file. */NULL		   /* The next pointer is initialised to NULL, as there are no other registered commands yet. */
};/* A buffer into which command outputs can be written is declared here, rather
than in the command console implementation, to allow multiple command consoles
to share the same buffer.  For example, an application may allow access to the
command interpreter by UART and by Ethernet.  Sharing a buffer is done purely
to save RAM.  Note, however, that the command console itself is not re-entrant,
so only one command interpreter interface can be used at any one time.  For that
reason, no attempt at providing mutual exclusion to the cOutputBuffer array is
attempted.configAPPLICATION_PROVIDES_cOutputBuffer is provided to allow the application
writer to provide their own cOutputBuffer declaration in cases where the
buffer needs to be placed at a fixed address (rather than by the linker). */
static char cOutputBuffer[configCOMMAND_INT_MAX_OUTPUT_SIZE];/*-----------------------------------------------------------*/BaseType_t FreeRTOS_CLIRegisterCommand(const CLI_Command_Definition_t *const pxCommandToRegister)
{static CLI_Definition_List_Item_t *pxLastCommandInList = &xRegisteredCommands;CLI_Definition_List_Item_t *pxNewListItem;BaseType_t xReturn = pdFAIL;/* Check the parameter is not NULL. */configASSERT(pxCommandToRegister);/* Create a new list item that will reference the command being registered. */pxNewListItem = (CLI_Definition_List_Item_t *)pvPortMalloc(sizeof(CLI_Definition_List_Item_t));configASSERT(pxNewListItem);if (pxNewListItem != NULL){taskENTER_CRITICAL();{/* Reference the command being registered from the newly createdlist item. */pxNewListItem->pxCommandLineDefinition = pxCommandToRegister;/* The new list item will get added to the end of the list, sopxNext has nowhere to point. */pxNewListItem->pxNext = NULL;/* Add the newly created list item to the end of the already existinglist. */pxLastCommandInList->pxNext = pxNewListItem;/* Set the end of list marker to the new list item. */pxLastCommandInList = pxNewListItem;}taskEXIT_CRITICAL();xReturn = pdPASS;}return xReturn;
}
/*-----------------------------------------------------------*/BaseType_t FreeRTOS_CLIProcessCommand(const char *const pcCommandInput, char *pcWriteBuffer, size_t xWriteBufferLen)
{static const CLI_Definition_List_Item_t *pxCommand = NULL;BaseType_t xReturn = pdTRUE;const char *pcRegisteredCommandString;size_t xCommandStringLength;/* Note:  This function is not re-entrant.  It must not be called from morethank one task. */if (pxCommand == NULL){/* Search for the command string in the list of registered commands. */for (pxCommand = &xRegisteredCommands; pxCommand != NULL; pxCommand = pxCommand->pxNext){pcRegisteredCommandString = pxCommand->pxCommandLineDefinition->pcCommand;xCommandStringLength = strlen(pcRegisteredCommandString);/* To ensure the string lengths match exactly, so as not to pick upa sub-string of a longer command, check the byte after the expectedend of the string is either the end of the string or a space beforea parameter. */if ((pcCommandInput[xCommandStringLength] == ' ') || (pcCommandInput[xCommandStringLength] == 0x00)){if (strncmp(pcCommandInput, pcRegisteredCommandString, xCommandStringLength) == 0){/* The command has been found.  Check it has the expectednumber of parameters.  If cExpectedNumberOfParameters is -1,then there could be a variable number of parameters and nocheck is made. */if (pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters >= 0){if (prvGetNumberOfParameters(pcCommandInput) != pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters){xReturn = pdFALSE;}}break;}}else if ((strncmp("AT", pcRegisteredCommandString, 2) == 0) && (strncmp("AT", pcCommandInput, 2) == 0)){break;}}}if ((pxCommand != NULL) && (xReturn == pdFALSE)){/* The command was found, but the number of parameters with the commandwas incorrect. */strncpy(pcWriteBuffer, "error cmd para(s).  Enter \"Help\".\r\n\r\n", xWriteBufferLen);pxCommand = NULL;}else if (pxCommand != NULL){/* Call the callback function that is registered to this command. */xReturn = pxCommand->pxCommandLineDefinition->pxCommandInterpreter(pcWriteBuffer, xWriteBufferLen, pcCommandInput);/* If xReturn is pdFALSE, then no further strings will be returnedafter this one, and	pxCommand can be reset to NULL ready to searchfor the next entered command. */if (xReturn == pdFALSE){pxCommand = NULL;}}else{/* pxCommand was NULL, the command was not found. */strncpy(pcWriteBuffer, "Command not recognised.  Enter 'help' to view a list of available commands.\r\n\r\n", xWriteBufferLen);xReturn = pdFALSE;}return xReturn;
}
/*-----------------------------------------------------------*/char *FreeRTOS_CLIGetOutputBuffer(void)
{return cOutputBuffer;
}
/*-----------------------------------------------------------*/const char *FreeRTOS_CLIGetParameter(const char *pcCommandString, UBaseType_t uxWantedParameter, BaseType_t *pxParameterStringLength)
{UBaseType_t uxParametersFound = 0;const char *pcReturn = NULL;*pxParameterStringLength = 0;while (uxParametersFound < uxWantedParameter){/* Index the character pointer past the current word.  If this is the startof the command string then the first word is the command itself. */while (((*pcCommandString) != 0x00) && ((*pcCommandString) != ' ')){pcCommandString++;}/* Find the start of the next string. */while (((*pcCommandString) != 0x00) && ((*pcCommandString) == ' ')){pcCommandString++;}/* Was a string found? */if (*pcCommandString != 0x00){/* Is this the start of the required parameter? */uxParametersFound++;if (uxParametersFound == uxWantedParameter){/* How long is the parameter? */pcReturn = pcCommandString;while (((*pcCommandString) != 0x00) && ((*pcCommandString) != ' ')){(*pxParameterStringLength)++;pcCommandString++;}if (*pxParameterStringLength == 0){pcReturn = NULL;}break;}}else{break;}}return pcReturn;
}
/*-----------------------------------------------------------*/static BaseType_t prvHelpCommand(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString)
{static const CLI_Definition_List_Item_t *pxCommand = NULL;BaseType_t xReturn;(void)pcCommandString;if (pxCommand == NULL){/* Reset the pxCommand pointer back to the start of the list. */pxCommand = &xRegisteredCommands;}/* Return the next command help string, before moving the pointer on tothe next command in the list. */strncpy(pcWriteBuffer, pxCommand->pxCommandLineDefinition->pcHelpString, xWriteBufferLen);pxCommand = pxCommand->pxNext;if (pxCommand == NULL){/* There are no more commands in the list, so there will be no morestrings to return after this one and pdFALSE should be returned. */xReturn = pdFALSE;}else{xReturn = pdTRUE;}return xReturn;
}
/*-----------------------------------------------------------*/static int8_t prvGetNumberOfParameters(const char *pcCommandString)
{int8_t cParameters = 0;BaseType_t xLastCharacterWasSpace = pdFALSE;/* Count the number of space delimited words in pcCommandString. */while (*pcCommandString != 0x00){if ((*pcCommandString) == ' '){if (xLastCharacterWasSpace != pdTRUE){cParameters++;xLastCharacterWasSpace = pdTRUE;}}else{xLastCharacterWasSpace = pdFALSE;}pcCommandString++;}/* If the command string ended with spaces, then there will have been toomany parameters counted. */if (xLastCharacterWasSpace == pdTRUE){cParameters--;}/* The value returned is one less than the number of space delimited words,as the first word should be the command itself. */return cParameters;
}
  • 注册命令

    调用 FreeRTOS_CLIRegisterCommand 函数注册新的命令。这个函数会将新的命令节点添加到链表末尾。

  • 处理命令

    调用 FreeRTOS_CLIProcessCommand 函数处理输入的命令字符串。这个函数会遍历注册的命令链表,找到匹配的命令,并调用相应的回调函数处理。

  • 帮助命令

    当输入 Help 命令时,prvHelpCommand 回调函数会被调用,输出所有注册命令的帮助信息。

 

这段代码实现了一个简单的 AT 指令解析器。

#include "CLI.h"
#include "string.h"
// "\r\nATcmd:\r\n +RST +MODE=0/1 +M=1/2,0~180\r\n",
typedef enum
{AT = 0,MODE,M,RST,/**将指令添加到上面**/MAXCMDNUM
} ATCommand;typedef enum 
{ATERROR = 0, ATSUCCESS ,ATERRORCODE1,
}ATStatus;typedef ATStatus (*pFuncCallback)(char *str);typedef struct
{ATCommand ATCommandName;char *ATStr;                    // 发送的AT指令pFuncCallback ATCallback; // AT指令接收完成,指令处理回调函数
} ATCommandConfig;ATStatus MODE_Callback(char *str);
ATStatus M_Callback(char *str);
ATStatus RST_Callback(char *str);static const ATCommandConfig ATCommandList[] ={{MODE, "MODE", MODE_Callback},{M, "M", M_Callback},{RST, "RST", RST_Callback},NULL,
};static void ATcmdGetRun(pATcmd, pATcmdLen, pATvalue)
{ATCommandConfig *pxCommand;const char *pcRegisteredCommandString;for (pxCommand = &ATCommandList[0]; pxCommand != NULL; pxCommand++){pcRegisteredCommandString = pxCommand->ATStr;if (strncmp(pATcmd, pcRegisteredCommandString, pATcmdLen) == 0){pxCommand->ATCallback(pATvalue);}}
}void ATcmdAnalyse(char *pATcmd)
{if ('+' == pATcmd[0]){pATcmd++;char *pATvalue;uint8_t pATcmdLen = 0; // ATcmd lenthpATvalue = strstr(pATcmd, "=");if (pATvalue){pATcmdLen = pATvalue - pATcmd; // ATcmd lenthpATvalue++;}else{pATvalue = NULL;pATcmdLen = strlen(pATcmd);}ATcmdGetRun(pATcmd, pATcmdLen, pATvalue);}
}/*--------------------------------------------------
In fact, the last step callback function of 
the at instruction cannot directly control the hardware. 
There should be a third intermediate layer SDK
--------------------------------------------------*/
ATStatus MODE_Callback(char *str)
{//挂起舵机的任务
}
ATStatus M_Callback(char *str)
{//Sg90MotorCtl(舵机1/2  , 舵机角度) //控制舵机运行到具体角度//内部需要对舵机能够转动的最大角度进行限位
}
ATStatus RST_Callback(char *str)
{//重启 mcu
}

让我们逐步解释它的功能和结构:

  1. 枚举类型定义:

    • ATCommand 枚举定义了一组 AT 指令的名称,包括 ATMODEMRST,以及一个特殊的枚举 MAXCMDNUM,用于表示指令数量上限。
  2. AT 状态枚举:

    • ATStatus 枚举定义了一组 AT 操作的状态,包括成功、失败以及可能的其他错误码。
  3. 函数指针定义:

    • pFuncCallback 是一个函数指针类型,指向一个接受 char* 参数并返回 ATStatus 类型的函数。
  4. AT 指令配置结构体:

    • ATCommandConfig 结构体定义了一个 AT 指令的配置,包括指令名称、发送的指令字符串以及指令完成时的回调函数。
  5. AT 指令配置列表:

    • ATCommandList 是一个数组,存储了所有 AT 指令的配置信息,包括指令名称、发送的指令字符串和回调函数。列表以 NULL 结尾。
  6. AT 指令解析函数:

    • ATcmdAnalyse 函数用于解析接收到的 AT 指令。它接收一个指向 AT 指令字符串的指针,并根据指令名称调用相应的回调函数。
  7. AT 指令回调函数:

    • MODE_CallbackM_CallbackRST_Callback 是对应于不同指令的回调函数,用于执行具体的操作。这些函数会根据指令参数进行不同的处理。
  8. AT 指令执行函数:

    • ATcmdGetRun 函数根据接收到的 AT 指令名称,在配置列表中查找对应的配置,并调用相应的回调函数执行操作。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/22915.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

06Docker-Compose和微服务部署

Docker-Compose 概述 Docker Compose通过一个单独的docker-compose.yml模板文件来定义一组相关联的应用容器&#xff0c;帮助我们实现多个相互关联的Docker容器的快速部署 一般一个docker-compose.yml对应完整的项目,项目中的服务和中间件对应不同的容器 Compose文件实质就…

锂电池寿命预测 | Matlab基于SSA-SVR麻雀优化支持向量回归的锂离子电池剩余寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 【锂电池剩余寿命RUL预测案例】 锂电池寿命预测 | Matlab基于SSA-SVR麻雀优化支持向量回归的锂离子电池剩余寿命预测&#xff08;完整源码和数据&#xff09; 1、提取NASA数据集的电池容量&#xff0c;以历史容量作…

【C++课程学习】:类和对象(上)(类的基础详细讲解)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;C课程学习 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 &#x1f35f;1.1类的引出&#xff1a; &#x1f35f;1.2类的结构&#xff1a; &#x1f35f;1.3类的…

LeetCode-82. 删除排序链表中的重复元素 II【链表 双指针】

LeetCode-82. 删除排序链表中的重复元素 II【链表 双指针】 题目描述&#xff1a;解题思路一&#xff1a;用一个cur即可实现去重cur.next cur.next.next背诵版&#xff1a;解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个已排序的链表的头 head &#xff0c; 删除原始…

十大排序-冒泡排序

算法原理如下&#xff1a; 给出一组数据&#xff1b;比较相邻的元素。如果第一个比第二个大&#xff0c;互换两个值。对每一组相邻元素同样方式比较&#xff0c;从开始的第一组到结束的最后一组。最后的元素会是最大数。除了排列好的最大数&#xff0c;针对所有元素重复以上步…

前端应用开发实验:组件应用

目录 实验目的相关知识点实验内容及要求代码实现效果 实验目的 &#xff08;1&#xff09;掌握组件的创建方法&#xff08;全局组件、局部组件&#xff09;&#xff1b; &#xff08;2&#xff09;重点学会组件之间的数据传递&#xff08;prop传值、自定义事件&#xff09;&am…

SAP 用事务码SQVI 制作简单的ALV报表

我们在项目实施和运维的过程中经常会接到用户的很多需求&#xff0c;有很大的一部分需求可能都是一些报表的需求&#xff0c;有些报表的需求需要开发人员使用ABAP编写&#xff0c;但是有些报表仅仅只是两个或者多个报表的表关联就可以实现。这个时候我们就可以用SQVI这个事物代…

揭秘!宠物空气净化器对抗猫毛过敏,效果真的超乎想象?

猫毛过敏困扰着不少爱猫人士。尽管网络上充斥着各种缓解策略&#xff0c;但究竟哪种方法效果最佳&#xff1f;作为一位经验丰富的宠物主人&#xff0c;我搜集了大量信息&#xff0c;对比了几种主流的猫毛过敏应对策略&#xff0c;比如药物治疗、日常清洁和宠物空气净化器的使用…

阿里云私有CA使用教程

点击免费生成 根CA详情 启用根CA -----BEGIN CERTIFICATE----- MIIDpzCCAogAwIBAgISBZ2QPcfDqvfI8fqoPkOq6AoMA0GCSqGSIb3DQEBCwUA MFwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdiZWlqaW5nMRAwDgYDVQQHDAdiZWlq aW5nMQ0wCwYDVQQKDARDU0REMQ0wCwYDVQQLDARDU0REMQswCQYDVQQDDAJDTjA…

单列集合--ArryList、LinkedList、Set

使用IDEA进入某个类之后&#xff0c;按ctrlF12,或者alt数字7&#xff0c;可查看该实现类的大纲。 package exercise;import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.function.Consumer;public class Demo3 {public static void…

开放式耳机哪个牌子好?2024年度热门机型推荐榜单分享!

随着音乐技术的不断革新&#xff0c;开放式耳机已成为音乐发烧友们的首选。从最初的简单音质&#xff0c;到如今的高清解析&#xff0c;开放式耳机不断进化。音质纯净&#xff0c;佩戴舒适&#xff0c;无论是街头漫步还是家中细细静听&#xff0c;都能带给你身临其境的音乐体验…

iOS18 新变化提前了解,除了AI还有这些变化

iOS 18即将在不久的将来与广大iPhone用户见面&#xff0c;这次更新被普遍认为是苹果历史上最重要的软件更新之一。据多方报道和泄露的消息&#xff0c;iOS 18将带来一系列全新的功能和改进&#xff0c;包括在人工智能领域的重大突破、全新的设计元素以及增强的性能和安全性。现…

AI教我变得厉害的思维模式01 - 成长型思维模式

今天和AI一起思考如何培养自己的成长性思维。 一一核对&#xff0c;自己哪里里做到&#xff0c;哪里没有做到&#xff0c;让AI来微调训练我自己。 成长性思维的介绍 成长性思维&#xff08;Growth Mindset&#xff09;是由斯坦福大学心理学教授卡罗尔德韦克&#xff08;Carol…

钡铼技术BL103助力实现PLC到OPC-UA无缝转换新高度

在工业4.0的大背景下&#xff0c;信息物理系统和工业物联网的融合日益加深&#xff0c;推动了工业自动化向更高层次的发展。OPC UA作为一种开放、安全、跨平台的通信协议&#xff0c;在实现不同设备、系统间数据交换和互操作性方面扮演了核心角色。钡铼技术公司推出的BL103 PLC…

调用讯飞星火API实现图像生成

目录 1. 作者介绍2. 关于理论方面的知识介绍3. 关于实验过程的介绍&#xff0c;完整实验代码&#xff0c;测试结果3.1 API获取3.2 代码解析与运行结果3.2.1 完整代码3.2.2 运行结果 3.3 界面的编写&#xff08;进阶&#xff09; 4. 问题分析5. 参考链接 1. 作者介绍 刘来顺&am…

Vitis HLS 学习笔记--通道的FIFO/PIPO选择

目录 1. 简介 2. 代码详解 2.1 FIFO 通道示例 2.1.1 配置默认通道 2.1.2 kernel 代码 2.1.3 综合报告 2.1.4 depth 32 解析 2.1.5 FIFO 通道分类 2.2 PIPO 2.2.1 配置默认通道 2.2.2 kernel 代码 2.2.3 综合报告 2.2.4 PIPO 通道分类 3. 综合对比 3.1 数据类…

2024年带你揭秘FL Studio 21破解版,2024年最新FL21内置汉化破解补丁

截止目前&#xff0c;FL Studio最新版是FL Studio 21.2.3.4004版本&#xff0c;想必很多朋友已经迫不及待了&#xff0c;那么今天这篇文章我将带大家详细的介绍FL Studio 21.2.3 Build 4004新特点以及如何下载&#xff0c;安装和激活。 PS.本次为你带来的是fl studio21破解版&a…

【python】Modulenotfounderror: no module named ‘open_clip’

成功解决“ModuleNotFoundError: No module named ‘open_clip’”错误的全面指南 在Python编程中&#xff0c;如果你遇到了“ModuleNotFoundError: No module named ‘open_clip’”这个错误&#xff0c;它意味着你的Python环境中没有安装名为open_clip的模块&#xff0c;或者…

grep、sed、awk

grep&#xff1a;文本过滤工具 sed: 文本编辑工具 awk: 格式化文本 grep -n 显示行号 -i 忽略大小写 -v 取反 -o 只保留关键消息 # 找出文件的空行 grep ^$ test.txt -n # 找出文件非空行内容 grep ^$ test.txt -n -v # 找出文件非空行内容&#xff0c;并且排除注释&#xff…

8个免费下载音乐的网站,建议收藏!

1、My Free MP3 tools.liumingye.cn/music/ 一个好用且免费的在线音乐播放和下载网站&#xff0c;几乎收录了所有国内外大火的歌手和歌曲&#xff0c;可以通过歌手列表找单曲&#xff0c;也可以直接搜索歌手或歌曲名&#xff0c;下面还有一些热门搜索&#xff0c;可以直接播放…