本文描述了一个基于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_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;