- 這是一個針對 STM32/GD32/AT32 等晶片的 AT 命令解析工具。其主要功能是從串列埠接收以 “\r\n” 結尾的命令資料,並將其解析為對應的命令,然後根據命令對映表呼叫相應的處理函式來執行特定操作。
1. 使用方式
-
將串列埠的資料放入
bsp_parseCommand
函式,並且傳入陣列和資料長度,此函式會自動儲存傳入的資料,所以不需要一次傳入一包完整的資料。如果資料超出緩衝區大小,會自動清零。 -
修改命令對映表,將需要解析的命令字串和命令的回撥函式傳入,在匹配到字串之後就會自動呼叫回撥函式
CommandMapping commandMap[MAX_COMMAND_COUNT] = { {"AT+COMMAND1", handleATCommand1}, {"AT+COMMAND2", handleATCommand2}, {"AT+RESET", handleATReset}, // 檢視韌體資訊和重啟原因 {"AT+FirmwareInfo", PrintFirmwareInfoC}, // 檢視任務資訊 {"AT+TaskInfo", PrintTaskInfo}, {NULL, NULL} // 結束標記 };
-
如果需要修改命令長度的緩衝區,請修改
MAX_COMMAND_LENGTH
的定義 -
如果需要修改命令對映表陣列的大小,請修改
MAX_COMMAND_COUNT
。 -
可以在匹配的字串後新增引數,在回撥中可自行解析。具體看下方程式碼
2. 執行流程
- 資料接收函式
- 進入條件:從串列埠接收到字元資料。
- 狀態判斷:每次接收到一個字元後,會進行判斷。
- 如果接收到的字元是
\n
或者\r
,且緩衝區索引bufferIndex
大於 0,意味著接收到了可能完整的命令(以\r\n
結尾),此時進入 “命令解析執行狀態”。 - 如果接收到的字元不是
\r
或\n
,並且緩衝區索引bufferIndex
小於MAX_COMMAND_LENGTH - 1
,則將該字元存入命令緩衝區commandBuffer
,並將bufferIndex
加 1,繼續保持在 “資料接收狀態” 等待下一個字元輸入。 - 如果接收到的字元不是
\r
或\n
,但緩衝區索引bufferIndex
已經達到MAX_COMMAND_LENGTH - 1
,表示命令緩衝區即將溢位,此時輸出 “Command buffer overflow” 提示資訊,然後將bufferIndex
重置為 0,繼續等待新的字元輸入,仍處於 “資料接收狀態”。
- 如果接收到的字元是
- 命令解析函式:
- 進入條件:在 “資料接收狀態” 下接收到
\n
或者\r
且緩衝區有有效資料(bufferIndex
> 0)。 - 狀態描述:
- 首先將命令緩衝區
commandBuffer
中的字元序列以\0
結尾,形成完整的字串形式的命令。 - 然後透過
parseAndExecuteCommand
函式遍歷命令對映表commandMap
,對比接收到的命令字串與對映表中的命令。- 如果找到匹配的命令,輸出 “Executing command: [具體命令]” 資訊,並呼叫對應的處理函式執行相關操作,之後進入 “初始狀態”,等待下一輪的串列埠資料輸入。
- 如果遍歷完整個命令對映表都沒有找到匹配的命令,輸出 “Unknown command: [具體命令]” 資訊,然後進入 “初始狀態”,繼續等待新的串列埠資料輸入。
- 首先將命令緩衝區
- 進入條件:在 “資料接收狀態” 下接收到
3. 程式碼例程
/*************************************************************************************************************************
* CopyRight: PlayerPencil FreeRtos通用庫/不使用作業系統也可以使用
* Filename: bsp_command.c
* ContentIntroduction:
* 該檔案實現了從串列埠接收 \r\n 結尾的命令,並根據命令呼叫相應的處理函式
* CommandMapping commandMap[MAX_COMMAND_COUNT],為命令對映表,用於儲存命令和處理函式的對映關係
* 在呼叫命令之後,也會把匹配到的字串傳入,可以匹配後面的引數。如:
* const char *volumeStr = command + strlen("AT+MP3Volume=");
* uint8_t volume = atoi(volumeStr);volume就為需要的值
* 使用者呼叫bsp_parseCommand,可以傳入1個或者多個字元,當檢測到 \r\n 時,會呼叫相應的處理函式。裡面有一個緩衝區
* 根據 MAX_COMMAND_LENGTH 來設定,MAX_COMMAND_COUNT為最大的命令數量-1.
*
*
* FileHistory:
* VersionNumber Date: Author: Content:
* 01a 2024-11-17 PlayerPencil 建立了該檔案,完成了基礎功能的測試
*
*
*
*
*****************************************************************************************************************************/
#include "bsp_command.h"
#include <string.h>
#include "MP3_Player.h"
#define MAX_COMMAND_LENGTH 20
#define MAX_COMMAND_COUNT 15
// 命令緩衝區
static char commandBuffer[MAX_COMMAND_LENGTH];
static uint8_t bufferIndex = 0;
void handleATCommand1(const char *command) {
printf("Handling AT+COMMAND1: %s\n", command);
}
void handleATCommand2(const char *command) {
printf("Handling AT+COMMAND1: %s\n", command);
}
void handleATReset(const char *command) {
// 系統復位
printf("Handling AT+RESET: %s\n", command);
NVIC_SystemReset();
}
void PrintTaskInfo(const char *command) {
// 動態分配記憶體
char *taskListBuffer = (char *)pvPortMalloc(512);
printf("\r\n");
printf("\r\n*************** Task Information ***************\r\n");
if (taskListBuffer == NULL) {
printf("Failed to allocate memory for task list buffer\n");
return;
}
size_t freeHeapSize = xPortGetFreeHeapSize();
size_t minEverFreeHeapSize = xPortGetMinimumEverFreeHeapSize();
vTaskList(taskListBuffer);
printf("TaskName TaskState Priority stack TaskNum\r\n");
printf("%s", taskListBuffer);
printf("\r\n");
DEBUG_PRINTF(LEVEL_DEBUG, "Current free heap size: %u bytes\n", (unsigned int)freeHeapSize);
DEBUG_PRINTF(LEVEL_DEBUG, "Minimum ever free heap size: %u bytes\n", (unsigned int)minEverFreeHeapSize);
printf("*************** Task Information ***************\r\n");
printf("\r\n");
vPortFree(taskListBuffer);
}
void PrintFirmwareInfoC(const char *command)
{
PrintFirmwareInfo();
}
void handleATMP3Command(const char *command) {
// 提取歌曲名
const char *songName = command + strlen("AT+MP3=");
// printf("Playing song: %s\n", songName);
// 在這裡新增播放歌曲的程式碼
// playSong(songName);
if (!MP3_Play(songName)) {
// printf("Failed to play song: %s\n", songName);
DEBUG_PRINTF(LEVEL_ERROR, "Failed to play song: %s\n", songName);
}else{
// printf("Playing song: %s\n", songName);
DEBUG_PRINTF(LEVEL_INFO, "Playing song: %s\n", songName);
}
}
void handleATMP3_Pause(const char *command) {
DEBUG_PRINTF(LEVEL_INFO, "MP3 Pause\n");
MP3_Pause();
}
void handleATMP3_Resume(const char *command) {
DEBUG_PRINTF(LEVEL_INFO, "MP3 Resume\n");
MP3_Resume();
}
void handleATMP3_Stop(const char *command) {
DEBUG_PRINTF(LEVEL_INFO, "MP3 Stop\n");
MP3_Stop();
}
void handleATMP3Time(const char *command) {
uint16_t time = MP3_GetTime();
DEBUG_PRINTF(LEVEL_INFO, "Current play time: %d seconds\n", time);
}
void handleATMP3Volume(const char *command) {
// 提取音量值
const char *volumeStr = command + strlen("AT+MP3Volume=");
DEBUG_PRINTF(LEVEL_INFO, "Setting volume to: %s\n", volumeStr);
uint8_t volume = atoi(volumeStr);
DEBUG_PRINTF(LEVEL_INFO, "Setting volume to: %d\n", volume);
MP3_Pause();
VS1053_SetVolume(255-volume, 255-volume);
MP3_Resume();
}
// 定義命令對映表
CommandMapping commandMap[MAX_COMMAND_COUNT] = {
{"AT+COMMAND1", handleATCommand1},
{"AT+COMMAND2", handleATCommand2},
{"AT+RESET", handleATReset},
// 檢視韌體資訊和重啟原因
{"AT+FirmwareInfo", PrintFirmwareInfoC},
// 檢視任務資訊
{"AT+TaskInfo", PrintTaskInfo},
// 播放MP3
{"AT+MP3=", handleATMP3Command},
// MP3 暫停播放
{"AT+MP3Pause", handleATMP3_Pause},
// MP3 繼續播放
{"AT+MP3Resume", handleATMP3_Resume},
// MP3 停止播放
{"AT+MP3Stop", handleATMP3_Stop},
// 檢視當前播放時間
{"AT+MP3Time", handleATMP3Time},
// 設定播放音量
{"AT+MP3Volume=", handleATMP3Volume},
{NULL, NULL} // 結束標記
};
// 解析並執行命令
void parseAndExecuteCommand(const char *command) {
for (int i = 0; commandMap[i].command != NULL; i++) {
if (strncmp(command, commandMap[i].command, strlen(commandMap[i].command)) == 0) {
printf("Executing command: %s\n", command);
commandMap[i].handler(command);
return;
}
}
printf("Unknown command: %s\n", command);
}
// 處理逐字元或多字元輸入
void bsp_parseCommand(const uint8_t *input, uint8_t length) {
for (uint8_t i = 0; i < length; i++) {
if (input[i] == '\n' || input[i] == '\r') {
// 命令結束,解析命令
if (bufferIndex > 0) {
commandBuffer[bufferIndex] = '\0'; // 終止字串
parseAndExecuteCommand(commandBuffer);
bufferIndex = 0; // 重置緩衝區索引
}
} else if (bufferIndex < MAX_COMMAND_LENGTH - 1) {
// 儲存字元到緩衝區
commandBuffer[bufferIndex++] = input[i];
} else {
// 緩衝區溢位處理
printf("Command buffer overflow\n");
bufferIndex = 0; // 重置緩衝區索引
break;
}
}
}
/*************************************************************************************************************************
* CopyRight: PlayerPencil FreeRtos通用庫
* Filename: bsp_command.h
* ContentIntroduction:
* 該檔案實現了從串列埠接收 \r\n 結尾的命令,並根據命令呼叫相應的處理函式
* CommandMapping commandMap[MAX_COMMAND_COUNT],為命令對映表,用於儲存命令和處理函式的對映關係
* 在呼叫命令之後,也會把匹配到的字串傳入,可以匹配後面的引數。如:
* const char *volumeStr = command + strlen("AT+MP3Volume=");
* uint8_t volume = atoi(volumeStr);volume就為需要的值
*
*
* FileHistory:
* VersionNumber Date: Author: Content:
* 01a 2024-11-17 PlayerPencil 建立了該檔案,完成了基礎功能的測試
*
*
*
*
*
*****************************************************************************************************************************/
#ifndef __BSP_COMMAND_H
#define __BSP_COMMAND_H
#include "bsp.h"
typedef void (*CommandHandler)(const char *command);
typedef struct {
const char *command;
CommandHandler handler;
} CommandMapping;
void bsp_parseCommand(const uint8_t *input, uint8_t length);
#endif // !__BSP_COMMAND_H