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.

198 lines
7.5 KiB

3 days ago
/******************************************************************************
(C), 2018-2099, Radkil
******************************************************************************
: ATParser.c
: 稿
: Radkil
: 2026314
:
: AT命令解释器
:
1. : 2026314
: 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;
}
}
}