MCU 通用AT指令处理框架
文章目录
- 设计原理与架构图
- 类图表示
- 序列图: AT命令发送流程
- 序列图: URC处理流程
- 头文件: `at_client.h`
- 源文件: `at_client.c`
- 使用示例
本文描述了一个基于RTOS的通用AT指令处理框架设计,适用于各类嵌入式MCU系统。该框架提供了完整的AT指令通信能力,包括命令发送、响应处理、URC事件处理和数据透传模式。
设计原理与架构图
类图表示
序列图: AT命令发送流程
序列图: URC处理流程
头文件: 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_FREERTOSSemaphoreHandle_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_FREERTOSclient->mutex = xSemaphoreCreateMutex();client->resp_sem = xSemaphoreCreateBinary();if (!client->mutex || !client->resp_sem) {free(client->urc_table);free(client);return NULL;}#endifreturn client;
}// 销毁AT客户端实例
void at_client_destroy(at_client_t* client) {if (!client) return;// 停止服务任务at_client_stop(client);// 释放RTOS资源#ifdef USE_FREERTOSif (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_FREERTOSif (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_FREERTOSvTaskDelay(1); // 短暂延时避免忙等#endifcontinue;}// 处理回车符(忽略)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. 检查URCfor (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_FREERTOSxSemaphoreGive(client->resp_sem);#endif} // 检查错误响应else if (strstr(line, "ERROR") != NULL) {// 错误响应#ifdef USE_FREERTOSxSemaphoreGive(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_FREERTOSxSemaphoreGive(self->mutex);#endifreturn 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_FREERTOSif (xSemaphoreTake(self->resp_sem, pdMS_TO_TICKS(self->cmd_state.timeout)) == pdTRUE) {status = AT_OK;}#endif// 清理命令状态self->cmd_in_progress = false;#ifdef USE_FREERTOSxSemaphoreGive(self->mutex);#endifreturn 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_FREERTOSxSemaphoreTake(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_FREERTOSxSemaphoreGive(self->mutex);#endifreturn;}}// 添加新条目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_FREERTOSxSemaphoreGive(self->mutex);#endif
}// 进入数据模式实现
static void enter_data_mode_impl(at_client_t* self, data_receive_cb_t cb, void* context) {#ifdef USE_FREERTOSxSemaphoreTake(self->mutex, portMAX_DELAY);#endifself->in_data_mode = true;self->data_cb = cb;self->data_context = context;#ifdef USE_FREERTOSxSemaphoreGive(self->mutex);#endif
}// 退出数据模式实现
static void exit_data_mode_impl(at_client_t* self) {#ifdef USE_FREERTOSxSemaphoreTake(self->mutex, portMAX_DELAY);#endifself->in_data_mode = false;self->data_cb = NULL;self->data_context = NULL;#ifdef USE_FREERTOSxSemaphoreGive(self->mutex);#endif
}// 发送数据实现
static size_t send_data_impl(at_client_t* self, const uint8_t* data, size_t len) {#ifdef USE_FREERTOSxSemaphoreTake(self->mutex, portMAX_DELAY);#endifsize_t sent = 0;if (self->in_data_mode) {sent = uart_write(self->uart_port, data, len);}#ifdef USE_FREERTOSxSemaphoreGive(self->mutex);#endifreturn 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;}// 注册URCat_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;