当前位置: 首页 > news >正文

手撸 串口交互命令行 及 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 指令名称,在配置列表中查找对应的配置,并调用相应的回调函数执行操作。

 

 

相关文章:

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

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

Redis几种部署模式介绍

Redis 提供了几种不同的部署模式&#xff0c;以满足不同的使用场景和可用性需求。这些模式包括单机模式、主从复制、哨兵模式和集群模式。下面我将简要介绍每种模式的特点和用途&#xff1a; 单机模式&#xff1a; 描述&#xff1a;单个 Redis 服务器实例运行在一台机器上&…...

【STM32HAL库学习】定时器功能、时钟以及各种模式理解

一、文章目的 记录自己从学习了定时器理论->代码实现使用定时->查询数据手册&#xff0c;加深了对定时器的理解以及该过程遇到了的一些不清楚的知识。 上图为参考手册里通用定时器框图&#xff0c;关于定时器各种情况的工作都在上面了&#xff0c;在理论学习和实际应用后…...

3588麒麟系统硬解码实战

目录 安装rockchip-mpp deb 查找头文件 .pro文件添加 检查库是否已安装 error: stdlib.h: No such file or directory ffmpeg 查找ffmpeg路径: 查找FFmpeg库和头文件的位置 使用pkg-config工具查找FFmpeg路径 ok的ffmpeg配置: ffmpeg查看是否支持libx264 ffmpeg …...

十二 nginx中location重写和匹配规则

十二 location匹配规则 ^~ ~ ~* !~ !~* /a / 内部服务跳转 十三 nginx地址重写rewrite if rewrite set return 13.1 if 应用环境 server location -x 文件是否可执行 $args $document_rot $host $limit_rate $remote_addr $server_name $document_uri if …...

python的视频处理FFmpeg库使用

FFmpeg 是一个强大的多媒体处理工具,用于录制、转换和流式传输音频和视频。它支持几乎所有的音频和视频格式,并且可以在各种平台上运行。FFmpeg 在 Python 中的使用可以通过调用其命令行工具或使用专门的库如 ffmpeg-python。以下是详细介绍如何在 Python 中使用 FFmpeg,包括…...

接口测试时, 数据Mock为何如此重要?

一、为什么要mock 工作中遇到以下问题&#xff0c;我们可以使用mock解决&#xff1a; 1、无法控制第三方系统某接口的返回&#xff0c;返回的数据不满足要求 2、某依赖系统还未开发完成&#xff0c;就需要对被测系统进行测试 3、有些系统不支持重复请求&#xff0c;或有访问…...

未授权与绕过漏洞

1、Laravel Framework 11 - Credential Leakage&#xff08;CVE-2024-29291&#xff09;认证泄漏 导航这个路径storage/logs/laravel.log搜索以下信息&#xff1a; PDO->__construct(mysql:host 2、 Flowise 1.6.5 - Authentication Bypass&#xff08;CVE-2024-31621&am…...

云原生周刊:Kubernetes 十周年 | 2024.6.11

开源项目推荐 Kubernetes Goat Kubernetes Goat 是一个故意设计成有漏洞的 Kubernetes 集群环境&#xff0c;旨在通过交互式实践场地来学习并练习 Kubernetes 安全性。 kube-state-metrics (KSM) kube-state-metrics 是一个用于收集 Kubernetes 集群状态信息的开源项目&…...

ClickHouse内幕(1)数据存储与过滤机制

本文主要讲述ClickHouse中的数据存储结构&#xff0c;包括文件组织结构和索引结构&#xff0c;以及建立在其基础上的数据过滤机制&#xff0c;从Part裁剪到Mark裁剪&#xff0c;最后到基于SIMD的行过滤机制。 数据过滤机制实质上是构建在数据存储格式之上的算法&#xff0c;所…...

1.Mongodb 介绍及部署

MongoDB 是一个开源的文档导向数据库&#xff0c;采用NoSQL&#xff08;非关系型数据库&#xff09;的设计理念。MongoDB是一个基于分布式文件存储的数据库。 分布式文件存储是一种将文件数据分布式的存储在多台计算机上。MongoDB是一款强大的文档导向数据库&#xff0c;适合处…...

Java 技巧:如何获取字符串中最后一个英文逗号后面的内容

在日常的Java编程中&#xff0c;处理字符串是非常常见的任务之一。有时我们需要从一个字符串中截取特定部分&#xff0c;例如获取最后一个英文逗号后的内容。这篇文章将详细介绍如何使用Java来实现这一需求&#xff0c;并提供一个示例代码来演示其实现过程。 需求分析 假设我们…...

玩转微服务-GateWay

目录 一. 背景二. API网关1. 概念2. API网关定义3. API网关的四大职能4. API网关分类5. 开源API网关介绍6. 开源网关的选择 三. Spring Cloud Gateway1. 文档地址2. 三个核心概念3. 工作流程4. 运行原理4.1 路由原理4.2 RouteLocator 5. Predicate 断言6. 过滤器 Filter6.1. 过…...

Amortized bootstrapping via Automorphisms

参考文献&#xff1a; [MS18] Micciancio D, Sorrell J. Ring packing and amortized FHEW bootstrapping. ICALP 2018: 100:1-100:14.[GPV23] Guimares A, Pereira H V L, Van Leeuwen B. Amortized bootstrapping revisited: Simpler, asymptotically-faster, implemented. …...

【人工智能】ChatGPT基本工作原理

ChatGPT 是由 OpenAI 开发的一种基于深度学习技术的自然语言处理模型&#xff0c;它使用了名为 GPT&#xff08;Generative Pre-trained Transformer&#xff09;的架构。GPT 模型是一种基于 Transformer 架构的预训练语言模型&#xff0c;它通过大量的文本数据进行预训练&…...

The First项目报告:Stargate Finance重塑跨链金融的未来

Stargate Finance是一个基于LayerZero协议的去中心化金融平台&#xff0c;自2022年3月由LayerZero Labs创建以来&#xff0c;一直致力于为不同区块链之间的资产转移提供高效、低成本的解决方案。凭借其独特的跨链技术和丰富的DeFi服务&#xff0c;Stargate Finance已成为连接不…...

Python魔法之旅-魔法方法(22)

目录 一、概述 1、定义 2、作用 二、应用场景 1、构造和析构 2、操作符重载 3、字符串和表示 4、容器管理 5、可调用对象 6、上下文管理 7、属性访问和描述符 8、迭代器和生成器 9、数值类型 10、复制和序列化 11、自定义元类行为 12、自定义类行为 13、类型检…...

公司面试题总结(三)

13.说说你对 BOM 的理解&#xff0c;常见的 BOM 对象你了解哪些&#xff1f; BOM (Browser Object Model)&#xff0c;浏览器对象模型&#xff0c; ⚫ 提供了独立于内容与浏览器窗口进行交互的对象 ⚫ 其作用就是跟浏览器做一些交互效果 ⚫ 比如如何进行页面的后退&…...

PLSQL 报错 could not locate oci.dll

0、确保PLSQL已激活。 1、在PLSQL安装包内搜索oci.dll&#xff0c;如果没有搜到需要下载 链接&#xff1a;https://pan.baidu.com/s/1HOfKAEFfuAGYACjfcwqJ1g 提取码&#xff1a;6evh 2、打开PLSQL&#xff0c;设置oci.dll的路径 ps&#xff1a;PLSQL安装包 链接&#xff…...

【方案+源码】智慧园区建设方案

智慧园区一体化运营管理平台建设方案旨在通过集成先进的信息技术&#xff0c;实现园区的智能化、高效化、绿色化管理。该平台整合了物联网、大数据、云计算等技术&#xff0c;为园区提供全方位、一体化的运营服务。 方案包括智能监控、能源管理、安防系统、停车管理、物业管理等…...

Java操作数据库 —— JDBC ① 基础篇

我走我的路&#xff0c;有人拦也走&#xff0c;没人陪也走 —— 24.6.7 JDBC JDBC就是使用Java语言操作关系型数据库的一套API 一、JDBC简介 JDBC 概念 JDBC 就是使用Java语言操作关系型数据库的一套API 全称:(Java DataBase Connectivity)意为Java 数据库连接 JDBC 本质: ①…...

webpack和vite区别

一、Webpack 1. 概述 Webpack 是一个模块打包工具&#xff0c;它会递归地构建依赖关系图&#xff0c;并将所有模块打包成一个或多个bundle&#xff08;包&#xff09;。 2. 特点 配置灵活&#xff1a;Webpack提供了高度可定制的配置文件&#xff0c;可以根据项目需求进行各…...

FL Studio21永久免费破解中文版下载,让我这个音乐制作爱好者如获至宝!

FL Studio21永久免费破解中文版下载&#xff0c;让我这个音乐制作爱好者如获至宝&#xff01;&#x1f3b6; 这款软件功能强大&#xff0c;操作简单易上手。我可以轻松地创作出各种风格的音乐作品。无论是流行、摇滚还是电子音乐&#xff0c;都能轻松驾驭。&#x1f3a7; 使用F…...

vue3 监听器,组合式API的watch用法

watch函数 在组合式 API 中&#xff0c;我们可以使用 watch 函数在每次响应式状态发生变化时触发回调函数 watch(ref,callback&#xff08;newValue,oldValue&#xff09;&#xff0c;option:{}) ref:被监听的响应式量&#xff0c;可以是一个 ref (包括计算属性)、一个响应式…...

苹果WWDC开幕发布AI大模型,股价却跌近2%

KlipC报道&#xff1a;北京时间6月11日凌晨&#xff0c;苹果一年一度的“全球开发者大会”&#xff08;WWDC&#xff09;开幕。会上&#xff0c;先后介绍了iOS 18、iPadOS 18、watchOS 11等系统的更新&#xff0c;同时还展示了多个AI功能。宣布与OpenAI构建合作伙伴关系。然而&…...

C++ 11 【可变参数模板】【lambda】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C修炼之路⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 目录 前言 一、新的类功能 1.1默认成员函数—…...

c 宏应用举例

1.概要 #include <iostream> //变量可以直接使用 #define fun() a 100; //用变量计算可以 #define fun2(a) a*2; //用变量替换可以 #define fun3(a) d[a] a; //##链接的作用&#xff0c;一般用于链接变量名 #define fun4(type,name) type name##_s 4; //#的作用是转换…...

微信公众号(公众平台) 和 微信开放平台的scope的差异

微信公众号&#xff08;公众平台&#xff09; 和 微信开放平台 是两码事。 公众号&#xff08;公众平台&#xff09;获取的scope只包括两种&#xff1a;snsapi_base 和snsapi_userinfo&#xff0c;前者是静默获取&#xff0c;用户无感知&#xff1b;后者是需要用户确认同意的。…...

基于pytorch实现的DenseUnet医学图像分割(腹部多脏器)

1、前言 本章将介绍将densenet的主干网络引入unet中 官方实现的代码&#xff1a;kits19-challenge/network at master nitsaick/kits19-challenge (github.com) 本章实现的项目目录如下&#xff1a; 主要代码有train、evaluate、predict脚本 2、代码介绍 数据预处理脚本 数据…...

富格林:正规策划实现安全做单

富格林悉知&#xff0c;在投资理财的过程中&#xff0c;最重要的是控制风险实现安全做单避免损失。但是市场客观因素带来的风险并不能完全避免&#xff0c;因此投资者需要采取一些正规技能来减低风险投资风险实现安全做单。接下来就由富格林给大家分享一些实现安全做单的正规方…...

新公司名字注册查询/教程推广优化网站排名

使用领域模型&#xff0c;很少会像创建实际领域模型类、然后使用它们那么简单。很快你就会发现&#xff0c;领域模型必须得到相当数量的基础架构代码的支持。 领域模型所需基础架构当中最显著的当然是持久化——通常是持久化到关系型数据库中&#xff0c;也就是对象/关系&#…...

网站文章更新怎么通知搜索引擎/百度推广效果怎么样

Golang 支持在一个平台下生成多个平台运行包编译之后直接可执行&#xff0c;使用起来非常方便1.Mac Mac下编译Linux, Windows平台的64位可执行程序&#xff1a;CGO_ENABLED0 GOOSlinux GOARCHamd64 go build test.go CGO_ENABLED0 GOOSwindows GOARCHamd64 go build test.go2.L…...

wordpress python api/查网站域名

Java语言的执行模式是()&#xff1f;更多相关问题反馈控制系统是指系统中有()。A、反馈回路B、惯性环节C、积分环节D、PID调节器肾锥体为()A.强回声B.等回声C.低回声D.较低回声E.无回声给图形添加栅格的命令是()。A、figureB、holdoffC、holdonD、gridMATLAB的主要窗口包括哪些…...

网站推广塔山双喜/网址缩短在线生成器

第1部分初入职场——程序员的职场成长初入社会的人都青涩懵懂。我们将利用今后几十年的时光去理解“职场”两个字的价值和意义。拥有好的开始&#xff0c;我们就等于拥有职场成功的一把金钥匙。第1章大学生活应该这样度过就像不能在大四下半学期才开始找工作一样&#xff0c;职…...

wordpress 防火墙/百度百科词条

a转载于:https://www.cnblogs.com/menggucaoyuan/archive/2013/04/23/3036903.html...

网页设计与网站建设项目教程/成都百度推广开户公司

网络通信、文件存储中经常需要交换数据&#xff0c;为了减少网络通信流量、文件存储大小以及加密通信规则&#xff0c;经常需要对数据进行双向加解密以保证数据的安全。PHP中实现此功能主要需要使用的函数主要是pack及unpack函数pack压缩资料到位字符串之中。语法: string pack…...