/****************************************************************************** 版权所有 (C), 2018-2099, Radkil ****************************************************************************** 文 件 名 : ATParser.c 版 本 号 : 初稿 作 者 : Radkil 生成日期 : 2026年3月14日星期六 最近修改 : 功能描述 : AT命令解释器 修改历史 : 1.日 期 : 2026年3月14日星期六 作 者 : Radkil 修改内容 : 创建文件 ******************************************************************************/ /*----------------------------------------------* * 外部变量说明 * *----------------------------------------------*/ /*----------------------------------------------* * 外部函数原型说明 * *----------------------------------------------*/ /*----------------------------------------------* * 内部函数原型说明 * *----------------------------------------------*/ /*----------------------------------------------* * 全局变量 * *----------------------------------------------*/ /*----------------------------------------------* * 模块级变量 * *----------------------------------------------*/ /*----------------------------------------------* * 常量定义 * *----------------------------------------------*/ /*----------------------------------------------* * 宏定义 * *----------------------------------------------*/ #include #include "ATParser.h" // 内部辅助函数:检查字符串是否以某前缀开头 static bool starts_with(const char *str, const char *prefix) { if (!str || !prefix) return false; return strncmp(str, prefix, strlen(prefix)) == 0; } void at_parser_init(at_parser_t *parser, void (*send_cb)(const uint8_t*, uint16_t), uint32_t (*tick_cb)(void)) { memset(parser, 0, sizeof(at_parser_t)); parser->hw_send = send_cb; parser->hw_get_tick = tick_cb; parser->response_count = 0; parser->last_status = AT_UNKNOWN; } bool at_parser_register_response(at_parser_t *parser, const char *prefix, void (*cb)(const char*, int)) { if (parser->response_count >= AT_MAX_RESPONSES) return false; parser->responses[parser->response_count].prefix = prefix; parser->responses[parser->response_count].callback = cb; parser->response_count++; return true; } void at_parser_feed(at_parser_t *p, uint8_t data) { // 存入环形缓冲区 uint16_t next_head = (p->head + 1) % AT_RX_BUFFER_SIZE; if (next_head != p->tail) { // 缓冲区未满 p->rx_buffer[p->head] = data; p->head = next_head; } // 简单的状态机解析行结束符 (\n) // 注意:AT 命令通常以 \r\n 结尾,这里简化处理,遇到 \n 即认为一行结束 // 更严谨的做法是检测 \r\n 序列 if (data == '\n') { if (p->cmd_len > 0 && p->cmd_len < AT_CMD_MAX_LEN) { p->cmd_buffer[p->cmd_len] = '\0'; // 封口 // 1. 检查是否是标准响应 (OK / ERROR) if (strcmp(p->cmd_buffer, "OK") == 0 || strcmp(p->cmd_buffer, "\rOK") == 0) { if (p->is_waiting_response) { p->last_status = AT_OK; p->is_waiting_response = false; } } else if (strcmp(p->cmd_buffer, "ERROR") == 0 || strcmp(p->cmd_buffer, "\rERROR") == 0) { if (p->is_waiting_response) { p->last_status = AT_ERROR; p->is_waiting_response = false; } } else { // 2. 检查注册的自定义前缀 (URC 或 带参数响应) for (int i = 0; i < p->response_count; i++) { if (starts_with(p->cmd_buffer, p->responses[i].prefix)) { if (p->responses[i].callback) { // 去掉前缀部分,只传递参数部分给回调 (可选优化) // 这里传递整行,用户回调内自行解析 p->responses[i].callback(p->cmd_buffer, strlen(p->cmd_buffer)); } // 注意:如果是 URC (异步消息),不应停止等待标志,除非它是命令的直接响应 // 这里简化逻辑:如果是等待响应期间收到的非 OK/ERROR,通常视为中间数据 break; } } // 如果正在等待响应,且收到了非 OK/ERROR 的未知行,通常忽略或视为中间数据 // 某些模块会在 OK 之前返回多行数据 } // 重置行缓冲 p->cmd_len = 0; } else { p->cmd_len = 0; } } else if (data != '\r') { // 忽略 \r,只存有效字符,防止缓冲溢出 if (p->cmd_len < AT_CMD_MAX_LEN - 1) { p->cmd_buffer[p->cmd_len++] = data; } } } at_status_t at_parser_send_cmd(at_parser_t *p, const char *cmd, uint32_t timeout_ms) { if (!p->hw_send) return AT_ERROR; // 1. 发送命令 + \r\n uint8_t tx_buf[AT_CMD_MAX_LEN + 4]; uint16_t len = strlen(cmd); memcpy(tx_buf, cmd, len); tx_buf[len++] = '\r'; tx_buf[len++] = '\n'; p->hw_send(tx_buf, len); // 2. 设置等待状态 p->is_waiting_response = true; p->last_status = AT_UNKNOWN; p->timeout_ms = timeout_ms; p->wait_start_time = p->hw_get_tick(); // 3. 阻塞等待 (或在外部轮询 process) // 为了兼容性,这里使用简单的轮询等待,实际嵌入式系统中建议配合 RTOS 信号量 while (p->is_waiting_response) { // 在等待期间,必须不断调用 feed 或让中断去 feed // 这里假设 feed 是在中断中调用的,所以这里只需要检查超时 uint32_t current_tick = p->hw_get_tick(); // 处理时间回绕 uint32_t elapsed = (current_tick >= p->wait_start_time) ? (current_tick - p->wait_start_time) : (0xFFFFFFFF - p->wait_start_time + current_tick); if (elapsed > timeout_ms) { p->is_waiting_response = false; p->last_status = AT_TIMEOUT; break; } // 短暂延时,避免死循环占用 CPU (根据平台调整) // 如果是 RTOS,这里应该是 osDelay(1); // 如果是裸机,确保中断能打断此处 } return p->last_status; } void at_parser_send_raw(at_parser_t *p, const uint8_t *data, uint16_t len) { if (p->hw_send) { p->hw_send(data, len); } } void at_parser_process(at_parser_t *p) { // 主要用于处理超时逻辑的非阻塞版本 // 如果使用了上面的阻塞式 send_cmd,此函数主要用于看门狗或额外逻辑 if (p->is_waiting_response) { uint32_t current_tick = p->hw_get_tick(); uint32_t elapsed = (current_tick >= p->wait_start_time) ? (current_tick - p->wait_start_time) : (0xFFFFFFFF - p->wait_start_time + current_tick); if (elapsed > p->timeout_ms) { p->is_waiting_response = false; p->last_status = AT_TIMEOUT; } } }