345 lines
11 KiB
C
345 lines
11 KiB
C
|
/*****************************************************************************
|
|||
|
* @copyright 2024-2024, . POWER SUPPLY CO., LTD.
|
|||
|
* @file drv_uart.c
|
|||
|
* @brief 串口底层驱动程序
|
|||
|
* @author Gary
|
|||
|
* @date 2024/09/02
|
|||
|
* @remark 初修订
|
|||
|
*****************************************************************************/
|
|||
|
#define _GNU_SOURCE
|
|||
|
#include <stdio.h>
|
|||
|
#include <fcntl.h>
|
|||
|
#include <termios.h>
|
|||
|
#include <string.h>
|
|||
|
#include <errno.h>
|
|||
|
#include <sys/time.h>
|
|||
|
#include "kit_log.h"
|
|||
|
#include "drv_uart.h"
|
|||
|
|
|||
|
/*****************************************************************************
|
|||
|
* @brief 打开串口驱动
|
|||
|
* @param[in] p_uart: 串口驱动结构体指针
|
|||
|
* @return 0-成功 1-失败
|
|||
|
*****************************************************************************/
|
|||
|
int32_t drvUartOpen(uart_lib_t* p_uart)
|
|||
|
{
|
|||
|
// 定义串口时序控制
|
|||
|
struct termios options;
|
|||
|
p_uart->fd = open((const char *)p_uart->address,O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);
|
|||
|
if (p_uart->fd == -1) {
|
|||
|
//p_uart->lastError = SERIAL_PORT_ERROR_OPEN_FAILED;
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
// 获取当前串口配置
|
|||
|
if (tcgetattr(p_uart->fd, &options) != 0)
|
|||
|
{
|
|||
|
KITLOG(LOG_DRIVER_EN, ERROR_EN, "tcgetattr failed");
|
|||
|
close(p_uart->fd);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
// 设置等待时间和最小接受字符
|
|||
|
options.c_cc[VTIME] = p_uart->timeout / 100; // 设置等待时间(单位:十分之一秒)
|
|||
|
options.c_cc[VMIN] = 1; // 最小接受字符
|
|||
|
//options.c_cflag |= (CREAD | CLOCAL);
|
|||
|
options.c_cflag &= ~CRTSCTS; // 不进行流控制
|
|||
|
|
|||
|
//options.c_cflag &= CRTSCTS; // 进行流控制
|
|||
|
|
|||
|
// 设置波特率
|
|||
|
switch (p_uart->baud)
|
|||
|
{
|
|||
|
case kUart_Baud_115200:
|
|||
|
cfsetospeed(&options, B115200);
|
|||
|
cfsetispeed(&options, B115200);
|
|||
|
break;
|
|||
|
case kUart_Baud_57600:
|
|||
|
cfsetospeed(&options, B57600);
|
|||
|
cfsetispeed(&options, B57600);
|
|||
|
break;
|
|||
|
case kUart_Baud_38400:
|
|||
|
cfsetospeed(&options, B38400);
|
|||
|
cfsetispeed(&options, B38400);
|
|||
|
break;
|
|||
|
case kUart_Baud_19200:
|
|||
|
cfsetospeed(&options, B19200);
|
|||
|
cfsetispeed(&options, B19200);
|
|||
|
break;
|
|||
|
case kUart_Baud_9600:
|
|||
|
cfsetospeed(&options, B9600);
|
|||
|
cfsetispeed(&options, B9600);
|
|||
|
break;
|
|||
|
case kUart_Baud_4800:
|
|||
|
cfsetospeed(&options, B4800);
|
|||
|
cfsetispeed(&options, B4800);
|
|||
|
break;
|
|||
|
case kUart_Baud_2400:
|
|||
|
cfsetospeed(&options, B2400);
|
|||
|
cfsetispeed(&options, B2400);
|
|||
|
break;
|
|||
|
case kUart_Baud_1800:
|
|||
|
cfsetospeed(&options, B1800);
|
|||
|
cfsetispeed(&options, B1800);
|
|||
|
break;
|
|||
|
case kUart_Baud_1200:
|
|||
|
cfsetospeed(&options, B1200);
|
|||
|
cfsetispeed(&options, B1200);
|
|||
|
break;
|
|||
|
case kUart_Baud_600:
|
|||
|
cfsetospeed(&options, B600);
|
|||
|
cfsetispeed(&options, B600);
|
|||
|
break;
|
|||
|
case kUart_Baud_300:
|
|||
|
cfsetospeed(&options, B300);
|
|||
|
cfsetispeed(&options, B300);
|
|||
|
break;
|
|||
|
case kUart_Baud_200:
|
|||
|
cfsetospeed(&options, B200);
|
|||
|
cfsetispeed(&options, B200);
|
|||
|
break;
|
|||
|
case kUart_Baud_150:
|
|||
|
cfsetospeed(&options, B150);
|
|||
|
cfsetispeed(&options, B150);
|
|||
|
break;
|
|||
|
default:
|
|||
|
//sprintf(log, "未知波特率:%d", p_uart->baud);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
options.c_cflag |= (CREAD | CLOCAL);
|
|||
|
/* Set data bits (5/6/7/8) */
|
|||
|
options.c_cflag &= ~CSIZE;
|
|||
|
switch (p_uart->data)
|
|||
|
{
|
|||
|
case kUart_Data_5:
|
|||
|
options.c_cflag |= CS5;
|
|||
|
break;
|
|||
|
case kUart_Data_6:
|
|||
|
options.c_cflag |= CS6;
|
|||
|
break;
|
|||
|
case kUart_Data_7:
|
|||
|
options.c_cflag |= CS7;
|
|||
|
break;
|
|||
|
case kUart_Data_8:
|
|||
|
options.c_cflag |= CS8;
|
|||
|
break;
|
|||
|
default:
|
|||
|
//sprintf(log, "未知数据位:%d", p_uart->data);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 设置停止位
|
|||
|
switch (p_uart->stop)
|
|||
|
{
|
|||
|
case kUart_Stop_1:
|
|||
|
options.c_cflag &= ~CSTOPB; // 一位停止位
|
|||
|
break;
|
|||
|
case kUart_Stop_2:
|
|||
|
options.c_cflag |= CSTOPB; // 两位停止位
|
|||
|
break;
|
|||
|
default:
|
|||
|
//sprintf(log, "未知停止位:%d", p_uart->stop);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
// 禁用输出处理
|
|||
|
options.c_oflag &= ~OPOST;
|
|||
|
// 禁用规范模式
|
|||
|
options.c_lflag &= ~(ICANON | ECHO | ECHONL | ISIG | ECHOE);
|
|||
|
// 启用接收器并忽略调制解调器状态行
|
|||
|
options.c_cflag |= CREAD | CLOCAL;
|
|||
|
// 禁用换行符转换
|
|||
|
options.c_oflag &= ~ONLCR;
|
|||
|
//禁用软件流特殊控制
|
|||
|
options.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);
|
|||
|
// 禁用特殊输入处理
|
|||
|
options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
|
|||
|
|
|||
|
// 清空串口的输入输出缓冲区
|
|||
|
tcflush(p_uart->fd, TCIOFLUSH);
|
|||
|
|
|||
|
// 激活配置,将配置的参数应用到串口
|
|||
|
if (tcsetattr(p_uart->fd, TCSANOW, &options) < 0)
|
|||
|
{
|
|||
|
KITLOG(LOG_DRIVER_EN, ERROR_EN, "tcsetattr failed");
|
|||
|
close(p_uart->fd);
|
|||
|
p_uart->fd = -1;
|
|||
|
//p_uart->lastError = SERIAL_PORT_ERROR_INVALID_ARGUMENT;
|
|||
|
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*****************************************************************************
|
|||
|
* @brief 关闭串口驱动
|
|||
|
* @param[in] p_uart: 串口驱动结构体指针
|
|||
|
* @return 0-成功 1-失败
|
|||
|
*****************************************************************************/
|
|||
|
int32_t drvUartClose(uart_lib_t* p_uart)
|
|||
|
{
|
|||
|
if (p_uart->fd > 0)
|
|||
|
{
|
|||
|
close(p_uart->fd);
|
|||
|
p_uart->fd = 0;
|
|||
|
p_uart->status = CONN_OFFLINE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
KITLOG(LOG_DRIVER_EN, ERROR_EN, "关闭失败!");
|
|||
|
return 1;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
void uartPrintLog(const char *str, uint8_t *data, uint16_t len, uint8_t order)
|
|||
|
{
|
|||
|
uint8_t sdata[256 * 2 * 2] = {0};
|
|||
|
int pos = 0;
|
|||
|
if ((uint8_t)getRtdbPointValue(rtdbType, kDev_Type_EMS, 0, kEms_UartLog_Enable) == 1)
|
|||
|
{
|
|||
|
if (len > 0)
|
|||
|
{
|
|||
|
for (int i = 0; i < len; i++)
|
|||
|
{
|
|||
|
pos += sprintf((char*)(sdata + pos), "%02x ", data[i]);
|
|||
|
}
|
|||
|
sprintf((char*)(sdata + pos), "\n");
|
|||
|
}
|
|||
|
KITPTF(LOG_DRIVER_EN, INFO_EN, "串口:%d,%s:%s", order, str, sdata);
|
|||
|
KITLOG(LOG_DRIVER_EN, INFO_EN, "串口:%d,%s:%s", order, str, sdata);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*****************************************************************************
|
|||
|
* @brief 串口驱动发送
|
|||
|
* @param[in] p_uart: 串口驱动结构体指针
|
|||
|
* @return 0-成功 1-失败
|
|||
|
*****************************************************************************/
|
|||
|
int32_t drvUartSend(uart_lib_t* p_uart)
|
|||
|
{
|
|||
|
// 连接状态
|
|||
|
int ret = 0;
|
|||
|
#if 0
|
|||
|
if (p_uart->status == CONN_OFFLINE)
|
|||
|
{
|
|||
|
KITLOG(LOG_DRIVER_EN, ERROR_EN, "串口(%s) 离线\n", p_uart->address);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
fd_set set;
|
|||
|
FD_ZERO(&set);
|
|||
|
FD_SET(p_uart->fd, &set);
|
|||
|
|
|||
|
struct timeval time_out = {1, 0};
|
|||
|
//int ret = select(p_uart->fd + 1, NULL,&set, NULL, &time_out);
|
|||
|
|
|||
|
if (0 == ret)
|
|||
|
{
|
|||
|
printf("串口select 超时 !!! fd:%d\r\n",p_uart->fd);
|
|||
|
//return -128;
|
|||
|
}
|
|||
|
else if (ret < 0)
|
|||
|
{
|
|||
|
printf("串口select 失败 为负值 !!! fd:%d\r\n",p_uart->fd);
|
|||
|
//return -1;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
ssize_t rett = 0;
|
|||
|
///tcflush(p_uart->fd, TCIOFLUSH);
|
|||
|
while (rett < p_uart->send_len)
|
|||
|
{
|
|||
|
ssize_t rc = write(p_uart->fd, p_uart->send_buf + rett, p_uart->send_len - rett);
|
|||
|
|
|||
|
if (rc > 0)
|
|||
|
{
|
|||
|
rett += rc;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// 发生错误
|
|||
|
KITLOG(LOG_DRIVER_EN, ERROR_EN, "串口(%s) 发送错误! ret: %ld, errno: %s(%d)\n",
|
|||
|
p_uart->address, ret, strerror(errno), errno);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
}
|
|||
|
uartPrintLog("串口发送:", p_uart->send_buf, p_uart->send_len, p_uart->uartId);
|
|||
|
//tcdrain(p_uart->fd); // 确保数据实际发送出去
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*****************************************************************************
|
|||
|
* @brief 串口驱动接收
|
|||
|
* @param[in] p_uart: 串口驱动结构体指针
|
|||
|
* @return 0-成功 1-失败
|
|||
|
*****************************************************************************/
|
|||
|
int32_t drvUartRecv(uart_lib_t* p_uart)
|
|||
|
{
|
|||
|
fd_set set;
|
|||
|
FD_ZERO(&set);
|
|||
|
FD_SET(p_uart->fd, &set);
|
|||
|
struct timeval time_out = {1, 0};
|
|||
|
|
|||
|
static uint8_t retries = 0;
|
|||
|
uint8_t expectedLength = p_uart->recv_len;
|
|||
|
//expectedLength = 67;
|
|||
|
// expectedLength = 65;
|
|||
|
p_uart->recv_len = 0;
|
|||
|
//同一帧最多接收p_uart->error_cnt,收不到全量报文,强行退出
|
|||
|
while (p_uart->recv_len < expectedLength)
|
|||
|
{
|
|||
|
int ret = select(p_uart->fd + 1, &set, NULL, NULL, &time_out); // 改为读集合
|
|||
|
if (0 == ret)
|
|||
|
{
|
|||
|
// tcflush(p_uart->fd, TCIOFLUSH);
|
|||
|
uartPrintLog("-128串口接收:", p_uart->recv_buf, p_uart->recv_len, p_uart->uartId);
|
|||
|
return -128;
|
|||
|
}
|
|||
|
else if (ret < 0)
|
|||
|
{
|
|||
|
// tcflush(p_uart->fd, TCIOFLUSH);
|
|||
|
uartPrintLog("-1串口接收:", p_uart->recv_buf, p_uart->recv_len, p_uart->uartId);
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
ssize_t len = read(p_uart->fd, p_uart->recv_buf + p_uart->recv_len, expectedLength - p_uart->recv_len);
|
|||
|
if (len > 0)
|
|||
|
{
|
|||
|
p_uart->recv_len += len;
|
|||
|
// 检查是否接收到完整的帧
|
|||
|
if (p_uart->recv_len >= expectedLength)
|
|||
|
{
|
|||
|
retries = 0;
|
|||
|
tcflush(p_uart->fd, TCIOFLUSH);
|
|||
|
uartPrintLog("串口接收:", p_uart->recv_buf, p_uart->recv_len, p_uart->uartId);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (len < 0)
|
|||
|
{
|
|||
|
KITLOG(LOG_DRIVER_EN, ERROR_EN, "串口(%d) 接收数据失败! ret: %zd, errno: %s(%d)\n",
|
|||
|
p_uart->uartId, len, strerror(errno), errno);
|
|||
|
return 1; // 接收失败
|
|||
|
}
|
|||
|
// 如果未达到期望长度,可以考虑重试接收
|
|||
|
if (p_uart->recv_len < expectedLength)
|
|||
|
{
|
|||
|
retries++;
|
|||
|
if(retries >= 3)
|
|||
|
{
|
|||
|
KITLOG(LOG_DRIVER_EN, ERROR_EN, "串口(%d) 全量数据接收失败! 期望长度: %d, 实际接收长度:%d",p_uart->uartId, expectedLength, p_uart->recv_len);
|
|||
|
// 清空串口接收缓冲区
|
|||
|
tcflush(p_uart->fd, TCIFLUSH);
|
|||
|
return -1;
|
|||
|
}
|
|||
|
usleep(100000); // 等待一段时间后重试
|
|||
|
}
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|