手擼 串口交互命令行 及 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.pswp.cn/web/22915.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/22915.shtml
英文地址,請注明出處:http://en.pswp.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; 刪除原始…

【java前端課堂】02_類和方法的定義區別

目錄 簡介&#xff1a; 類 方法 類和方法之間的主要區別如下&#xff1a; 定義與結構&#xff1a; 實例化&#xff1a; 作用范圍&#xff1a; 生命周期&#xff1a; 下面是一個簡單的Java類和方法示例&#xff1a; 簡介&#xff1a; 類 在Java&#xff08;以及許多其他面向…

十大排序-冒泡排序

算法原理如下&#xff1a; 給出一組數據&#xff1b;比較相鄰的元素。如果第一個比第二個大&#xff0c;互換兩個值。對每一組相鄰元素同樣方式比較&#xff0c;從開始的第一組到結束的最后一組。最后的元素會是最大數。除了排列好的最大數&#xff0c;針對所有元素重復以上步…

臺式機ubuntu22.04安裝nvidia驅動

總結一個極簡易的安裝方法 正常安裝ubuntu 22.04正常更新軟件 sudo apt update sudo apt upgrade -y參考ubuntu官方網站的說明https://ubuntu.com/server/docs/nvidia-drivers-installation#/ # 首先檢查系統支持驅動的版本號 sudo ubuntu-drivers list我顯示的內容如下&…

前端應用開發實驗:組件應用

目錄 實驗目的相關知識點實驗內容及要求代碼實現效果 實驗目的 &#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;包括在人工智能領域的重大突破、全新的設計元素以及增強的性能和安全性。現…

make 中 DESTDIR 和 --prefix 的區別

1.configure + make./configure --prefix=/usr make -j8make install DESTDIR=/home2.meson + ninja meson build --prefix=/usr DESTDIR=/home ninja install 這兩個示例分別展示了如何使用兩種流行的構建系統(configure + make 和 meson + ninja)來編譯和安裝軟件,并…

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 數據類…

docker安裝及常見命令

歷史版本docker下載 https://docs.docker.com/desktop/release-notes/#upgrades-17 docker start 容器id # 啟動容器 docker restart 容器id # 重啟容器 docker stop 容器id # 停止當前運行的容器 docker kill 容器id # 強制停止當前容器…