1136 lines
39 KiB
C
1136 lines
39 KiB
C
/**
|
||
* @file at_uart.c
|
||
* @brief AT Command UART Implementation
|
||
* @details UART communication for 4G module AT commands
|
||
* Reference: c_version/at_uart.c (command/response/URC mechanism)
|
||
* Using lisa_uart driver with FreeRTOS synchronization
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <stdint.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <ctype.h>
|
||
|
||
#include "at_uart.h"
|
||
#include "IOMuxManager.h"
|
||
#include "lisa_device.h"
|
||
#include "lisa_uart.h"
|
||
|
||
#include "FreeRTOS.h"
|
||
#include "task.h"
|
||
#include "semphr.h"
|
||
#include "event_groups.h"
|
||
|
||
#define TAG "at_uart"
|
||
#include "lisa_log.h"
|
||
|
||
/* Bit position macros for event groups */
|
||
#ifndef BIT0
|
||
#define BIT0 (1 << 0)
|
||
#define BIT1 (1 << 1)
|
||
#define BIT2 (1 << 2)
|
||
#define BIT3 (1 << 3)
|
||
#define BIT4 (1 << 4)
|
||
#define BIT5 (1 << 5)
|
||
#define BIT6 (1 << 6)
|
||
#define BIT7 (1 << 7)
|
||
#define BIT8 (1 << 8)
|
||
#define BIT9 (1 << 9)
|
||
#define BIT10 (1 << 10)
|
||
#define BIT11 (1 << 11)
|
||
#define BIT12 (1 << 12)
|
||
#define BIT13 (1 << 13)
|
||
#define BIT14 (1 << 14)
|
||
#define BIT15 (1 << 15)
|
||
#endif
|
||
|
||
/* AT UART Configuration */
|
||
#define AT_UART_DEVICE "uart2" /* UART device name */
|
||
#define AT_UART_DEFAULT_BAUDRATE 115200 /* Default baudrate */
|
||
#define AT_UART_USED_BAUDRATE 921600 /* used baudrate */
|
||
#define AT_UART_RX_BUF_SIZE 512 /* Receive buffer size per circular buffer */
|
||
#define AT_UART_RX_BUF_COUNT 60 /* Circular buffer count - increased for high load scenarios */
|
||
#define AT_UART_TASK_STACK_SIZE 2048 /* Task stack size */
|
||
#define AT_UART_TASK_PRIORITY 5 /* Task priority */
|
||
|
||
#define RX_BUFFER_INITIAL_SIZE 512 /* Initial dynamic buffer size */
|
||
#define RESPONSE_BUFFER_SIZE 512 /* Response buffer size */
|
||
|
||
/* AT Response Constants */
|
||
#define AT_RESPONSE_OK_LEN 4 /* Length of "OK\r\n" */
|
||
#define AT_RESPONSE_ERROR_LEN 7 /* Length of "ERROR\r\n" */
|
||
#define AT_RESPONSE_PROMPT_LEN 1 /* Length of '>' prompt */
|
||
#define AT_RESPONSE_CRLF_LEN 2 /* Length of "\r\n" */
|
||
|
||
/* Event bits */
|
||
#define AT_EVENT_COMMAND_DONE BIT0 /* Command completed successfully */
|
||
#define AT_EVENT_COMMAND_ERROR BIT1 /* Command error */
|
||
#define AT_EVENT_DATA_PROMPT BIT2 /* '>' prompt received */
|
||
|
||
/* UART1 引脚: PB2=TX, PB3=RX */
|
||
#define UART2_TX_PAD CSK_IOMUX_PAD_A
|
||
#define UART2_TX_PIN 15
|
||
#define UART2_RX_PAD CSK_IOMUX_PAD_A
|
||
#define UART2_RX_PIN 16
|
||
#define UART2_FUNC CSK_IOMUX_FUNC_ALTER4
|
||
|
||
/* AT UART Module Context */
|
||
static struct {
|
||
lisa_device_t *uart_dev; /* UART device pointer */
|
||
uint8_t rx_hw_buffer[1024]; /* HW receive buffer */
|
||
|
||
/* Dynamic receive buffer (auto-grow) */
|
||
char *rx_buffer;
|
||
size_t rx_buffer_size;
|
||
size_t rx_buffer_capacity;
|
||
|
||
/* Response buffer */
|
||
char response[RESPONSE_BUFFER_SIZE];
|
||
int cme_error_code;
|
||
|
||
/* Synchronization */
|
||
TaskHandle_t process_task; /* Processing task handle */
|
||
SemaphoreHandle_t tx_mutex; /* TX mutex for thread safety */
|
||
SemaphoreHandle_t cmd_mutex; /* Command mutex */
|
||
SemaphoreHandle_t buffer_mutex; /* Buffer mutex */
|
||
EventGroupHandle_t event_group; /* Event group for command sync */
|
||
|
||
/* URC callback list */
|
||
at_urc_callback_node_t *urc_callbacks_head;
|
||
|
||
/* Flags */
|
||
uint8_t initialized;
|
||
uint8_t uart_config_status;
|
||
|
||
bool wait_for_response;
|
||
} at_uart_ctx = {0};
|
||
|
||
void lisa_uart2_pinmux(void)
|
||
{
|
||
IOMuxManager_PinConfigure(UART2_TX_PAD, UART2_TX_PIN, UART2_FUNC);
|
||
IOMuxManager_PinConfigure(UART2_RX_PAD, UART2_RX_PIN, UART2_FUNC);
|
||
}
|
||
|
||
/**
|
||
* @brief Handle URC (Unsolicited Result Code)
|
||
*/
|
||
static void handle_urc(const char *command, at_arg_value_t *arguments, size_t arg_count)
|
||
{
|
||
if (!command) return;
|
||
|
||
/* Special handling for CME ERROR */
|
||
if (strcmp(command, "CME ERROR") == 0 && arg_count > 0) {
|
||
if (arguments[0].type == AT_ARG_TYPE_INT) {
|
||
at_uart_ctx.cme_error_code = arguments[0].data.int_val;
|
||
LISA_LOGE(TAG, "CME ERROR received, error_code=%d", at_uart_ctx.cme_error_code);
|
||
xEventGroupSetBits(at_uart_ctx.event_group, AT_EVENT_COMMAND_ERROR);
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* Call all registered URC callbacks */
|
||
at_urc_callback_node_t *node = at_uart_ctx.urc_callbacks_head;
|
||
while (node) {
|
||
if (node->callback) {
|
||
node->callback(command, arguments, arg_count, node->user_data);
|
||
}
|
||
node = node->next;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Check if string is a number
|
||
*/
|
||
static bool is_number(const char *s)
|
||
{
|
||
if (!s || *s == '\0') return false;
|
||
|
||
size_t len = strlen(s);
|
||
if (len >= 10) return false; /* Prevent overflow */
|
||
|
||
for (size_t i = 0; i < len; i++) {
|
||
if (!isdigit((unsigned char)s[i])) return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
/* Legacy API compatibility (deprecated) */
|
||
int at_uart_send(const uint8_t *data, uint16_t size)
|
||
{
|
||
LISA_LOGW(TAG, "at_uart_send() is deprecated. Use at_uart_send_command() instead.");
|
||
|
||
if (!at_uart_ctx.initialized || !data || size == 0) {
|
||
return -1;
|
||
}
|
||
|
||
xSemaphoreTake(at_uart_ctx.tx_mutex, portMAX_DELAY);
|
||
int ret = lisa_uart_write_sync(at_uart_ctx.uart_dev, (const uint8_t *)data, size, 1000);
|
||
xSemaphoreGive(at_uart_ctx.tx_mutex);
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* @brief Send AT command,todo:返回发送的字节数
|
||
*/
|
||
bool at_uart_send_command(const char *command, size_t timeout_ms, bool add_crlf)
|
||
{
|
||
// LISA_LOGI(TAG, "at_uart_send_command: %s", command);
|
||
return at_uart_send_command_with_data(command, timeout_ms, add_crlf, NULL, 0);
|
||
}
|
||
|
||
/**
|
||
* @brief Send AT command with data
|
||
*/
|
||
bool at_uart_send_command_with_data(const char *command, size_t timeout_ms,
|
||
bool add_crlf, const uint8_t *data, size_t data_len)
|
||
{
|
||
if (!at_uart_ctx.initialized || !command) {
|
||
LISA_LOGE(TAG, "Invalid parameters");
|
||
return false;
|
||
}
|
||
if (at_uart_ctx.uart_config_status){
|
||
return false;
|
||
}
|
||
xSemaphoreTake(at_uart_ctx.cmd_mutex, portMAX_DELAY);
|
||
/* Clear event bits */
|
||
xEventGroupClearBits(at_uart_ctx.event_group,
|
||
AT_EVENT_COMMAND_DONE | AT_EVENT_COMMAND_ERROR | AT_EVENT_DATA_PROMPT);
|
||
at_uart_ctx.wait_for_response = true;
|
||
at_uart_ctx.cme_error_code = 0;
|
||
|
||
/* Clear response buffer */
|
||
xSemaphoreTake(at_uart_ctx.buffer_mutex, portMAX_DELAY);
|
||
at_uart_ctx.response[0] = '\0';
|
||
xSemaphoreGive(at_uart_ctx.buffer_mutex);
|
||
|
||
/* Send command */
|
||
xSemaphoreTake(at_uart_ctx.tx_mutex, portMAX_DELAY);
|
||
|
||
int ret;
|
||
if (add_crlf) {
|
||
size_t cmd_len = strlen(command);
|
||
|
||
/* 防止超长命令 */
|
||
if (cmd_len > 1024) {
|
||
LISA_LOGE(TAG, "Command too long: %zu bytes", cmd_len);
|
||
xSemaphoreGive(at_uart_ctx.tx_mutex);
|
||
at_uart_ctx.wait_for_response = false;
|
||
xSemaphoreGive(at_uart_ctx.cmd_mutex);
|
||
return false;
|
||
}
|
||
|
||
/* 分配缓冲区: 命令长度 + "\r\n" + '\0' */
|
||
char *cmd_with_crlf = (char *)malloc(cmd_len + 3);
|
||
if (!cmd_with_crlf) {
|
||
LISA_LOGE(TAG, "Failed to allocate memory for command");
|
||
xSemaphoreGive(at_uart_ctx.tx_mutex);
|
||
at_uart_ctx.wait_for_response = false;
|
||
xSemaphoreGive(at_uart_ctx.cmd_mutex);
|
||
return false;
|
||
}
|
||
|
||
/* 安全地构造带 CRLF 的命令 */
|
||
memcpy(cmd_with_crlf, command, cmd_len);
|
||
cmd_with_crlf[cmd_len] = '\r';
|
||
cmd_with_crlf[cmd_len + 1] = '\n';
|
||
cmd_with_crlf[cmd_len + 2] = '\0';
|
||
|
||
ret = lisa_uart_write_sync(at_uart_ctx.uart_dev, (const uint8_t *)cmd_with_crlf, cmd_len + 2, 1000);
|
||
free(cmd_with_crlf);
|
||
} else {
|
||
ret = lisa_uart_write_sync(at_uart_ctx.uart_dev, (const uint8_t *)command, strlen(command), 1000);
|
||
}
|
||
|
||
xSemaphoreGive(at_uart_ctx.tx_mutex);
|
||
|
||
if (ret < 0) {
|
||
LISA_LOGE(TAG, "Failed to send command");
|
||
at_uart_ctx.wait_for_response = false;
|
||
xSemaphoreGive(at_uart_ctx.cmd_mutex);
|
||
return false;
|
||
}
|
||
/* Wait for response */
|
||
bool result = false;
|
||
if (timeout_ms > 0) {
|
||
EventBits_t bits = xEventGroupWaitBits(at_uart_ctx.event_group,
|
||
AT_EVENT_COMMAND_DONE | AT_EVENT_COMMAND_ERROR | AT_EVENT_DATA_PROMPT,
|
||
pdTRUE, pdFALSE,
|
||
pdMS_TO_TICKS(timeout_ms));
|
||
at_uart_ctx.wait_for_response = false;
|
||
|
||
if (bits & AT_EVENT_COMMAND_DONE) {
|
||
result = true;
|
||
} else if (bits & AT_EVENT_DATA_PROMPT) {
|
||
/* '>' prompt received, ready to send data */
|
||
result = true;
|
||
}
|
||
} else {
|
||
at_uart_ctx.wait_for_response = false;
|
||
result = true;
|
||
}
|
||
|
||
/* Send data payload if provided */
|
||
if (result && data && data_len > 0) {
|
||
at_uart_ctx.wait_for_response = true;
|
||
xEventGroupClearBits(at_uart_ctx.event_group,
|
||
AT_EVENT_COMMAND_DONE | AT_EVENT_COMMAND_ERROR);
|
||
|
||
xSemaphoreTake(at_uart_ctx.tx_mutex, portMAX_DELAY);
|
||
ret = lisa_uart_write_sync(at_uart_ctx.uart_dev, (const uint8_t *)data, data_len, 1000);
|
||
xSemaphoreGive(at_uart_ctx.tx_mutex);
|
||
|
||
if (ret < 0) {
|
||
LISA_LOGE(TAG, "Failed to send data");
|
||
result = false;
|
||
} else {
|
||
EventBits_t bits = xEventGroupWaitBits(at_uart_ctx.event_group,
|
||
AT_EVENT_COMMAND_DONE | AT_EVENT_COMMAND_ERROR,
|
||
pdTRUE, pdFALSE,
|
||
pdMS_TO_TICKS(timeout_ms));
|
||
result = (bits & AT_EVENT_COMMAND_DONE) != 0;
|
||
}
|
||
|
||
at_uart_ctx.wait_for_response = false;
|
||
}
|
||
xSemaphoreGive(at_uart_ctx.cmd_mutex);
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Get CME error code
|
||
*/
|
||
int at_uart_get_cme_error_code(void)
|
||
{
|
||
return at_uart_ctx.cme_error_code;
|
||
}
|
||
|
||
/**
|
||
* @brief Parse AT command arguments
|
||
*/
|
||
static void parse_arguments(const char *values, at_arg_value_t **out_args, size_t *out_count)
|
||
{
|
||
if (!values || !out_args || !out_count) {
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
|
||
/* Allocate argument array */
|
||
size_t capacity = 4;
|
||
at_arg_value_t *args = (at_arg_value_t *)calloc(capacity, sizeof(at_arg_value_t));
|
||
size_t count = 0;
|
||
|
||
const char *p = values;
|
||
const char *start = p;
|
||
|
||
/* 动态分配item缓冲区,支持大数据 (如TCP hex数据可达2KB+) */
|
||
#define MAX_ARG_ITEM_SIZE 10240
|
||
char *item = (char *)malloc(MAX_ARG_ITEM_SIZE);
|
||
if (!item) {
|
||
free(args);
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
|
||
while (*p) {
|
||
/* Find delimiter or end */
|
||
if (*p == ',' || *p == '\r' || *p == '\n' || *p == '\0') {
|
||
size_t item_len = p - start;
|
||
|
||
/* 检查缓冲区大小,确保有空间存储null终止符 */
|
||
if (item_len >= MAX_ARG_ITEM_SIZE) {
|
||
LISA_LOGE(TAG, "Argument too long (%zu bytes), truncating to %d", item_len, MAX_ARG_ITEM_SIZE - 1);
|
||
item_len = MAX_ARG_ITEM_SIZE - 1;
|
||
}
|
||
|
||
/* Expand capacity if needed */
|
||
if (count >= capacity) {
|
||
capacity *= 2;
|
||
at_arg_value_t *new_args = (at_arg_value_t *)realloc(args, capacity * sizeof(at_arg_value_t));
|
||
if (new_args) {
|
||
args = new_args;
|
||
} else {
|
||
/* Memory allocation failed */
|
||
at_arg_array_destroy(args, count);
|
||
free(item);
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* Copy argument (包括空参数,如 1,,,,"INITIAL" 中的空项) */
|
||
memcpy(item, start, item_len);
|
||
item[item_len] = '\0';
|
||
|
||
/* Trim whitespace */
|
||
char *trimmed = item;
|
||
while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
|
||
char *end = trimmed + strlen(trimmed) - 1;
|
||
while (end > trimmed && (*end == ' ' || *end == '\t')) *end-- = '\0';
|
||
|
||
/* Identify type */
|
||
if (strlen(trimmed) == 0) {
|
||
/* 空参数: 保留为空字符串 */
|
||
args[count].type = AT_ARG_TYPE_STRING;
|
||
args[count].data.string_val.value = strdup("");
|
||
if (!args[count].data.string_val.value) {
|
||
LISA_LOGE(TAG, "Failed to allocate memory for empty string");
|
||
at_arg_array_destroy(args, count);
|
||
free(item);
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
args[count].data.string_val.len = 0;
|
||
} else if (trimmed[0] == '"' && strlen(trimmed) > 1) {
|
||
/* String type */
|
||
args[count].type = AT_ARG_TYPE_STRING;
|
||
size_t str_len = strlen(trimmed) - 2;
|
||
args[count].data.string_val.value = (char *)malloc(str_len + 1);
|
||
if (!args[count].data.string_val.value) {
|
||
LISA_LOGE(TAG, "Failed to allocate memory for string argument");
|
||
at_arg_array_destroy(args, count);
|
||
free(item);
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
memcpy(args[count].data.string_val.value, trimmed + 1, str_len);
|
||
args[count].data.string_val.value[str_len] = '\0';
|
||
args[count].data.string_val.len = str_len;
|
||
} else if (is_number(trimmed)) {
|
||
/* Integer type */
|
||
args[count].type = AT_ARG_TYPE_INT;
|
||
args[count].data.int_val = atoi(trimmed);
|
||
} else {
|
||
/* Other as string */
|
||
args[count].type = AT_ARG_TYPE_STRING;
|
||
args[count].data.string_val.value = strdup(trimmed);
|
||
if (!args[count].data.string_val.value) {
|
||
LISA_LOGE(TAG, "Failed to allocate memory for string argument");
|
||
at_arg_array_destroy(args, count);
|
||
free(item);
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
args[count].data.string_val.len = strlen(trimmed);
|
||
}
|
||
|
||
count++;
|
||
|
||
/* 只在遇到行结束符时才退出循环 */
|
||
if (*p == '\r' || *p == '\n' || *p == '\0') break;
|
||
|
||
start = p + 1;
|
||
}
|
||
p++;
|
||
}
|
||
|
||
/* 处理最后一个参数(如果字符串不以逗号结尾) */
|
||
if (p > start) {
|
||
size_t item_len = p - start;
|
||
|
||
/* 检查缓冲区大小,确保有空间存储null终止符 */
|
||
if (item_len >= MAX_ARG_ITEM_SIZE) {
|
||
LISA_LOGE(TAG, "Last argument too long (%zu bytes), truncating to %d", item_len, MAX_ARG_ITEM_SIZE - 1);
|
||
item_len = MAX_ARG_ITEM_SIZE - 1;
|
||
}
|
||
|
||
/* Expand capacity if needed */
|
||
if (count >= capacity) {
|
||
capacity *= 2;
|
||
at_arg_value_t *new_args = (at_arg_value_t *)realloc(args, capacity * sizeof(at_arg_value_t));
|
||
if (new_args) {
|
||
args = new_args;
|
||
} else {
|
||
/* Memory allocation failed */
|
||
at_arg_array_destroy(args, count);
|
||
free(item);
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* Copy last argument */
|
||
memcpy(item, start, item_len);
|
||
item[item_len] = '\0';
|
||
|
||
/* Trim whitespace */
|
||
char *trimmed = item;
|
||
while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
|
||
char *end = trimmed + strlen(trimmed) - 1;
|
||
while (end > trimmed && (*end == ' ' || *end == '\t')) *end-- = '\0';
|
||
|
||
/* Identify type */
|
||
if (strlen(trimmed) == 0) {
|
||
args[count].type = AT_ARG_TYPE_STRING;
|
||
args[count].data.string_val.value = strdup("");
|
||
if (!args[count].data.string_val.value) {
|
||
LISA_LOGE(TAG, "Failed to allocate memory for empty string");
|
||
at_arg_array_destroy(args, count);
|
||
free(item);
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
args[count].data.string_val.len = 0;
|
||
} else if (trimmed[0] == '"' && strlen(trimmed) > 1) {
|
||
args[count].type = AT_ARG_TYPE_STRING;
|
||
size_t str_len = strlen(trimmed) - 2;
|
||
args[count].data.string_val.value = (char *)malloc(str_len + 1);
|
||
if (!args[count].data.string_val.value) {
|
||
LISA_LOGE(TAG, "Failed to allocate memory for string argument");
|
||
at_arg_array_destroy(args, count);
|
||
free(item);
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
memcpy(args[count].data.string_val.value, trimmed + 1, str_len);
|
||
args[count].data.string_val.value[str_len] = '\0';
|
||
args[count].data.string_val.len = str_len;
|
||
} else if (is_number(trimmed)) {
|
||
args[count].type = AT_ARG_TYPE_INT;
|
||
args[count].data.int_val = atoi(trimmed);
|
||
} else {
|
||
args[count].type = AT_ARG_TYPE_STRING;
|
||
args[count].data.string_val.value = strdup(trimmed);
|
||
if (!args[count].data.string_val.value) {
|
||
LISA_LOGE(TAG, "Failed to allocate memory for string argument");
|
||
at_arg_array_destroy(args, count);
|
||
free(item);
|
||
*out_args = NULL;
|
||
*out_count = 0;
|
||
return;
|
||
}
|
||
args[count].data.string_val.len = strlen(trimmed);
|
||
}
|
||
|
||
count++;
|
||
}
|
||
|
||
/* 释放临时缓冲区 */
|
||
free(item);
|
||
|
||
*out_args = args;
|
||
*out_count = count;
|
||
}
|
||
|
||
/**
|
||
* @brief Register URC callback
|
||
*/
|
||
at_urc_callback_node_t *at_uart_register_urc_callback(at_urc_callback_t callback, void *user_data)
|
||
{
|
||
if (!at_uart_ctx.initialized || !callback) {
|
||
return NULL;
|
||
}
|
||
|
||
at_urc_callback_node_t *node = (at_urc_callback_node_t *)malloc(sizeof(at_urc_callback_node_t));
|
||
if (!node) {
|
||
return NULL;
|
||
}
|
||
|
||
node->callback = callback;
|
||
node->user_data = user_data;
|
||
|
||
xSemaphoreTake(at_uart_ctx.buffer_mutex, portMAX_DELAY);
|
||
node->next = at_uart_ctx.urc_callbacks_head;
|
||
at_uart_ctx.urc_callbacks_head = node;
|
||
xSemaphoreGive(at_uart_ctx.buffer_mutex);
|
||
|
||
LISA_LOGI(TAG, "URC callback registered");
|
||
|
||
return node;
|
||
}
|
||
|
||
/**
|
||
* @brief Unregister URC callback
|
||
*/
|
||
void at_uart_unregister_urc_callback(at_urc_callback_node_t *node)
|
||
{
|
||
if (!at_uart_ctx.initialized || !node) {
|
||
return;
|
||
}
|
||
|
||
xSemaphoreTake(at_uart_ctx.buffer_mutex, portMAX_DELAY);
|
||
|
||
at_urc_callback_node_t **current = &at_uart_ctx.urc_callbacks_head;
|
||
while (*current) {
|
||
if (*current == node) {
|
||
*current = node->next;
|
||
free(node);
|
||
LISA_LOGI(TAG, "URC callback unregistered");
|
||
break;
|
||
}
|
||
current = &(*current)->next;
|
||
}
|
||
|
||
xSemaphoreGive(at_uart_ctx.buffer_mutex);
|
||
}
|
||
|
||
/**
|
||
* @brief Destroy argument array
|
||
*/
|
||
void at_arg_array_destroy(at_arg_value_t *args, size_t count)
|
||
{
|
||
if (!args) return;
|
||
|
||
for (size_t i = 0; i < count; i++) {
|
||
if (args[i].type == AT_ARG_TYPE_STRING && args[i].data.string_val.value) {
|
||
free(args[i].data.string_val.value);
|
||
}
|
||
}
|
||
free(args);
|
||
}
|
||
|
||
/**
|
||
* @brief Parse response from RX buffer
|
||
*/
|
||
static bool parse_response(void)
|
||
{
|
||
if (!at_uart_ctx.rx_buffer || at_uart_ctx.rx_buffer_size == 0) {
|
||
return false;
|
||
}
|
||
|
||
/* Check for '>' prompt (data input mode) */
|
||
if (at_uart_ctx.wait_for_response && at_uart_ctx.rx_buffer[0] == '>') {
|
||
size_t remaining = at_uart_ctx.rx_buffer_size - AT_RESPONSE_PROMPT_LEN;
|
||
if (remaining > 0) {
|
||
memmove(at_uart_ctx.rx_buffer, at_uart_ctx.rx_buffer + AT_RESPONSE_PROMPT_LEN, remaining);
|
||
}
|
||
at_uart_ctx.rx_buffer_size = remaining;
|
||
at_uart_ctx.rx_buffer[at_uart_ctx.rx_buffer_size] = '\0';
|
||
|
||
xEventGroupSetBits(at_uart_ctx.event_group, AT_EVENT_DATA_PROMPT);
|
||
return true;
|
||
}
|
||
|
||
/* Find line ending */
|
||
char *end_pos = strstr(at_uart_ctx.rx_buffer, "\r\n");
|
||
if (!end_pos) {
|
||
return false; /* Wait for more data */
|
||
}
|
||
|
||
size_t line_len = end_pos - at_uart_ctx.rx_buffer;
|
||
|
||
/* Ignore empty lines */
|
||
if (line_len == 0) {
|
||
size_t remaining = at_uart_ctx.rx_buffer_size - AT_RESPONSE_CRLF_LEN;
|
||
if (remaining > 0) {
|
||
memmove(at_uart_ctx.rx_buffer, at_uart_ctx.rx_buffer + AT_RESPONSE_CRLF_LEN, remaining);
|
||
}
|
||
at_uart_ctx.rx_buffer_size = remaining;
|
||
at_uart_ctx.rx_buffer[at_uart_ctx.rx_buffer_size] = '\0';
|
||
return true;
|
||
}
|
||
|
||
bool handled = false;
|
||
/* 安全日志: 限制打印长度,防止缓冲区溢出 */
|
||
// if (line_len <= 100) {
|
||
// LISA_LOGE(TAG, "parse_response recv: %.*s", (int)line_len, at_uart_ctx.rx_buffer);
|
||
// } else {
|
||
// LISA_LOGE(TAG, "parse_response recv (len=%zu): %.*s...", line_len, 150, at_uart_ctx.rx_buffer);
|
||
// }
|
||
/* URC message (starts with '+') */
|
||
if (at_uart_ctx.rx_buffer[0] == '+') {
|
||
char command[64] = {0};
|
||
const char *colon = strchr(at_uart_ctx.rx_buffer, ':');
|
||
if (colon && (size_t)(colon - at_uart_ctx.rx_buffer) < line_len) {
|
||
/* URC with arguments: +COMMAND: value1,value2 */
|
||
size_t cmd_len = colon - at_uart_ctx.rx_buffer - 1; /* Skip '+' */
|
||
if (cmd_len < sizeof(command)) {
|
||
strncpy(command, at_uart_ctx.rx_buffer + 1, cmd_len);
|
||
command[cmd_len] = '\0';
|
||
|
||
/* Parse arguments - 安全地跳过 ": " */
|
||
size_t colon_offset = colon - at_uart_ctx.rx_buffer;
|
||
|
||
/* 计算参数字符串的长度: 从冒号后到行尾 */
|
||
size_t values_len = 0;
|
||
const char *values_start = NULL;
|
||
|
||
/* 检查冒号后是否还有至少2个字符 (空格和数据) */
|
||
if (colon_offset + 2 < line_len) {
|
||
values_start = colon + 2; /* Skip ": " */
|
||
values_len = line_len - (colon_offset + 2);
|
||
} else if (colon_offset + 1 < line_len) {
|
||
values_start = colon + 1; /* 只跳过冒号,没有空格 */
|
||
values_len = line_len - (colon_offset + 1);
|
||
} else {
|
||
values_start = ""; /* 冒号后没有数据 */
|
||
values_len = 0;
|
||
}
|
||
|
||
/* 复制参数字符串到临时缓冲区,确保只包含当前行 */
|
||
char *values = NULL;
|
||
if (values_len > 0) {
|
||
values = (char *)malloc(values_len + 1);
|
||
if (values) {
|
||
memcpy(values, values_start, values_len);
|
||
values[values_len] = '\0';
|
||
} else {
|
||
LISA_LOGE(TAG, "Failed to allocate memory for URC values");
|
||
values = strdup(""); /* 尝试使用空字符串 */
|
||
if (!values) {
|
||
return false; /* 内存严重不足,跳过此URC */
|
||
}
|
||
}
|
||
} else {
|
||
values = strdup("");
|
||
if (!values) {
|
||
LISA_LOGE(TAG, "Failed to allocate memory for empty URC values");
|
||
return false; /* 内存严重不足,跳过此URC */
|
||
}
|
||
}
|
||
|
||
at_arg_value_t *args = NULL;
|
||
size_t arg_count = 0;
|
||
|
||
parse_arguments(values, &args, &arg_count);
|
||
handle_urc(command, args, arg_count);
|
||
|
||
/* Free arguments */
|
||
at_arg_array_destroy(args, arg_count);
|
||
|
||
/* Free values buffer */
|
||
free(values);
|
||
}
|
||
} else {
|
||
/* URC without arguments: +COMMAND */
|
||
size_t cmd_len = line_len - 1; /* Skip '+' */
|
||
if (cmd_len < sizeof(command)) {
|
||
strncpy(command, at_uart_ctx.rx_buffer + 1, cmd_len);
|
||
command[cmd_len] = '\0';
|
||
handle_urc(command, NULL, 0);
|
||
}
|
||
}
|
||
handled = true;
|
||
} else if (at_uart_ctx.rx_buffer_size >= AT_RESPONSE_OK_LEN &&
|
||
at_uart_ctx.rx_buffer[0] == 'O' &&
|
||
at_uart_ctx.rx_buffer[1] == 'K' &&
|
||
at_uart_ctx.rx_buffer[2] == '\r' &&
|
||
at_uart_ctx.rx_buffer[3] == '\n') {
|
||
size_t remaining = at_uart_ctx.rx_buffer_size - AT_RESPONSE_OK_LEN;
|
||
if (remaining > 0) {
|
||
memmove(at_uart_ctx.rx_buffer, at_uart_ctx.rx_buffer + AT_RESPONSE_OK_LEN, remaining);
|
||
}
|
||
at_uart_ctx.rx_buffer_size = remaining;
|
||
at_uart_ctx.rx_buffer[at_uart_ctx.rx_buffer_size] = '\0';
|
||
|
||
xEventGroupSetBits(at_uart_ctx.event_group, AT_EVENT_COMMAND_DONE);
|
||
return true;
|
||
} else if (at_uart_ctx.rx_buffer_size >= AT_RESPONSE_ERROR_LEN &&
|
||
memcmp(at_uart_ctx.rx_buffer, "ERROR\r\n", AT_RESPONSE_ERROR_LEN) == 0) {
|
||
size_t remaining = at_uart_ctx.rx_buffer_size - AT_RESPONSE_ERROR_LEN;
|
||
if (remaining > 0) {
|
||
memmove(at_uart_ctx.rx_buffer, at_uart_ctx.rx_buffer + AT_RESPONSE_ERROR_LEN, remaining);
|
||
}
|
||
at_uart_ctx.rx_buffer_size = remaining;
|
||
at_uart_ctx.rx_buffer[at_uart_ctx.rx_buffer_size] = '\0';
|
||
|
||
xEventGroupSetBits(at_uart_ctx.event_group, AT_EVENT_COMMAND_ERROR);
|
||
return true;
|
||
} else {
|
||
size_t copy_len = (line_len < RESPONSE_BUFFER_SIZE - 1) ?
|
||
line_len : (RESPONSE_BUFFER_SIZE - 1);
|
||
memcpy(at_uart_ctx.response, at_uart_ctx.rx_buffer, copy_len);
|
||
at_uart_ctx.response[copy_len] = '\0';
|
||
LISA_LOGE(TAG, "response: %s--------", at_uart_ctx.response);
|
||
handled = true;
|
||
}
|
||
|
||
/* Remove processed line */
|
||
if (handled) {
|
||
size_t bytes_to_remove = line_len + AT_RESPONSE_CRLF_LEN; /* line + "\r\n" */
|
||
|
||
/* 防止下溢: 确保缓冲区足够大 */
|
||
if (bytes_to_remove > at_uart_ctx.rx_buffer_size) {
|
||
LISA_LOGE(TAG, "BUG: bytes_to_remove(%zu) > buffer_size(%zu), corrupted state!",
|
||
bytes_to_remove, at_uart_ctx.rx_buffer_size);
|
||
/* 清空缓冲区避免进一步错误 */
|
||
at_uart_ctx.rx_buffer_size = 0;
|
||
at_uart_ctx.rx_buffer[0] = '\0';
|
||
return false;
|
||
}
|
||
|
||
/* 防止 memmove 源指针越界: 确保 end_pos + AT_RESPONSE_CRLF_LEN 在缓冲区内 */
|
||
size_t end_offset = end_pos - at_uart_ctx.rx_buffer + AT_RESPONSE_CRLF_LEN;
|
||
if (end_offset > at_uart_ctx.rx_buffer_size) {
|
||
LISA_LOGE(TAG, "BUG: end_offset(%zu) > buffer_size(%zu), corrupted!",
|
||
end_offset, at_uart_ctx.rx_buffer_size);
|
||
at_uart_ctx.rx_buffer_size = 0;
|
||
at_uart_ctx.rx_buffer[0] = '\0';
|
||
return false;
|
||
}
|
||
|
||
/* 计算剩余数据的实际长度 */
|
||
size_t remaining_bytes = at_uart_ctx.rx_buffer_size - bytes_to_remove;
|
||
at_uart_ctx.rx_buffer_size = remaining_bytes;
|
||
|
||
/* 只移动剩余数据,避免越界读取 */
|
||
if (remaining_bytes > 0) {
|
||
memmove(at_uart_ctx.rx_buffer, end_pos + AT_RESPONSE_CRLF_LEN, remaining_bytes);
|
||
}
|
||
at_uart_ctx.rx_buffer[at_uart_ctx.rx_buffer_size] = '\0';
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @brief Process received data from AT UART
|
||
*/
|
||
static void at_uart_process_data(uint8_t *data, int len)
|
||
{
|
||
if (len <= 0) {
|
||
return;
|
||
}
|
||
|
||
xSemaphoreTake(at_uart_ctx.buffer_mutex, portMAX_DELAY);
|
||
|
||
/* Ensure buffer has enough space (需要额外1字节存储'\0') */
|
||
if (at_uart_ctx.rx_buffer_size + len + 1 > at_uart_ctx.rx_buffer_capacity) {
|
||
/* 扩容: 当前大小 + 新数据长度 + 1(终止符) + 512(额外空间) */
|
||
at_uart_ctx.rx_buffer_capacity = at_uart_ctx.rx_buffer_size + len + 1 + 512;
|
||
char *new_buffer = (char *)realloc(at_uart_ctx.rx_buffer, at_uart_ctx.rx_buffer_capacity);
|
||
if (new_buffer) {
|
||
at_uart_ctx.rx_buffer = new_buffer;
|
||
} else {
|
||
LISA_LOGE(TAG, "Failed to reallocate rx_buffer");
|
||
xSemaphoreGive(at_uart_ctx.buffer_mutex);
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* Append data to buffer */
|
||
memcpy(at_uart_ctx.rx_buffer + at_uart_ctx.rx_buffer_size, data, len);
|
||
at_uart_ctx.rx_buffer_size += len;
|
||
at_uart_ctx.rx_buffer[at_uart_ctx.rx_buffer_size] = '\0';
|
||
|
||
/* Parse all available responses */
|
||
while (parse_response()) {}
|
||
|
||
xSemaphoreGive(at_uart_ctx.buffer_mutex);
|
||
}
|
||
|
||
/**
|
||
* @brief AT UART data processing task
|
||
*/
|
||
static void at_uart_process_task(void *pvParameters)
|
||
{
|
||
int ret;
|
||
|
||
LISA_LOGI(TAG, "AT UART process task started");
|
||
|
||
while (1) {
|
||
if (at_uart_ctx.uart_config_status){
|
||
vTaskDelay(pdMS_TO_TICKS(10));
|
||
continue;
|
||
}
|
||
/* Synchronous receive data (blocking wait) */
|
||
ret = lisa_uart_read_sync(at_uart_ctx.uart_dev, at_uart_ctx.rx_hw_buffer,
|
||
sizeof(at_uart_ctx.rx_hw_buffer));
|
||
// LISA_LOGI(TAG, "UART read data : %d, %s", ret, at_uart_ctx.rx_hw_buffer);
|
||
if (ret < 0) {
|
||
if (ret == LISA_DEVICE_ERR_OVERFLOW) {
|
||
LISA_LOGE(TAG, "Buffer overflow! Recovering...");
|
||
|
||
/* 禁用接收,等待硬件FIFO清空 */
|
||
lisa_uart_rx_disable(at_uart_ctx.uart_dev);
|
||
vTaskDelay(pdMS_TO_TICKS(10));
|
||
|
||
/*缓冲区状态 */
|
||
xSemaphoreTake(at_uart_ctx.buffer_mutex, portMAX_DELAY);
|
||
at_uart_ctx.rx_buffer_size = 0;
|
||
if (at_uart_ctx.rx_buffer) {
|
||
at_uart_ctx.rx_buffer[0] = '\0';
|
||
}
|
||
at_uart_ctx.response[0] = '\0';
|
||
xSemaphoreGive(at_uart_ctx.buffer_mutex);
|
||
|
||
/* 通知所有等待响应的命令:发生错误 */
|
||
if (at_uart_ctx.wait_for_response) {
|
||
xEventGroupSetBits(at_uart_ctx.event_group, AT_EVENT_COMMAND_ERROR);
|
||
at_uart_ctx.wait_for_response = false;
|
||
}
|
||
|
||
/* 重新启用接收 */
|
||
lisa_uart_rx_enable(at_uart_ctx.uart_dev);
|
||
|
||
LISA_LOGI(TAG, "Buffer overflow recovery completed, AT state cleared");
|
||
} else {
|
||
LISA_LOGE(TAG, "Failed to read data (ret=%d)", ret);
|
||
}
|
||
// vTaskDelay(pdMS_TO_TICKS(1));
|
||
} else if (ret > 0) {
|
||
/* Process received data */
|
||
at_uart_process_data(at_uart_ctx.rx_hw_buffer, ret);
|
||
memset(at_uart_ctx.rx_hw_buffer, 0, sizeof(at_uart_ctx.rx_hw_buffer));
|
||
}
|
||
vTaskDelay(pdMS_TO_TICKS(1));
|
||
}
|
||
}
|
||
|
||
bool uart_baudrate_adapt(void)
|
||
{
|
||
/* Configure UART */
|
||
lisa_uart_config_t uart_config = LISA_UART_CONFIG_HIGH_SPEED();
|
||
uart_config.rx_buf_config.buffer_count = AT_UART_RX_BUF_COUNT;
|
||
uart_config.rx_buf_config.buffer_size = AT_UART_RX_BUF_SIZE;
|
||
|
||
/* Test AT communication */
|
||
int retry, switch_count = 0;
|
||
const int max_retries_per_baudrate = 5;
|
||
const int max_switch_times = 5; /* 最多切换5次波特率 */
|
||
bool at_ready = false;
|
||
|
||
for (switch_count = 0; switch_count < max_switch_times && !at_ready; switch_count++) {
|
||
/* 每个波特率重试5次 */
|
||
for (retry = 0; retry < max_retries_per_baudrate; retry++) {
|
||
if (at_uart_send_command("AT", 1000, true)) {
|
||
LISA_LOGI(TAG, "4G AT is ready: %d, baudrate=%d.", __LINE__, uart_config.baudrate);
|
||
at_ready = true;
|
||
break;
|
||
}
|
||
LISA_LOGI(TAG, "4G AT retry %d/%d at baudrate %d.", retry + 1, max_retries_per_baudrate, uart_config.baudrate);
|
||
vTaskDelay(pdMS_TO_TICKS(200));
|
||
}
|
||
|
||
if (at_ready) {
|
||
break;
|
||
}
|
||
|
||
/* 5次重试都失败,切换波特率 */
|
||
if (uart_config.baudrate == AT_UART_DEFAULT_BAUDRATE) {
|
||
uart_config.baudrate = AT_UART_USED_BAUDRATE;
|
||
} else {
|
||
uart_config.baudrate = AT_UART_DEFAULT_BAUDRATE;
|
||
}
|
||
|
||
LISA_LOGI(TAG, "Switching baudrate to %d.", uart_config.baudrate);
|
||
|
||
at_uart_ctx.uart_config_status = 1;
|
||
lisa_uart_rx_disable(at_uart_ctx.uart_dev);
|
||
int ret = lisa_uart_configure(at_uart_ctx.uart_dev, &uart_config);
|
||
lisa_uart_rx_enable(at_uart_ctx.uart_dev);
|
||
at_uart_ctx.uart_config_status = 0;
|
||
if (ret != 0) {
|
||
LISA_LOGE(TAG, "Failed to configure UART");
|
||
free(at_uart_ctx.rx_buffer);
|
||
return false;
|
||
}
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
}
|
||
|
||
if (!at_ready) {
|
||
LISA_LOGE(TAG, "4G AT is not ready after %d baudrate switches!", max_switch_times);
|
||
return false;
|
||
}
|
||
|
||
/*修改串口波特率为921600*/
|
||
if (uart_config.baudrate != AT_UART_USED_BAUDRATE) {
|
||
for (retry = 0; retry < 5; retry++) {
|
||
if (at_uart_send_command("AT+IPR=921600", 1000, true)) {
|
||
LISA_LOGI(TAG, "uart baudrate is 921600.");
|
||
|
||
uart_config.baudrate = AT_UART_USED_BAUDRATE;
|
||
at_uart_ctx.uart_config_status = 1;
|
||
// vTaskDelay(pdMS_TO_TICKS(20));
|
||
LISA_LOGI(TAG, "lisa_uart_configure: %d.", uart_config.baudrate);
|
||
lisa_uart_rx_disable(at_uart_ctx.uart_dev);
|
||
int ret = lisa_uart_configure(at_uart_ctx.uart_dev, &uart_config);
|
||
lisa_uart_rx_enable(at_uart_ctx.uart_dev);
|
||
// vTaskDelay(pdMS_TO_TICKS(20));
|
||
at_uart_ctx.uart_config_status = 0;
|
||
if (ret != 0) {
|
||
LISA_LOGE(TAG, "Failed to configure UART");
|
||
free(at_uart_ctx.rx_buffer);
|
||
return false;
|
||
}
|
||
break;
|
||
}
|
||
vTaskDelay(pdMS_TO_TICKS(200));
|
||
}
|
||
if (retry >= 5) {
|
||
LISA_LOGE(TAG, "4G AT is not ready: %d, %d.", __LINE__, uart_config.baudrate);
|
||
return false;
|
||
}
|
||
|
||
for (retry = 0; retry < 15; retry++) {
|
||
if (at_uart_send_command("AT", 1000, true)) {
|
||
LISA_LOGI(TAG, "4G AT is ready, 921600, %d.", __LINE__);
|
||
break;
|
||
}
|
||
LISA_LOGI(TAG, "4G AT is not ready, 921600, %d.", __LINE__);
|
||
vTaskDelay(pdMS_TO_TICKS(200));
|
||
}
|
||
if (retry >= 15) {
|
||
LISA_LOGE(TAG, "4G AT baudrate adapt fail, 921600, %d.", __LINE__);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @brief Initialize AT UART
|
||
*/
|
||
int at_uart_init(const char *uart_dev_name)
|
||
{
|
||
int ret;
|
||
|
||
if (at_uart_ctx.initialized) {
|
||
LISA_LOGW(TAG, "AT UART already initialized");
|
||
return 0;
|
||
}
|
||
|
||
LISA_LOGI(TAG, "Initializing AT UART module...");
|
||
|
||
/* Allocate dynamic receive buffer */
|
||
at_uart_ctx.rx_buffer_capacity = RX_BUFFER_INITIAL_SIZE;
|
||
at_uart_ctx.rx_buffer = (char *)malloc(at_uart_ctx.rx_buffer_capacity);
|
||
if (!at_uart_ctx.rx_buffer) {
|
||
LISA_LOGE(TAG, "Failed to allocate rx_buffer");
|
||
return -1;
|
||
}
|
||
at_uart_ctx.rx_buffer_size = 0;
|
||
at_uart_ctx.rx_buffer[0] = '\0';
|
||
|
||
/* Get UART device */
|
||
at_uart_ctx.uart_dev = lisa_device_get(uart_dev_name);
|
||
if (!lisa_device_ready(at_uart_ctx.uart_dev)) {
|
||
LISA_LOGE(TAG, "Failed to get UART device or device not ready");
|
||
free(at_uart_ctx.rx_buffer);
|
||
return -1;
|
||
}
|
||
|
||
lisa_uart_config_t uart_config = LISA_UART_CONFIG_HIGH_SPEED();
|
||
uart_config.rx_buf_config.buffer_count = AT_UART_RX_BUF_COUNT;
|
||
uart_config.rx_buf_config.buffer_size = AT_UART_RX_BUF_SIZE;
|
||
|
||
ret = lisa_uart_configure(at_uart_ctx.uart_dev, &uart_config);
|
||
if (ret != 0) {
|
||
LISA_LOGE(TAG, "Failed to configure UART");
|
||
free(at_uart_ctx.rx_buffer);
|
||
return -1;
|
||
}
|
||
|
||
/* Create synchronization objects */
|
||
at_uart_ctx.tx_mutex = xSemaphoreCreateMutex();
|
||
at_uart_ctx.cmd_mutex = xSemaphoreCreateMutex();
|
||
at_uart_ctx.buffer_mutex = xSemaphoreCreateMutex();
|
||
at_uart_ctx.event_group = xEventGroupCreate();
|
||
|
||
if (!at_uart_ctx.tx_mutex || !at_uart_ctx.cmd_mutex ||
|
||
!at_uart_ctx.buffer_mutex || !at_uart_ctx.event_group) {
|
||
LISA_LOGE(TAG, "Failed to create synchronization objects");
|
||
if (at_uart_ctx.tx_mutex) vSemaphoreDelete(at_uart_ctx.tx_mutex);
|
||
if (at_uart_ctx.cmd_mutex) vSemaphoreDelete(at_uart_ctx.cmd_mutex);
|
||
if (at_uart_ctx.buffer_mutex) vSemaphoreDelete(at_uart_ctx.buffer_mutex);
|
||
if (at_uart_ctx.event_group) vEventGroupDelete(at_uart_ctx.event_group);
|
||
free(at_uart_ctx.rx_buffer);
|
||
return -1;
|
||
}
|
||
|
||
/* Enable reception */
|
||
ret = lisa_uart_rx_enable(at_uart_ctx.uart_dev);
|
||
if (ret != 0) {
|
||
LISA_LOGE(TAG, "Failed to enable UART RX");
|
||
vSemaphoreDelete(at_uart_ctx.tx_mutex);
|
||
vSemaphoreDelete(at_uart_ctx.cmd_mutex);
|
||
vSemaphoreDelete(at_uart_ctx.buffer_mutex);
|
||
vEventGroupDelete(at_uart_ctx.event_group);
|
||
free(at_uart_ctx.rx_buffer);
|
||
return -1;
|
||
}
|
||
|
||
/* Create data processing task */
|
||
ret = xTaskCreate(at_uart_process_task,
|
||
"at_uart_proc",
|
||
AT_UART_TASK_STACK_SIZE,
|
||
NULL,
|
||
AT_UART_TASK_PRIORITY,
|
||
&at_uart_ctx.process_task);
|
||
if (ret != pdPASS) {
|
||
LISA_LOGE(TAG, "Failed to create process task");
|
||
lisa_uart_rx_disable(at_uart_ctx.uart_dev);
|
||
vSemaphoreDelete(at_uart_ctx.tx_mutex);
|
||
vSemaphoreDelete(at_uart_ctx.cmd_mutex);
|
||
vSemaphoreDelete(at_uart_ctx.buffer_mutex);
|
||
vEventGroupDelete(at_uart_ctx.event_group);
|
||
free(at_uart_ctx.rx_buffer);
|
||
return -1;
|
||
}
|
||
|
||
/* Initialize state */
|
||
at_uart_ctx.wait_for_response = false;
|
||
at_uart_ctx.cme_error_code = 0;
|
||
at_uart_ctx.response[0] = '\0';
|
||
at_uart_ctx.urc_callbacks_head = NULL;
|
||
at_uart_ctx.initialized = 1;
|
||
|
||
LISA_LOGI(TAG, "AT UART module initialized successfully");
|
||
LISA_LOGI(TAG, " - Device: %s", AT_UART_DEVICE);
|
||
LISA_LOGI(TAG, " - Buffer: %zu bytes (dynamic)", at_uart_ctx.rx_buffer_capacity);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief Deinitialize AT UART
|
||
*/
|
||
int at_uart_deinit(void)
|
||
{
|
||
if (!at_uart_ctx.initialized) {
|
||
LISA_LOGW(TAG, "AT UART not initialized");
|
||
return -1;
|
||
}
|
||
|
||
LISA_LOGI(TAG, "Deinitializing AT UART...");
|
||
|
||
/* Delete processing task */
|
||
if (at_uart_ctx.process_task) {
|
||
vTaskDelete(at_uart_ctx.process_task);
|
||
at_uart_ctx.process_task = NULL;
|
||
}
|
||
|
||
/* Disable reception */
|
||
lisa_uart_rx_disable(at_uart_ctx.uart_dev);
|
||
|
||
/* Delete synchronization objects */
|
||
if (at_uart_ctx.tx_mutex) vSemaphoreDelete(at_uart_ctx.tx_mutex);
|
||
if (at_uart_ctx.cmd_mutex) vSemaphoreDelete(at_uart_ctx.cmd_mutex);
|
||
if (at_uart_ctx.buffer_mutex) vSemaphoreDelete(at_uart_ctx.buffer_mutex);
|
||
if (at_uart_ctx.event_group) vEventGroupDelete(at_uart_ctx.event_group);
|
||
|
||
/* Free URC callback list */
|
||
at_urc_callback_node_t *node = at_uart_ctx.urc_callbacks_head;
|
||
while (node) {
|
||
at_urc_callback_node_t *next = node->next;
|
||
free(node);
|
||
node = next;
|
||
}
|
||
|
||
/* Free dynamic buffer */
|
||
if (at_uart_ctx.rx_buffer) {
|
||
free(at_uart_ctx.rx_buffer);
|
||
at_uart_ctx.rx_buffer = NULL;
|
||
}
|
||
|
||
at_uart_ctx.initialized = 0;
|
||
|
||
LISA_LOGI(TAG, "AT UART deinitialized");
|
||
|
||
return 0;
|
||
}
|