Files
ESPC3-wireless/app/drivers/data_port/uart/uart_port.c
2025-04-10 10:57:28 +08:00

497 lines
14 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "os/os.h"
#include "uart_port.h"
#include "drivers/chip/gpio.h"
#include "driver/uart.h"
#include "hal/uart_ll.h"
#include "drivers/data_port/sb_data_port.h"
#define CONFIG_SYS_LOG_DUMP_ON 0
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN
#define SYS_LOG_DOMAIN "UART"
#include "sys_log.h"
#define _MAX_UART_NUM 3
#define UART_IN_TAST_STK_SIZE 2304
typedef struct
{
uint16_t size;
uint8_t data[0];
} __fifo_data_t;
typedef struct
{
int uart_num;
int baudrate;
int tx_pin;
int rx_pin;
int rx_task_priority;
uint16_t buffer_size;
uint8_t frame_ms;
volatile uint16_t buffer_used;
os_work_t *rx_resume_work;
QueueHandle_t event_queue;
os_thread_t rx_thread_hdl;
union
{
os_pipe_t rx_pipe;
os_fifo_t rx_fifo;
};
} __uart_data_t;
static void _uart_rx_thread(void *parmas)
{
uart_event_t event;
int ret = 0;
int len = 0;
int rx_len = 0;
__uart_data_t *uart_data = ((sb_data_port_t *)parmas)->data;
const uint32_t TMP_BUF_LEN = 1024;
uint8_t tmpbuffer[TMP_BUF_LEN];
TickType_t tick = portMAX_DELAY;
uint16_t recv_total = 0;
os_time_t rx_time = 0;
for (;;)
{
if (xQueueReceive(uart_data->event_queue, (void *)&event, tick))
{
switch (event.type)
{
case UART_DATA:
{
rx_len = event.size;
while (rx_len > 0)
{
if (uart_data->frame_ms == 0)
{
len = (rx_len > TMP_BUF_LEN) ? TMP_BUF_LEN : rx_len;
ret = uart_read_bytes(uart_data->uart_num, tmpbuffer, len, portMAX_DELAY);
if (ret <= 0)
{
break;
}
rx_len -= ret;
// 将数据写入ringbuffer由外部轮询ringbuffer
uint8_t *p = tmpbuffer;
for (int i = 0; i < 100 && ret != 0; i++)
{
int write_result = os_pipe_fifo_fill(&uart_data->rx_pipe, p, ret);
p = &((uint8_t *)p)[write_result];
ret -= write_result;
if (ret != 0)
{
os_thread_sleep(10);
}
}
if (ret != 0)
{
SYS_LOG_ERR("write data to rx_pipe_obj failed, remain: %d bytes", ret);
}
}
else
{
len = sizeof(tmpbuffer) - recv_total;
if (len > rx_len)
{
len = rx_len;
}
ret = uart_read_bytes(uart_data->uart_num, &tmpbuffer[recv_total], len, portMAX_DELAY);
if (ret <= 0)
{
break;
}
rx_len -= ret;
recv_total += ret;
if (recv_total >= sizeof(tmpbuffer))
{
rx_time = os_get_sys_time() - uart_data->frame_ms - 1; // 使退出 switch 后的超时条件必定成立而立即保存数据
break;
}
rx_time = os_get_sys_time();
}
}
}
break;
// Event of HW FIFO overflow detected
case UART_FIFO_OVF:
SYS_LOG_WRN("hw fifo overflow");
// If fifo overflow happened, you should consider adding flow control for your application.
// The ISR has already reset the rx FIFO,
uart_flush_input(uart_data->uart_num);
xQueueReset(uart_data->event_queue);
break;
// Event of UART ring buffer full
case UART_BUFFER_FULL:
SYS_LOG_WRN("ring buffer full");
// If buffer full happened, you should consider encreasing your buffer size
uart_flush_input(uart_data->uart_num);
xQueueReset(uart_data->event_queue);
break;
// Event of UART RX break detected
case UART_BREAK:
SYS_LOG_WRN("uart rx break");
break;
// Event of UART parity check error
case UART_PARITY_ERR:
SYS_LOG_WRN("uart parity error");
break;
// Event of UART frame error
case UART_FRAME_ERR:
SYS_LOG_WRN("uart frame error");
break;
// Others
default:
SYS_LOG_WRN("uart event type: %d", event.type);
break;
}
}
if (uart_data->frame_ms != 0)
{
if (os_get_sys_time() - rx_time > uart_data->frame_ms)
{
if (recv_total > 0)
{
while (uart_data->buffer_used >= uart_data->buffer_size)
{
os_thread_sleep(1);
}
__fifo_data_t *fifo_data = os_fifo_alloc(sizeof(__fifo_data_t) + recv_total);
if (fifo_data)
{
os_scheduler_suspend();
uart_data->buffer_used += recv_total + sizeof(((__fifo_data_t *)0)->size);
os_scheduler_resume();
fifo_data->size = recv_total;
memcpy(fifo_data->data, tmpbuffer, recv_total);
os_fifo_put(&uart_data->rx_fifo, fifo_data);
}
recv_total = 0;
tick = 1;
}
else
{
tick = portMAX_DELAY;
}
}
else
{
tick = 1;
}
}
}
}
static void uart_port_initialize(sb_data_port_t *port)
{
__uart_data_t *uart_data = port->data;
uart_config_t uart_config;
memset(&uart_config, 0, sizeof(uart_config));
uart_config.baud_rate = uart_data->baudrate;
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
esp_err_t ret = uart_set_pin(uart_data->uart_num, uart_data->tx_pin, uart_data->rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
if (ret != ESP_OK)
{
SYS_LOG_ERR("failed to set uart pin:%d, %d", uart_data->uart_num, ret);
return;
}
ret = uart_param_config(uart_data->uart_num, &uart_config);
if (ret != ESP_OK)
{
SYS_LOG_ERR("failed to set uart param:%d, %d", uart_data->uart_num, ret);
return;
}
ret = uart_driver_install(uart_data->uart_num, 0x400, 0x400, 3, &(uart_data->event_queue), 0);
if (ret != ESP_OK)
{
SYS_LOG_ERR("failed to uart_driver_install:%d, %d", uart_data->uart_num, ret);
return;
}
uart_disable_intr_mask(uart_data->uart_num, UART_INTR_BRK_DET); // 关闭检测UART_BREAK中断防止过多的UART_BREAK导致的程序异常
SYS_LOG_INF("uart_driver_install ok. (%d, %d, %d, %d, %d, %d)", uart_data->uart_num, 0x400, 0x400, 3, (uint32_t)&uart_data->event_queue, 0);
}
static int _uart_port_start(sb_data_port_t *port)
{
__uart_data_t *uart_data = port->data;
SYS_LOG_INF("uart start, uartnum:%d", uart_data->uart_num);
if (uart_data->frame_ms == 0)
{
if (!os_pipe_is_valid(&uart_data->rx_pipe))
{
if (os_pipe_create(&uart_data->rx_pipe, uart_data->buffer_size) != OS_OK)
{
port->vtable->stop(port);
return -1;
}
os_pipe_regist(&uart_data->rx_pipe, uart_data->rx_resume_work, 0);
}
}
else
{
if (!os_fifo_q_is_valid(&uart_data->rx_fifo))
{
if (os_fifo_q_create(&uart_data->rx_fifo) != OS_OK)
{
port->vtable->stop(port);
return -1;
}
os_fifo_q_regist(&uart_data->rx_fifo, uart_data->rx_resume_work, 0);
}
uart_data->buffer_used = 0;
}
if (!uart_is_driver_installed(uart_data->uart_num))
{
uart_port_initialize(port);
}
if (!os_thread_is_valid(&uart_data->rx_thread_hdl))
{
if (os_thread_create(&uart_data->rx_thread_hdl,
"uart_rx",
_uart_rx_thread,
port,
UART_IN_TAST_STK_SIZE,
uart_data->rx_task_priority) != OS_OK)
{
SYS_LOG_WRN("task create fail");
port->vtable->stop(port);
return -1;
}
}
return 0;
}
static int _uart_port_stop(sb_data_port_t *port)
{
__uart_data_t *uart_data = port->data;
if (os_thread_is_valid(&uart_data->rx_thread_hdl))
{
os_thread_delete(&uart_data->rx_thread_hdl);
}
if (uart_is_driver_installed(uart_data->uart_num))
{
uart_driver_delete(uart_data->uart_num);
}
if (uart_data->frame_ms == 0)
{
if (os_pipe_is_valid(&uart_data->rx_pipe))
{
os_pipe_delete(&uart_data->rx_pipe);
}
}
else
{
if (os_fifo_q_is_valid(&uart_data->rx_fifo))
{
os_fifo_q_delete(&uart_data->rx_fifo);
}
}
return 0;
}
static int _uart_port_write(sb_data_port_t *port, const void *data, uint32_t size)
{
__uart_data_t *uart_data = port->data;
int ret = uart_write_bytes(uart_data->uart_num, data, size);
if (ret > 0)
{
SYS_LOG_DUMP(data, ret, 0, 0);
}
return ret;
}
static int _uart_port_read(sb_data_port_t *port, void *data, uint32_t size)
{
__uart_data_t *uart_data = port->data;
int ret;
if (uart_data->frame_ms == 0)
{
ret = os_pipe_fifo_read(&uart_data->rx_pipe, data, size);
}
else
{
__fifo_data_t *fifo_data = os_fifo_take(&uart_data->rx_fifo, 0);
if (fifo_data)
{
ret = size < fifo_data->size ? size : fifo_data->size;
if (data)
{
memcpy(data, fifo_data->data, ret);
}
os_fifo_free(fifo_data);
os_scheduler_suspend();
uart_data->buffer_used -= ret + sizeof(((__fifo_data_t *)0)->size);
os_scheduler_resume();
}
else
{
ret = 0;
}
}
if (ret > 0)
{
SYS_LOG_DUMP(data, ret, 0, 0);
}
return ret;
}
static bool _uart_port_is_start(sb_data_port_t *port)
{
if (port == NULL)
{
return false;
}
__uart_data_t *uart_data = port->data;
if (uart_data == NULL)
{
return false;
}
if (uart_data->frame_ms == 0)
{
if (!os_pipe_is_valid(&uart_data->rx_pipe))
{
return false;
}
}
else
{
if (!os_fifo_q_is_valid(&uart_data->rx_fifo))
{
return false;
}
}
return true;
}
static uint32_t _uart_port_get_rx_length(sb_data_port_t *port)
{
__uart_data_t *uart_data = port->data;
if (uart_data->frame_ms == 0)
{
return os_pipe_get_valid_size(&uart_data->rx_pipe);
}
else
{
__fifo_data_t *fifo_data = os_fifo_peek_head(&uart_data->rx_fifo);
if (fifo_data)
{
return fifo_data->size;
}
else
{
return 0;
}
}
}
static sb_data_port_vtable_t const s_uart_vtable = {
.start = _uart_port_start,
.stop = _uart_port_stop,
.write = _uart_port_write,
.read = _uart_port_read,
.is_started = _uart_port_is_start,
.get_rx_length = _uart_port_get_rx_length,
};
int sb_uart_port_init(void)
{
return 0;
}
/**
* @brief
*
* @param uart_num 串口号 0..n
* @param baudrate 波特率
* @param tx_pin TXD 引脚
* @param rx_pin RXD 引脚
* @param rx_task_priority 内部接收任务的优先级
* @param buffer_size 接收缓存大小
* @param frame_ms 0: 数据流, > 0: 最少连续有 n 毫秒没有收到数据时成为一帧
* @param rx_resume_work 当收到新数据时唤醒的工作项,可设置为 NULL
* @return sb_data_port_t*
*/
sb_data_port_t *sb_uart_port_bind(int uart_num,
int baudrate,
int tx_pin,
int rx_pin,
int rx_task_priority,
uint16_t buffer_size,
uint8_t frame_ms,
os_work_t *rx_resume_work)
{
static sb_data_port_t s_uart_port[_MAX_UART_NUM];
static __uart_data_t s_uart_data[_MAX_UART_NUM];
sb_data_port_t *port = &s_uart_port[uart_num];
__uart_data_t *uart_data = &s_uart_data[uart_num];
SYS_ASSERT(uart_num < _MAX_UART_NUM, "");
SYS_ASSERT(port->data == NULL, "The interface has already been bound");
// SYS_ASSERT(baudrate > 0, "");
uart_data->uart_num = uart_num;
uart_data->baudrate = baudrate;
uart_data->tx_pin = tx_pin;
uart_data->rx_pin = rx_pin;
uart_data->rx_task_priority = rx_task_priority;
uart_data->buffer_size = buffer_size;
uart_data->frame_ms = frame_ms;
uart_data->rx_resume_work = rx_resume_work;
port->vtable = &s_uart_vtable;
port->data = uart_data;
return port;
}
void sb_uart_port_unbind(sb_data_port_t *port)
{
if (_uart_port_is_start(port))
{
_uart_port_stop(port);
__uart_data_t *uart_data = port->data;
drv_gpio_pin_configure(uart_data->tx_pin, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP);
drv_gpio_pin_configure(uart_data->rx_pin, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP);
port->data = NULL;
}
}