MCU 通用AT指令处理框架

发布于:2025-08-01 ⋅ 阅读:(19) ⋅ 点赞:(0)


​ 本文描述了一个基于RTOS的通用AT指令处理框架设计,适用于各类嵌入式MCU系统。该框架提供了完整的AT指令通信能力,包括命令发送、响应处理、URC事件处理和数据透传模式。

设计原理与架构图

类图表示

URC注册表
1
*
命令状态
1
1
at_client_t
- int uart_port
- uint32_t baudrate
- uint32_t default_timeout
- at_cmd_state_t cmd_state
- bool in_data_mode
- bool cmd_in_progress
- data_receive_cb_t data_cb
- void* data_context
- urc_entry_t* urc_table
- size_t urc_table_size
- size_t urc_count
- SemaphoreHandle_t mutex
- SemaphoreHandle_t resp_sem
- TaskHandle_t task_handle
+at_status_t(*send_command)
+void(*register_urc)
+void(*enter_data_mode)
+void(*exit_data_mode)
+size_t(*send_data)
urc_entry_t
- const char* prefix
- urc_handler_t handler
- void* context
at_cmd_state_t
- const char* expect
- uint32_t timeout
- char* resp_buf
- size_t buf_size
- size_t resp_len

序列图: AT命令发送流程

应用程序 AT客户端 UART驱动 RTOS内核 send_command("AT+CSQ", "+CSQ:", 2000, buffer, 128) 获取互斥锁 锁成功 发送"AT+CSQ\r\n" 等待响应信号量(2000ms) 超时 释放互斥锁 AT_TIMEOUT 接收到"+CSQ:24,99" 匹配到期望响应 发送响应信号量 信号量到达 释放互斥锁 AT_OK alt [超时] [收到响应] 应用程序 AT客户端 UART驱动 RTOS内核

序列图: URC处理流程

UART驱动 AT客户端 应用程序 接收到"+CMT: +8613800138000,Hello" 检查URC表 调用注册的URC处理函数 处理短信通知v UART驱动 AT客户端 应用程序

头文件: at_client.h

#ifndef AT_CLIENT_H
#define AT_CLIENT_H

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

// RTOS头文件 (根据实际RTOS选择)
#ifdef USE_FREERTOS
#include "FreeRTOS.h"
#include "semphr.h"
#else
// 其他RTOS适配
#endif

// AT指令执行状态
typedef enum {
    AT_OK = 0,      // 执行成功
    AT_ERROR,       // 执行失败
    AT_TIMEOUT,     // 超时
    AT_BUSY,        // 模块忙
    AT_MEM_ERR,     // 内存错误
    AT_PARAM_ERR    // 参数错误
} at_status_t;

// URC处理函数类型
typedef void (*urc_handler_t)(void* context, const char* urc);

// 数据接收回调类型
typedef void (*data_receive_cb_t)(void* context, uint8_t* data, size_t len);

// URC注册项
typedef struct {
    const char* prefix;         // URC前缀
    urc_handler_t handler;      // 处理函数
    void* context;              // 上下文指针
} urc_entry_t;

// AT命令状态
typedef struct {
    const char* expect;         // 期望响应
    uint32_t timeout;           // 超时时间 (RTOS ticks)
    char* resp_buf;             // 响应缓冲区
    size_t buf_size;            // 缓冲区大小
    size_t resp_len;            // 已接收长度
} at_cmd_state_t;

// AT客户端对象
typedef struct at_client {
    // 配置参数
    int uart_port;              // UART端口号
    uint32_t baudrate;          // 波特率
    uint32_t default_timeout;   // 默认超时(ms)
    
    // 内部状态
    at_cmd_state_t cmd_state;   // 命令状态
    bool in_data_mode;          // 数据模式标志
    bool cmd_in_progress;       // 命令执行中标志
    
    // 回调函数
    data_receive_cb_t data_cb;  // 数据接收回调
    void* data_context;         // 数据回调上下文
    
    // URC处理
    urc_entry_t* urc_table;     // URC注册表
    size_t urc_table_size;      // URC表大小
    size_t urc_count;           // 已注册URC数量
    
    // RTOS资源
    #ifdef USE_FREERTOS
    SemaphoreHandle_t mutex;    // 互斥锁
    SemaphoreHandle_t resp_sem; // 响应信号量
    TaskHandle_t task_handle;   // 任务句柄
    #endif
    
    // 方法指针
    at_status_t (*send_command)(struct at_client* self, 
                               const char* cmd, 
                               const char* expect, 
                               uint32_t timeout, 
                               char* resp_buf, 
                               size_t buf_size);
                               
    void (*register_urc)(struct at_client* self, 
                        const char* prefix, 
                        urc_handler_t handler, 
                        void* context);
                        
    void (*enter_data_mode)(struct at_client* self, 
                           data_receive_cb_t cb, 
                           void* context);
                           
    void (*exit_data_mode)(struct at_client* self);
    
    size_t (*send_data)(struct at_client* self, 
                       const uint8_t* data, 
                       size_t len);
} at_client_t;

// 创建AT客户端实例
at_client_t* at_client_create(int uart_port, uint32_t baudrate, uint32_t default_timeout);

// 销毁AT客户端实例
void at_client_destroy(at_client_t* client);

// 启动AT服务任务
bool at_client_start(at_client_t* client);

// 停止AT服务任务
void at_client_stop(at_client_t* client);

#endif // AT_CLIENT_H

源文件: at_client.c

#include "at_client.h"
#include <string.h>
#include <stdio.h>

// 模拟UART驱动接口
void uart_write(int port, const uint8_t* data, size_t len);
size_t uart_read(int port, uint8_t* data, size_t len, uint32_t timeout);

// 内部函数声明
static void at_service_task(void* arg);
static void at_process_line(at_client_t* client, const char* line);
static at_status_t at_cmd_send_impl(at_client_t* self, 
                                   const char* cmd, 
                                   const char* expect, 
                                   uint32_t timeout, 
                                   char* resp_buf, 
                                   size_t buf_size);
                                   
static void register_urc_impl(at_client_t* self, 
                             const char* prefix, 
                             urc_handler_t handler, 
                             void* context);
                             
static void enter_data_mode_impl(at_client_t* self, 
                                data_receive_cb_t cb, 
                                void* context);
                                
static void exit_data_mode_impl(at_client_t* self);
                                
static size_t send_data_impl(at_client_t* self, 
                            const uint8_t* data, 
                            size_t len);

// 创建AT客户端实例
at_client_t* at_client_create(int uart_port, uint32_t baudrate, uint32_t default_timeout) {
    at_client_t* client = malloc(sizeof(at_client_t));
    if (!client) return NULL;
    
    memset(client, 0, sizeof(at_client_t));
    
    // 初始化配置
    client->uart_port = uart_port;
    client->baudrate = baudrate;
    client->default_timeout = default_timeout;
    
    // 初始化状态
    client->cmd_in_progress = false;
    client->in_data_mode = false;
    
    // 初始化方法指针
    client->send_command = at_cmd_send_impl;
    client->register_urc = register_urc_impl;
    client->enter_data_mode = enter_data_mode_impl;
    client->exit_data_mode = exit_data_mode_impl;
    client->send_data = send_data_impl;
    
    // 创建URC表 (固定大小)
    client->urc_table_size = 10;
    client->urc_table = malloc(client->urc_table_size * sizeof(urc_entry_t));
    if (!client->urc_table) {
        free(client);
        return NULL;
    }
    memset(client->urc_table, 0, client->urc_table_size * sizeof(urc_entry_t));
    client->urc_count = 0;
    
    // 创建RTOS资源
    #ifdef USE_FREERTOS
    client->mutex = xSemaphoreCreateMutex();
    client->resp_sem = xSemaphoreCreateBinary();
    
    if (!client->mutex || !client->resp_sem) {
        free(client->urc_table);
        free(client);
        return NULL;
    }
    #endif
    
    return client;
}

// 销毁AT客户端实例
void at_client_destroy(at_client_t* client) {
    if (!client) return;
    
    // 停止服务任务
    at_client_stop(client);
    
    // 释放RTOS资源
    #ifdef USE_FREERTOS
    if (client->mutex) vSemaphoreDelete(client->mutex);
    if (client->resp_sem) vSemaphoreDelete(client->resp_sem);
    #endif
    
    // 释放URC表
    if (client->urc_table) free(client->urc_table);
    
    // 释放命令状态缓冲区
    if (client->cmd_state.resp_buf) free(client->cmd_state.resp_buf);
    
    free(client);
}

// 启动AT服务任务
bool at_client_start(at_client_t* client) {
    #ifdef USE_FREERTOS
    // 创建服务任务
    BaseType_t result = xTaskCreate(
        at_service_task,        // 任务函数
        "AT_Service",           // 任务名
        configMINIMAL_STACK_SIZE * 4, // 堆栈大小
        client,                 // 参数
        tskIDLE_PRIORITY + 2,   // 优先级
        &client->task_handle    // 任务句柄
    );
    
    return (result == pdPASS);
    #else
    // 其他RTOS实现
    return true;
    #endif
}

// 停止AT服务任务
void at_client_stop(at_client_t* client) {
    #ifdef USE_FREERTOS
    if (client->task_handle) {
        vTaskDelete(client->task_handle);
        client->task_handle = NULL;
    }
    #endif
}

// AT服务任务实现
static void at_service_task(void* arg) {
    at_client_t* client = (at_client_t*)arg;
    uint8_t rx_buf[256];
    size_t rx_index = 0;
    
    while (1) {
        uint8_t c;
        
        // 从串口读取一个字节
        if (uart_read(client->uart_port, &c, 1, 10) != 1) {
            #ifdef USE_FREERTOS
            vTaskDelay(1); // 短暂延时避免忙等
            #endif
            continue;
        }
        
        // 处理回车符(忽略)
        if (c == '\r') {
            continue;
        }
        
        // 处理换行符(行结束)
        if (c == '\n') {
            if (rx_index > 0) {
                rx_buf[rx_index] = '\0'; // 确保字符串终止
                
                if (client->in_data_mode) {
                    // 数据模式处理
                    if (client->data_cb) {
                        client->data_cb(client->data_context, rx_buf, rx_index);
                    }
                } else {
                    // 命令模式处理
                    at_process_line(client, (const char*)rx_buf);
                }
                
                rx_index = 0;
            }
            continue;
        }
        
        // 处理普通字符
        if (rx_index < sizeof(rx_buf) - 1) {
            rx_buf[rx_index++] = c;
        } else {
            // 缓冲区溢出处理
            rx_index = 0; // 重置缓冲区
        }
    }
}

// 处理接收行
static void at_process_line(at_client_t* client, const char* line) {
    // 1. 检查URC
    for (size_t i = 0; i < client->urc_count; i++) {
        urc_entry_t* entry = &client->urc_table[i];
        if (strstr(line, entry->prefix) == line) {
            entry->handler(entry->context, line);
            return;
        }
    }
    
    // 2. 检查命令响应
    if (client->cmd_in_progress) {
        // 检查期望响应
        if (strstr(line, client->cmd_state.expect) != NULL) {
            // 成功响应
            #ifdef USE_FREERTOS
            xSemaphoreGive(client->resp_sem);
            #endif
        } 
        // 检查错误响应
        else if (strstr(line, "ERROR") != NULL) {
            // 错误响应
            #ifdef USE_FREERTOS
            xSemaphoreGive(client->resp_sem);
            #endif
        }
        
        // 保存响应数据
        if (client->cmd_state.resp_buf && 
            client->cmd_state.resp_len < client->cmd_state.buf_size) {
            int len = snprintf(
                client->cmd_state.resp_buf + client->cmd_state.resp_len,
                client->cmd_state.buf_size - client->cmd_state.resp_len,
                "%s\n", line
            );
            if (len > 0) {
                client->cmd_state.resp_len += len;
            }
        }
    }
}

// AT命令发送实现
static at_status_t at_cmd_send_impl(at_client_t* self, 
                                   const char* cmd, 
                                   const char* expect, 
                                   uint32_t timeout, 
                                   char* resp_buf, 
                                   size_t buf_size) {
    #ifdef USE_FREERTOS
    // 获取互斥锁
    if (xSemaphoreTake(self->mutex, pdMS_TO_TICKS(100)) != pdTRUE) {
        return AT_BUSY;
    }
    #endif
    
    // 检查是否已有命令在执行
    if (self->cmd_in_progress) {
        #ifdef USE_FREERTOS
        xSemaphoreGive(self->mutex);
        #endif
        return AT_BUSY;
    }
    
    // 设置命令状态
    self->cmd_in_progress = true;
    self->cmd_state.expect = expect ? expect : "OK";
    self->cmd_state.timeout = timeout ? timeout : self->default_timeout;
    self->cmd_state.resp_buf = resp_buf;
    self->cmd_state.buf_size = buf_size;
    self->cmd_state.resp_len = 0;
    
    // 发送命令
    uart_write(self->uart_port, (const uint8_t*)cmd, strlen(cmd));
    uart_write(self->uart_port, (const uint8_t*)"\r\n", 2);
    
    // 等待响应
    at_status_t status = AT_TIMEOUT;
    #ifdef USE_FREERTOS
    if (xSemaphoreTake(self->resp_sem, pdMS_TO_TICKS(self->cmd_state.timeout)) == pdTRUE) {
        status = AT_OK;
    }
    #endif
    
    // 清理命令状态
    self->cmd_in_progress = false;
    
    #ifdef USE_FREERTOS
    xSemaphoreGive(self->mutex);
    #endif
    
    return status;
}

// 注册URC实现
static void register_urc_impl(at_client_t* self, 
                             const char* prefix, 
                             urc_handler_t handler, 
                             void* context) {
    if (!prefix || !handler) return;
    
    #ifdef USE_FREERTOS
    xSemaphoreTake(self->mutex, portMAX_DELAY);
    #endif
    
    // 检查是否已存在
    for (size_t i = 0; i < self->urc_count; i++) {
        if (strcmp(self->urc_table[i].prefix, prefix) == 0) {
            // 更新现有条目
            self->urc_table[i].handler = handler;
            self->urc_table[i].context = context;
            #ifdef USE_FREERTOS
            xSemaphoreGive(self->mutex);
            #endif
            return;
        }
    }
    
    // 添加新条目
    if (self->urc_count < self->urc_table_size) {
        urc_entry_t* entry = &self->urc_table[self->urc_count++];
        entry->prefix = prefix;
        entry->handler = handler;
        entry->context = context;
    }
    
    #ifdef USE_FREERTOS
    xSemaphoreGive(self->mutex);
    #endif
}

// 进入数据模式实现
static void enter_data_mode_impl(at_client_t* self, 
                                data_receive_cb_t cb, 
                                void* context) {
    #ifdef USE_FREERTOS
    xSemaphoreTake(self->mutex, portMAX_DELAY);
    #endif
    
    self->in_data_mode = true;
    self->data_cb = cb;
    self->data_context = context;
    
    #ifdef USE_FREERTOS
    xSemaphoreGive(self->mutex);
    #endif
}

// 退出数据模式实现
static void exit_data_mode_impl(at_client_t* self) {
    #ifdef USE_FREERTOS
    xSemaphoreTake(self->mutex, portMAX_DELAY);
    #endif
    
    self->in_data_mode = false;
    self->data_cb = NULL;
    self->data_context = NULL;
    
    #ifdef USE_FREERTOS
    xSemaphoreGive(self->mutex);
    #endif
}

// 发送数据实现
static size_t send_data_impl(at_client_t* self, 
                           const uint8_t* data, 
                           size_t len) {
    #ifdef USE_FREERTOS
    xSemaphoreTake(self->mutex, portMAX_DELAY);
    #endif
    
    size_t sent = 0;
    if (self->in_data_mode) {
        sent = uart_write(self->uart_port, data, len);
    }
    
    #ifdef USE_FREERTOS
    xSemaphoreGive(self->mutex);
    #endif
    
    return sent;
}

使用示例

#include "at_client.h"

// URC处理函数
void sms_urc_handler(void* context, const char* urc) {
    printf("Received SMS: %s\n", urc);
    // 实际项目中解析短信内容
}

// 数据接收回调
void data_receive_callback(void* context, uint8_t* data, size_t len) {
    printf("Received %zu bytes: %.*s\n", len, (int)len, data);
}

int main() {
    // 创建AT客户端
    at_client_t* at_client = at_client_create(1, 115200, 2000);
    if (!at_client) {
        printf("Failed to create AT client\n");
        return -1;
    }
    
    // 注册URC
    at_client->register_urc(at_client, "+CMT:", sms_urc_handler, NULL);
    
    // 启动服务任务
    if (!at_client_start(at_client)) {
        printf("Failed to start AT service\n");
        at_client_destroy(at_client);
        return -1;
    }
    
    // 发送AT命令
    char resp_buffer[128];
    at_status_t status = at_client->send_command(at_client, "AT+CSQ", "+CSQ:", 
                                               2000, resp_buffer, sizeof(resp_buffer));
    if (status == AT_OK) {
        printf("Signal quality: %s\n", resp_buffer);
    } else {
        printf("Command failed: %d\n", status);
    }
    
    // 进入数据模式
    at_client->enter_data_mode(at_client, data_receive_callback, NULL);
    
    // 发送数据
    const char* data = "Hello, module!";
    at_client->send_data(at_client, (uint8_t*)data, strlen(data));
    
    // 保持运行...
    while(1) {
        // 主循环
    }
    
    // 清理资源
    at_client_stop(at_client);
    at_client_destroy(at_client);
    
    return 0;