Files
chrg/apps/chrg/applications/bsp/chrg_tty.c
T
2025-09-27 21:51:45 +08:00

559 lines
13 KiB
C

#include <rtthread.h>
#include <rtdevice.h>
#include <rthw.h>
#include <chrg_tty.h>
#define DBG_TAG "chrg.tty"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#ifdef TTY_USING_DMA_TX
static rt_err_t chrg_tty_send_comp_hook (rt_device_t dev, void *buffer)
{
struct chrg_tty_t *pTTY = (struct chrg_tty_t *)(dev->user_data);
rt_completion_done(&pTTY->tx_comp);
return (RT_EOK);
}
#endif
static rt_err_t chrg_tty_recv_ind_hook (rt_device_t dev, rt_size_t size)
{
struct chrg_tty_t *pTTY = (struct chrg_tty_t *)(dev->user_data);
if (pTTY->evt) {
rt_event_send(pTTY->evt, TTY_EVT_RX_IND);
}
return (RT_EOK);
}
static int chrg_tty_cal_byte_tmo (int baudrate)
{
int tmo = (40 * 1000) / baudrate;
if (tmo < TTY_BYTE_TMO_MIN) {
tmo = TTY_BYTE_TMO_MIN;
} else if (tmo > TTY_BYTE_TMO_MAX) {
tmo = TTY_BYTE_TMO_MAX;
}
return (tmo);
}
// mode : 0--receive mode, 1--send mode
void chrg_tty_mode_set (struct chrg_tty_t *pTTY, int mode)
{
if (pTTY->pin < 0) {
return;
}
if (mode) {
rt_pin_write(pTTY->pin, pTTY->level);
#if (TTY_SW_DLY_US > 0)
rt_hw_us_delay(TTY_SW_DLY_US);
#endif
} else {
#ifdef TTY_USING_DMA_TX
rt_completion_wait(&pTTY->tx_comp, TTY_TX_COMP_TMO_MAX);
rt_thread_mdelay(pTTY->tx_dly_ms); // 等待末尾数据传输完成
#elif (TTY_SW_DLY_US > 0)
rt_hw_us_delay(TTY_SW_DLY_US);
#endif
rt_pin_write(pTTY->pin, !pTTY->level);
}
}
static int chrg_tty_dev_open (struct chrg_tty_t *pTTY)
{
rt_err_t ret = RT_EOK;
#ifdef TTY_USING_DMA_RX
#ifdef TTY_USING_DMA_TX
ret = rt_device_open(pTTY->dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX);
if (RT_EOK == ret) {
return (RT_EOK);
}
#endif
#ifdef TTY_USING_INT_TX
ret = rt_device_open(pTTY->dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_INT_TX);
if (RT_EOK == ret) {
return (RT_EOK);
}
#endif
ret = rt_device_open(pTTY->dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);
if (RT_EOK == ret) {
return (RT_EOK);
}
#endif
#ifdef TTY_USING_DMA_TX
ret = rt_device_open(pTTY->dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_DMA_TX);
if (RT_EOK == ret) {
return (RT_EOK);
}
#endif
#ifdef TTY_USING_INT_TX
ret = rt_device_open(pTTY->dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX);
if (RT_EOK == ret) {
return (RT_EOK);
}
#endif
ret = rt_device_open(pTTY->dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
if (RT_EOK == ret) {
return (RT_EOK);
}
return (-RT_ERROR);
}
/*
* @brief create tty instance dynamically
* @param name - serial device name
* @param baudrate - serial baud rate
* @param parity - serial parity mode
* @param pin - mode contrle pin
* @param level - send mode level
* @retval instance handle
*/
struct chrg_tty_t *chrg_tty_create (const char *devname, int baudrate, int parity, int pin, int level)
{
struct chrg_tty_t *pTTY = RT_NULL;
rt_device_t dev;
dev = rt_device_find(devname);
if (RT_NULL == dev) {
LOG_E("tty device '%s' not found.", devname);
return (RT_NULL);
}
if (RT_Device_Class_Char != dev->type) {
LOG_E("tty device '%s' type is not char.", devname);
return (RT_NULL);
}
pTTY = (struct chrg_tty_t *)rt_malloc(sizeof(struct chrg_tty_t));
if (RT_NULL == pTTY) {
LOG_E("no memory for tty create instance.");
return (RT_NULL);
}
pTTY->devname = devname;
pTTY->lock = rt_mutex_create(devname, RT_IPC_FLAG_FIFO);
if (RT_NULL == pTTY->lock) {
rt_free(pTTY);
LOG_E("create mutex failed.");
return (RT_NULL);
}
pTTY->evt = rt_event_create(devname, RT_IPC_FLAG_FIFO);
if (RT_NULL == pTTY->evt) {
rt_mutex_delete(pTTY->lock);
rt_free(pTTY);
LOG_E("create event failed.");
return (RT_NULL);
}
#ifdef TTY_USING_DMA_TX
pTTY->tx_dly_ms = ((2 * 11 *1000) / baudrate) + 1;
rt_completion_init(&pTTY->tx_comp);
#endif
pTTY->dev = dev;
pTTY->status = 0;
pTTY->pin = pin;
pTTY->level = (level != 0);
pTTY->timeout = 0;
pTTY->byte_tmo = chrg_tty_cal_byte_tmo(baudrate);
chrg_tty_config(pTTY, baudrate, 8, parity, 0);
return pTTY;
}
/*
* @brief destory tty instance created dynamically
* @param pTTY - instance handle
* @retval 0 - success, other - error
*/
int chrg_tty_destory (struct chrg_tty_t *pTTY)
{
if (RT_NULL == pTTY) {
LOG_E("param null.");
return (-RT_ERROR);
}
chrg_tty_disconn(pTTY);
if (pTTY->lock) {
rt_mutex_delete(pTTY->lock);
pTTY->lock = RT_NULL;
}
if (pTTY->evt) {
rt_event_delete(pTTY->evt);
pTTY->evt = RT_NULL;
}
rt_free(pTTY);
return (RT_EOK);
}
/*
* @brief config tty params
* @param pTTY - instance handle
* @param baudrate - baudrate of communication
* @param databits - data bits, 5~8
* @param parity - parity bit, 0~2, 0 - none, 1 - odd, 2 - even
* @param stopbits - stop bits, 0~1, 0 - 1 stop bit, 1 - 2 stop bits
* @retval 0 - success, other - error
*/
int chrg_tty_config (struct chrg_tty_t *pTTY,
int baudrate, int databits, int parity, int stopbits)
{
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
if (RT_NULL == pTTY) {
LOG_E("param null.");
return (-RT_ERROR);
}
#ifdef TTY_USING_DMA_TX
pTTY->tx_dly_ms = ((2 * 11 *1000) / baudrate) + 1;
#endif
pTTY->byte_tmo = chrg_tty_cal_byte_tmo(baudrate);
config.baud_rate = baudrate;
config.data_bits = databits;
config.parity = parity;
config.stop_bits = stopbits;
rt_device_control(pTTY->dev, RT_DEVICE_CTRL_CONFIG, &config);
return (RT_EOK);
}
/*
* @brief set wait datas timeout for receiving
* @param pTTY - instance handle
* @param tmo_ms - receive wait timeout, 0--no wait, <0--wait forever, >0--wait timeout, default = 0
* @retval 0 - success, other - error
*/
int chrg_tty_set_recv_tmo (struct chrg_tty_t *pTTY, int tmo_ms)
{
if (RT_NULL == pTTY) {
LOG_E("param null.");
return (-RT_ERROR);
}
pTTY->timeout = tmo_ms;
return (RT_EOK);
}
/*
* @brief set byte interval timeout for receiving
* @param pTTY - instance handle
* @param tmo_ms - byte interval timeout, default is calculated from baudrate
* @retval 0 - success, other - error
*/
int chrg_tty_set_byte_tmo (struct chrg_tty_t *pTTY, int tmo_ms)
{
if (RT_NULL == pTTY) {
LOG_E("param null.");
return (-RT_ERROR);
}
if (tmo_ms < TTY_BYTE_TMO_MIN) {
tmo_ms = TTY_BYTE_TMO_MIN;
} else if (tmo_ms > TTY_BYTE_TMO_MAX) {
tmo_ms = TTY_BYTE_TMO_MAX;
}
pTTY->byte_tmo = tmo_ms;
return (RT_EOK);
}
/*
* @brief open tty connect
* @param pTTY - instance handle
* @retval 0 - success, other - error
*/
int chrg_tty_connect (struct chrg_tty_t * pTTY)
{
if (RT_NULL == pTTY) {
LOG_E("param null.");
return -RT_ERROR;
}
if (pTTY->status == 1) {
return RT_EOK;
}
if (chrg_tty_dev_open(pTTY) != RT_EOK) {
LOG_E("tty open failed.");
return -RT_ERROR;
}
if (pTTY->pin >= 0) {
rt_pin_mode(pTTY->pin, PIN_MODE_OUTPUT);
rt_pin_write(pTTY->pin, !pTTY->level);
}
pTTY->dev->user_data = pTTY;
pTTY->dev->rx_indicate = chrg_tty_recv_ind_hook;
#ifdef TTY_USING_DMA_TX
pTTY->dev->tx_complete = chrg_tty_send_comp_hook;
#endif
pTTY->status = 1;
return (RT_EOK);
}
/*
* @brief close tty connect
* @param pTTY - instance handle
* @retval 0 - success, other - error
*/
int chrg_tty_disconn (struct chrg_tty_t *pTTY)
{
if (RT_NULL == pTTY) {
LOG_E("param null.");
return (-RT_ERROR);
}
if (pTTY->status == 0) {
return(RT_EOK);
}
rt_mutex_take(pTTY->lock, RT_WAITING_FOREVER);
if (pTTY->dev) {
pTTY->dev->rx_indicate = RT_NULL;
#ifdef TTY_USING_DMA_TX
pTTY->dev->tx_complete = RT_NULL;
#endif
rt_device_close(pTTY->dev);
}
if (pTTY->pin >= 0) {
rt_pin_mode(pTTY->pin, PIN_MODE_INPUT);
}
pTTY->status = 0;
rt_mutex_release(pTTY->lock);
return (RT_EOK);
}
/*
* @brief receive datas from tty
* @param pTTY - instance handle
* @param buf - buffer addr
* @param size - maximum length of received datas
* @retval >=0 - length of received datas, <0 - error
*/
int chrg_tty_recv (struct chrg_tty_t *pTTY, void *buf, int size)
{
rt_err_t ret = RT_EOK;
int recv_len = 0;
rt_uint32_t recved = 0;
if (pTTY == RT_NULL || buf == RT_NULL || size == 0) {
LOG_E("param null.");
return (-RT_ERROR);
}
if (pTTY->status == 0) {
return (-RT_ERROR);
}
ret = rt_mutex_take(pTTY->lock, RT_WAITING_FOREVER);
if (RT_EOK != ret) {
LOG_E("mutex take failed. [%d]", ret);
return(-RT_ERROR);
}
while (size) {
int len = rt_device_read(pTTY->dev, 0, (char *)buf + recv_len, size);
if (len) {
recv_len += len;
size -= len;
continue;
}
rt_event_control(pTTY->evt, RT_IPC_CMD_RESET, RT_NULL);
if (recv_len) {
ret = rt_event_recv(pTTY->evt, TTY_EVT_RX_IND,
(RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR), pTTY->byte_tmo, &recved);
if (RT_EOK != ret) {
break;
}
} else {
ret = rt_event_recv(pTTY->evt, (TTY_EVT_RX_IND | TTY_EVT_RX_BREAK),
(RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR), pTTY->timeout, &recved);
if (RT_EOK != ret) {
break;
}
if ((recved & TTY_EVT_RX_BREAK) != 0) {
rt_mutex_release(pTTY->lock);
rt_thread_delay(2);
return 0;
}
}
}
rt_mutex_release(pTTY->lock);
return recv_len;
}
/*
* @brief send datas to tty
* @param pTTY - instance handle
* @param buf - buffer addr
* @param size - length of send datas
* @retval >=0 - length of sent datas, <0 - error
*/
int chrg_tty_send (struct chrg_tty_t *pTTY, void *buf, int size)
{
rt_err_t ret = RT_EOK;
int send_len = 0;
if (pTTY == RT_NULL || buf == RT_NULL || size == 0) {
LOG_E("param null.");
return (-RT_ERROR);
}
if (pTTY->status == 0) {
LOG_E("tty not connected.");
return (-RT_ERROR);
}
ret = rt_mutex_take(pTTY->lock, RT_WAITING_FOREVER);
if (RT_EOK != ret) {
LOG_E("mutex take failed. [%d]", ret);
return (-RT_ERROR);
}
chrg_tty_mode_set(pTTY, 1);//set to send mode
send_len = rt_device_write(pTTY->dev, 0, buf, size);
chrg_tty_mode_set(pTTY, 0);//set to receive mode
rt_mutex_release(pTTY->lock);
return send_len;
}
/*
* @brief break tty receive wait
* @param pTTY - instance handle
* @retval 0 - success, other - error
*/
int chrg_tty_break_recv (struct chrg_tty_t *pTTY)
{
if ((pTTY == RT_NULL) || (pTTY->evt == RT_NULL)) {
return (-RT_ERROR);
}
rt_event_send(pTTY->evt, TTY_EVT_RX_BREAK);
return (RT_EOK);
}
/*
* @brief send data to tty and then receive response data from tty
* @param pTTY - instance handle
* @param send_buf - send buffer addr
* @param send_len - length of send datas
* @param recv_buf - recv buffer addr
* @param recv_size - maximum length of received datas
* @retval >=0 - length of received datas, <0 - error
*/
int chrg_tty_send_then_recv (struct chrg_tty_t *pTTY,
void *send_buf, int send_len, void *recv_buf, int recv_size)
{
rt_err_t ret = RT_EOK;
int recv_len = 0;
rt_uint32_t recved = 0;
if (pTTY == RT_NULL || send_buf == RT_NULL || send_len == 0 || recv_buf == RT_NULL || recv_size == 0) {
LOG_E("null param");
return(-RT_ERROR);
}
if (pTTY->status == 0) {
LOG_E("tty not connected.");
return(-RT_ERROR);
}
ret = rt_mutex_take(pTTY->lock, RT_WAITING_FOREVER);
if (RT_EOK != ret) {
LOG_E("mutex take failed. [%d]", ret);
return(-RT_ERROR);
}
chrg_tty_mode_set(pTTY, 1);//set to send mode
send_len = rt_device_write(pTTY->dev, 0, send_buf, send_len);
chrg_tty_mode_set(pTTY, 0);//set to receive mode
if (send_len < 0) {
rt_mutex_release(pTTY->lock);
LOG_E("send error.");
return(-RT_ERROR);
}
while (recv_size) {
int len = rt_device_read(pTTY->dev, 0, (char *)recv_buf + recv_len, recv_size);
if (len) {
recv_len += len;
recv_size -= len;
continue;
}
rt_event_control(pTTY->evt, RT_IPC_CMD_RESET, RT_NULL);
if (recv_len) {
ret = rt_event_recv(pTTY->evt, TTY_EVT_RX_IND,
(RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR), pTTY->byte_tmo, &recved);
if (RT_EOK != ret) {
break;
}
} else {
ret = rt_event_recv(pTTY->evt, TTY_EVT_RX_IND,
(RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR), pTTY->timeout, &recved);
if (RT_EOK != ret) {
break;
}
}
}
rt_mutex_release(pTTY->lock);
return recv_len;
}