freeRTOS操作系统机器人实现
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

197 lines
7.5 KiB

/******************************************************************************
版权所有 (C), 2018-2099, Radkil
******************************************************************************
文 件 名 : ATParser.c
版 本 号 : 初稿
作 者 : Radkil
生成日期 : 2026年3月14日星期六
最近修改 :
功能描述 : AT命令解释器
修改历史 :
1.日 期 : 2026年3月14日星期六
作 者 : Radkil
修改内容 : 创建文件
******************************************************************************/
/*----------------------------------------------*
* 外部变量说明 *
*----------------------------------------------*/
/*----------------------------------------------*
* 外部函数原型说明 *
*----------------------------------------------*/
/*----------------------------------------------*
* 内部函数原型说明 *
*----------------------------------------------*/
/*----------------------------------------------*
* 全局变量 *
*----------------------------------------------*/
/*----------------------------------------------*
* 模块级变量 *
*----------------------------------------------*/
/*----------------------------------------------*
* 常量定义 *
*----------------------------------------------*/
/*----------------------------------------------*
* 宏定义 *
*----------------------------------------------*/
#include <stdio.h>
#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;
}
}
}