STM32/GD32/AT32 通过AT命令解析工具
- 这是一个针对 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