/** * @file at_cmd.c * @brief AT命令驱动核心 - 重构版(安全架构) * @note 采用查询模式而非回调,彻底避免函数指针导致的IBUSERR * 简化状态机,增加边界检查,提高可靠性 */ #define DBG_TAG "at.cmd" #define DBG_LVL DBG_LOG #include #include "at_cmd.h" #include #include #include /* ==================== 配置 ==================== */ #ifndef AT_RECV_BUFFER_SIZE #define AT_RECV_BUFFER_SIZE 1024 #endif #ifndef AT_CMD_TIMEOUT_MS #define AT_CMD_TIMEOUT_MS 5000 #endif /* ==================== 内部状态 ==================== */ typedef struct { /* 串口 */ rt_device_t uart; /* 状态机 */ volatile at_state_t state; at_result_t result; /* 当前命令 */ const at_cmd_item_t *current_cmd; uint8_t retry_count; rt_tick_t start_tick; /* 接收缓冲 */ char rx_buf[AT_RECV_BUFFER_SIZE]; uint16_t rx_len; uint8_t rx_overflow; /* 溢出标志 */ /* 错误信息 */ char error_info[64]; /* URC表 */ struct { char prefix[AT_URC_MAX_LEN]; at_urc_cb_t callback; uint8_t valid; } urc_table[AT_URC_MAX_NUM]; /* 调试 */ uint8_t debug_enable; /* 同步信号量(用于可选的同步API) */ rt_sem_t cmd_sem; /* 初始化标志 */ uint8_t initialized; } at_context_t; static at_context_t g_at = {0}; /* ==================== 工具函数 ==================== */ /** * @brief 安全字符串拷贝 */ static void safe_strcpy(char *dst, size_t dst_size, const char *src) { if (!dst || !src || dst_size == 0) return; size_t src_len = strlen(src); size_t copy_len = (src_len < dst_size - 1) ? src_len : dst_size - 1; memcpy(dst, src, copy_len); dst[copy_len] = '\0'; } /** * @brief 清空接收缓冲区 */ static void rx_buf_clear(void) { g_at.rx_len = 0; g_at.rx_buf[0] = '\0'; g_at.rx_overflow = 0; } /** * @brief 追加数据到接收缓冲区(带边界检查) */ static void rx_buf_append(const char *data, uint16_t len) { if (!data || len == 0) return; /* 检查溢出 */ if (g_at.rx_len + len >= AT_RECV_BUFFER_SIZE - 1) { /* 缓冲区满,丢弃旧数据(保留一半) */ uint16_t keep_len = AT_RECV_BUFFER_SIZE / 2; memmove(g_at.rx_buf, g_at.rx_buf + g_at.rx_len - keep_len, keep_len); g_at.rx_len = keep_len; g_at.rx_overflow = 1; LOG_W("RX buffer overflow, discarding old data"); } /* 追加数据 */ uint16_t copy_len = len; if (g_at.rx_len + copy_len > AT_RECV_BUFFER_SIZE - 1) { copy_len = AT_RECV_BUFFER_SIZE - 1 - g_at.rx_len; } memcpy(g_at.rx_buf + g_at.rx_len, data, copy_len); g_at.rx_len += copy_len; g_at.rx_buf[g_at.rx_len] = '\0'; } /** * @brief 从缓冲区移除已处理的数据 */ static void rx_buf_consume(uint16_t len) { if (len >= g_at.rx_len) { rx_buf_clear(); } else { memmove(g_at.rx_buf, g_at.rx_buf + len, g_at.rx_len - len); g_at.rx_len -= len; g_at.rx_buf[g_at.rx_len] = '\0'; } } /** * @brief 查找字符串,返回位置(类似strstr但限制长度) */ static char* strnstr(const char *haystack, const char *needle, uint16_t haystack_len) { if (!haystack || !needle) return NULL; uint16_t needle_len = strlen(needle); if (needle_len == 0) return (char*)haystack; if (haystack_len < needle_len) return NULL; for (uint16_t i = 0; i <= haystack_len - needle_len; i++) { if (memcmp(haystack + i, needle, needle_len) == 0) { return (char*)(haystack + i); } } return NULL; } /** * @brief 检查是否包含完整响应行(以\n结尾) */ static int has_complete_line(const char *buf, uint16_t len) { if (!buf || len == 0) return 0; for (uint16_t i = 0; i < len; i++) { if (buf[i] == '\n') return 1; } return 0; } /** * @brief 提取一行响应 */ static uint16_t extract_line(char *dst, uint16_t dst_size, const char *src, uint16_t src_len) { if (!dst || !src || dst_size == 0) return 0; for (uint16_t i = 0; i < src_len && i < dst_size - 1; i++) { if (src[i] == '\r' || src[i] == '\n') { dst[i] = '\0'; /* 跳过\r\n */ uint16_t skip = i; if (src[i] == '\r' && i + 1 < src_len && src[i+1] == '\n') { skip = i + 2; } else { skip = i + 1; } return skip; } dst[i] = src[i]; } dst[0] = '\0'; return 0; } /* ==================== URC处理 ==================== */ /** * @brief 检查并处理URC * @return 1表示处理了URC,0表示无URC */ static int process_urc(void) { if (g_at.rx_len == 0) return 0; /* 提取一行检查 */ char line[128]; uint16_t line_len = 0; for (uint16_t i = 0; i < g_at.rx_len && i < sizeof(line) - 1; i++) { if (g_at.rx_buf[i] == '\r' || g_at.rx_buf[i] == '\n') { line_len = i; break; } line[i] = g_at.rx_buf[i]; } if (line_len == 0) return 0; line[line_len] = '\0'; /* 匹配URC前缀 */ for (int i = 0; i < AT_URC_MAX_NUM; i++) { if (!g_at.urc_table[i].valid) continue; const char *prefix = g_at.urc_table[i].prefix; if (strncmp(line, prefix, strlen(prefix)) == 0) { /* 匹配成功,调用回调 */ if (g_at.urc_table[i].callback) { g_at.urc_table[i].callback(line, line_len); } /* 消费这行数据 */ uint16_t consume = line_len; while (consume < g_at.rx_len && (g_at.rx_buf[consume] == '\r' || g_at.rx_buf[consume] == '\n')) { consume++; } rx_buf_consume(consume); return 1; } } return 0; } /* ==================== 响应处理 ==================== */ /** * @brief 检查响应是否完成 * @return 1表示完成,0表示未完成 */ static int check_response_complete(void) { if (!g_at.current_cmd) return 1; const char *rsp = g_at.rx_buf; uint16_t rsp_len = g_at.rx_len; const at_cmd_item_t *cmd = g_at.current_cmd; /* 检查错误响应 */ if (strnstr(rsp, "ERROR", rsp_len)) { g_at.result = AT_RESULT_ERROR; return 1; } /* 检查CME错误 */ char *cme = strnstr(rsp, "+CME ERROR:", rsp_len); if (cme) { g_at.result = AT_RESULT_CME_ERROR; /* 提取错误信息 */ safe_strcpy(g_at.error_info, sizeof(g_at.error_info), cme); return 1; } /* 检查CMS错误 */ char *cms = strnstr(rsp, "+CMS ERROR:", rsp_len); if (cms) { g_at.result = AT_RESULT_CMS_ERROR; safe_strcpy(g_at.error_info, sizeof(g_at.error_info), cms); return 1; } /* 检查期望的成功响应 */ if (cmd->expect_ok) { if (strnstr(rsp, cmd->expect_ok, rsp_len)) { /* 还需要OK确认(如果有) */ if (strnstr(rsp, "OK", rsp_len)) { g_at.result = AT_RESULT_OK; return 1; } } } else { /* 没有指定期望响应,只要收到OK就算成功 */ if (strnstr(rsp, "OK", rsp_len)) { g_at.result = AT_RESULT_OK; return 1; } } return 0; } /* ==================== 串口回调 ==================== */ static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size) { (void)dev; if (size == 0 || !g_at.uart) return RT_EOK; /* 读取数据 */ char buf[128]; rt_size_t read_len = rt_device_read(g_at.uart, 0, buf, sizeof(buf)); if (read_len > 0) { /* 追加到缓冲区 */ rx_buf_append(buf, read_len); /* 调试输出 */ if (g_at.debug_enable) { rt_kprintf("<<< "); for (rt_size_t i = 0; i < read_len; i++) { rt_kprintf("%c", buf[i]); } } } return RT_EOK; } /* ==================== API实现 ==================== */ int at_init(const char *uart_name) { if (g_at.initialized) { LOG_W("AT already initialized"); return 0; } /* 清零上下文 */ memset(&g_at, 0, sizeof(g_at)); /* 打开串口 */ g_at.uart = rt_device_find(uart_name); if (!g_at.uart) { LOG_E("UART %s not found", uart_name); return -1; } /* 配置串口 */ struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; config.baud_rate = BAUD_RATE_115200; config.data_bits = DATA_BITS_8; config.stop_bits = STOP_BITS_1; config.parity = PARITY_NONE; rt_device_control(g_at.uart, RT_DEVICE_CTRL_CONFIG, &config); rt_device_set_rx_indicate(g_at.uart, uart_rx_ind); rt_device_open(g_at.uart, RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX); /* 创建信号量 */ g_at.cmd_sem = rt_sem_create("at_sem", 0, RT_IPC_FLAG_FIFO); g_at.initialized = 1; g_at.state = AT_STATE_IDLE; LOG_I("AT initialized on %s", uart_name); return 0; } void at_deinit(void) { if (!g_at.initialized) return; if (g_at.uart) { rt_device_close(g_at.uart); g_at.uart = NULL; } if (g_at.cmd_sem) { rt_sem_delete(g_at.cmd_sem); g_at.cmd_sem = NULL; } g_at.initialized = 0; LOG_I("AT deinitialized"); } int at_exec_cmd(const at_cmd_item_t *cmd) { /* 安全检查 */ if (!g_at.initialized) { LOG_E("AT not initialized"); return -1; } if (!cmd) { LOG_E("cmd is NULL"); return -1; } if (!cmd->cmd) { LOG_E("cmd->cmd is NULL"); return -1; } /* 检查状态 */ if (g_at.state != AT_STATE_IDLE) { LOG_W("AT busy, state=%d", g_at.state); return -1; } /* 设置当前命令 */ g_at.current_cmd = cmd; g_at.retry_count = 0; g_at.result = AT_RESULT_IDLE; g_at.error_info[0] = '\0'; /* 清空接收缓冲区 */ rx_buf_clear(); /* 发送命令 */ rt_size_t cmd_len = strlen(cmd->cmd); rt_device_write(g_at.uart, 0, cmd->cmd, cmd_len); if (g_at.debug_enable) { rt_kprintf(">>> %s", cmd->cmd); } /* 转换状态 */ g_at.state = AT_STATE_SENDING; g_at.start_tick = rt_tick_get(); LOG_D("CMD started: %s", cmd->cmd); return 0; } int at_send_raw(const uint8_t *data, uint16_t len) { if (!g_at.initialized || !g_at.uart || !data || len == 0) { return 0; } return rt_device_write(g_at.uart, 0, data, len); } void at_process(void) { if (!g_at.initialized) return; /* 处理URC(高优先级) */ while (process_urc()) { /* 持续处理所有URC */ } /* 状态机 */ switch (g_at.state) { case AT_STATE_IDLE: /* 空闲状态,检查是否有URC需要处理 */ break; case AT_STATE_SENDING: /* 发送完成,进入等待 */ g_at.state = AT_STATE_WAITING; g_at.start_tick = rt_tick_get(); break; case AT_STATE_WAITING: { /* 检查是否收到完整响应 */ if (check_response_complete()) { g_at.state = AT_STATE_DONE; if (g_at.cmd_sem) { rt_sem_release(g_at.cmd_sem); } break; } /* 检查超时 */ uint32_t timeout = g_at.current_cmd ? g_at.current_cmd->timeout_ms : AT_CMD_TIMEOUT_MS; if (rt_tick_get() - g_at.start_tick > RT_TICK_PER_SECOND * timeout / 1000) { LOG_W("CMD timeout after %d ms", timeout); /* 检查重试 */ if (g_at.current_cmd && g_at.retry_count < g_at.current_cmd->retries) { g_at.retry_count++; LOG_I("Retry %d/%d", g_at.retry_count, g_at.current_cmd->retries); /* 重新发送 */ rx_buf_clear(); rt_device_write(g_at.uart, 0, g_at.current_cmd->cmd, strlen(g_at.current_cmd->cmd)); g_at.start_tick = rt_tick_get(); } else { g_at.result = AT_RESULT_TIMEOUT; g_at.state = AT_STATE_DONE; if (g_at.cmd_sem) { rt_sem_release(g_at.cmd_sem); } } } break; } case AT_STATE_DONE: /* 完成状态,等待获取结果后自动回到IDLE */ break; default: g_at.state = AT_STATE_IDLE; break; } } at_result_t at_get_result(void) { if (!g_at.initialized) return AT_RESULT_ERROR; /* 如果状态是DONE,获取结果后自动重置 */ if (g_at.state == AT_STATE_DONE) { at_result_t result = g_at.result; g_at.state = AT_STATE_IDLE; g_at.current_cmd = NULL; return result; } return AT_RESULT_IDLE; } const char* at_get_response(void) { if (!g_at.initialized) return ""; return g_at.rx_buf; } at_state_t at_get_state(void) { return g_at.state; } void at_abort_cmd(void) { if (!g_at.initialized) return; g_at.result = AT_RESULT_ABORT; g_at.state = AT_STATE_DONE; g_at.current_cmd = NULL; if (g_at.cmd_sem) { rt_sem_release(g_at.cmd_sem); } LOG_W("CMD aborted"); } int at_register_urc(const char *prefix, at_urc_cb_t callback) { if (!prefix || !callback) return -1; /* 检查是否已注册 */ for (int i = 0; i < AT_URC_MAX_NUM; i++) { if (g_at.urc_table[i].valid && strcmp(g_at.urc_table[i].prefix, prefix) == 0) { /* 更新回调 */ g_at.urc_table[i].callback = callback; return 0; } } /* 查找空槽 */ for (int i = 0; i < AT_URC_MAX_NUM; i++) { if (!g_at.urc_table[i].valid) { safe_strcpy(g_at.urc_table[i].prefix, AT_URC_MAX_LEN, prefix); g_at.urc_table[i].callback = callback; g_at.urc_table[i].valid = 1; LOG_D("URC registered: %s", prefix); return 0; } } LOG_E("URC table full"); return -1; } void at_unregister_urc(const char *prefix) { if (!prefix) return; for (int i = 0; i < AT_URC_MAX_NUM; i++) { if (g_at.urc_table[i].valid && strcmp(g_at.urc_table[i].prefix, prefix) == 0) { g_at.urc_table[i].valid = 0; LOG_D("URC unregistered: %s", prefix); return; } } } void at_clear_response(void) { rx_buf_clear(); } void at_set_debug(uint8_t enable) { g_at.debug_enable = enable; } const char* at_get_error_info(void) { return g_at.error_info[0] ? g_at.error_info : "Unknown error"; }