STM32/GD32/AT32 通过AT命令解析工具

  • 这是一个针对 STM32/GD32/AT32 等芯片的 AT 命令解析工具。其主要功能是从串口接收以 “\r\n” 结尾的命令数据,并将其解析为对应的命令,然后根据命令映射表调用相应的处理函数来执行特定操作。

1. 使用方式

  1. 将串口的数据放入 bsp_parseCommand 函数,并且传入数组和数据长度,此函数会自动保存传入的数据,所以不需要一次传入一包完整的数据。如果数据超出缓冲区大小,会自动清零。

  2. 修改命令映射表,将需要解析的命令字符串和命令的回调函数传入,在匹配到字符串之后就会自动调用回调函数

    CommandMapping commandMap[MAX_COMMAND_COUNT] = {
        {"AT+COMMAND1", handleATCommand1},
        {"AT+COMMAND2", handleATCommand2},
        {"AT+RESET", handleATReset},
        // 查看固件信息和重启原因
        {"AT+FirmwareInfo", PrintFirmwareInfoC},
        // 查看任务信息
        {"AT+TaskInfo", PrintTaskInfo},
        {NULL, NULL} // 结束标记
    };
    
  3. 如果需要修改命令长度的缓冲区,请修改 MAX_COMMAND_LENGTH 的定义

  4. 如果需要修改命令映射表数组的大小,请修改 MAX_COMMAND_COUNT

  5. 可以在匹配的字符串后添加参数,在回调中可自行解析。具体看下方代码

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

posted @ 2024-11-21 23:11  PlayerPencil  阅读(80)  评论(0编辑  收藏  举报