????????在嵌入式系統開發中,命令行接口(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(命令行接口)命令:Echo
和 AT
。
/* 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
函數注冊Echo
和AT
命令。
這些命令通過 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
}
讓我們逐步解釋它的功能和結構:
-
枚舉類型定義:
ATCommand
枚舉定義了一組 AT 指令的名稱,包括AT
、MODE
、M
和RST
,以及一個特殊的枚舉MAXCMDNUM
,用于表示指令數量上限。
-
AT 狀態枚舉:
ATStatus
枚舉定義了一組 AT 操作的狀態,包括成功、失敗以及可能的其他錯誤碼。
-
函數指針定義:
pFuncCallback
是一個函數指針類型,指向一個接受char*
參數并返回ATStatus
類型的函數。
-
AT 指令配置結構體:
ATCommandConfig
結構體定義了一個 AT 指令的配置,包括指令名稱、發送的指令字符串以及指令完成時的回調函數。
-
AT 指令配置列表:
ATCommandList
是一個數組,存儲了所有 AT 指令的配置信息,包括指令名稱、發送的指令字符串和回調函數。列表以NULL
結尾。
-
AT 指令解析函數:
ATcmdAnalyse
函數用于解析接收到的 AT 指令。它接收一個指向 AT 指令字符串的指針,并根據指令名稱調用相應的回調函數。
-
AT 指令回調函數:
MODE_Callback
、M_Callback
和RST_Callback
是對應于不同指令的回調函數,用于執行具體的操作。這些函數會根據指令參數進行不同的處理。
-
AT 指令執行函數:
ATcmdGetRun
函數根據接收到的 AT 指令名稱,在配置列表中查找對應的配置,并調用相應的回調函數執行操作。
?
?