本文针对Linux系统上如何对各类串口硬件进行出厂测试进行硬件连接和软件使用说明,提供的软件测试工具wchsertest,适用于USB、PCI、PCIe转串口设备等、同样也适用于原生ttyS串口。
在测试前,需要制作单独的硬件治具,按下表连接信号线:
串口针式座子(DB9)硬件连线 |
||||
引脚名 |
引脚号 |
引脚名 |
引脚号 |
|
RTS |
7 |
<----------------------> |
CTS |
8 |
RTS |
7 |
<----------------------> |
RI |
9 |
TXD |
3 |
<----------------------> |
RXD |
2 |
DTR |
4 |
<----------------------> |
DSR |
6 |
DTR |
4 |
<----------------------> |
CD |
1 |
引脚连接示意图:
(1)插入待测试USB/PCI/PCIe转串口设备。
(2)以CH342F(USB转2串口芯片)为例,安装对应VCP厂商驱动程序,进入/dev目录查看出现如下设备节点:
以CH382为例,安装对应VCP厂商驱动程序,进入/dev目录查看出现如下设备节点:
(3)运行软件,输入命令格式:
./[可执行文件] –D [设备节点路径]
实例1(测试CH342的UART0):sudo ./serial_port_test -D /dev/ttyCH343USB0
实例2(测试CH382的UART0):sudo ./serial_port_test -D /dev/ttyWCH0
根据输出的错误码和终端输出信息可判断故障信号线,下表为错误码和说明。
错误码 |
错误码说明 |
0 |
DTR--DSR线错误 |
1 |
DTR--DCD线错误 |
2 |
RTS--CTS线错误 |
3 |
RTS--RI线错误 |
4 |
TXD--RXD线错误 |
(1)测试成功实例
软件分别以2400bps、9600bps、115200bps各测试一次。
(2)测试错误实例
根据输出信息可知,DTR—DSR信号通讯存在错误,错误码:0。
/*
* serial port factory test utility.
*
* Copyright (C) 2023 Nanjing Qinheng Microelectronics Co., Ltd.
* Web: http://wch.cn
* Author: WCH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Cross-compile with cross-gcc -I /path/to/cross-kernel/include
*
* V1.0 - initial version
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define termios asmtermios
#include
#undef termios
#include
#define DTR_ON 1
#define DTR_OFF 0
#define RTS_ON 1
#define RTS_OFF 0
#define BUF_SIZE 64
#define DTR_ON_CMD 0x0
#define DTR_OFF_CMD 0x1
#define RTS_ON_CMD 0x2
#define RTS_OFF_CMD 0x3
extern int ioctl(int d, int request, ...);
static const char *device = "/dev/ttyCH343USB0";
static int hardflow = 0;
static int verbose = 0;
static FILE *fp;
static const struct option lopts[] = {
{ "device", required_argument, 0, 'D' },
{ NULL, 0, 0, 0 },
};
static void print_usage(const char *prog)
{
printf("Usage: %s [-DSvf]\n", prog);
puts(" -D --device tty device to use\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
int c;
while (1) {
c = getopt_long(argc, argv, "D:S:h", lopts, NULL);
if (c == -1) {
break;
}
switch (c) {
case 'D':
if (optarg != NULL)
device = optarg;
break;
case 'h':
default:
print_usage(argv[0]);
break;
}
}
}
/**
* libtty_setcustombaudrate - set baud rate of tty device
* @fd: device handle
* @speed: baud rate to set
*
* The function return 0 if success, or -1 if fail.
*/
static int libtty_setcustombaudrate(int fd, int baudrate)
{
struct termios2 tio;
if (ioctl(fd, TCGETS2, &tio)) {
perror("TCGETS2");
return -1;
}
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= BOTHER;
tio.c_ispeed = baudrate;
tio.c_ospeed = baudrate;
if (ioctl(fd, TCSETS2, &tio)) {
perror("TCSETS2");
return -1;
}
if (ioctl(fd, TCGETS2, &tio)) {
perror("TCGETS2");
return -1;
}
return 0;
}
/**
* libtty_setopt - config tty device
* @fd: device handle
* @speed: baud rate to set
* @databits: data bits to set
* @stopbits: stop bits to set
* @parity: parity to set
* @hardflow: hardflow to set
*
* The function return 0 if success, or -1 if fail.
*/
static int libtty_setopt(int fd, int speed, int databits, int stopbits, char parity, char hardflow)
{
struct termios newtio;
struct termios oldtio;
int i;
bzero(&newtio, sizeof(newtio));
bzero(&oldtio, sizeof(oldtio));
if (tcgetattr(fd, &oldtio) != 0) {
perror("tcgetattr");
return -1;
}
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
/* set data bits */
switch (databits) {
case 5:
newtio.c_cflag |= CS5;
break;
case 6:
newtio.c_cflag |= CS6;
break;
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
default:
fprintf(stderr, "unsupported data size\n");
return -1;
}
/* set parity */
switch (parity) {
case 'n':
case 'N':
newtio.c_cflag &= ~PARENB; /* Clear parity enable */
newtio.c_iflag &= ~INPCK; /* Disable input parity check */
break;
case 'o':
case 'O':
newtio.c_cflag |= (PARODD | PARENB); /* Odd parity instead of even */
newtio.c_iflag |= INPCK; /* Enable input parity check */
break;
case 'e':
case 'E':
newtio.c_cflag |= PARENB; /* Enable parity */
newtio.c_cflag &= ~PARODD; /* Even parity instead of odd */
newtio.c_iflag |= INPCK; /* Enable input parity check */
break;
default:
fprintf(stderr, "unsupported parity\n");
return -1;
}
/* set stop bits */
switch (stopbits) {
case 1:
newtio.c_cflag &= ~CSTOPB;
break;
case 2:
newtio.c_cflag |= CSTOPB;
break;
default:
perror("unsupported stop bits\n");
return -1;
}
if (hardflow)
newtio.c_cflag |= CRTSCTS;
else
newtio.c_cflag &= ~CRTSCTS;
newtio.c_cc[VTIME] = 10; /* Time-out value (tenths of a second) [!ICANON]. */
newtio.c_cc[VMIN] = 64; /* Minimum number of bytes read at once [!ICANON]. */
tcflush(fd, TCIOFLUSH);
if (tcsetattr(fd, TCSANOW, &newtio) != 0) {
perror("tcsetattr");
return -1;
}
/* set tty speed */
if (libtty_setcustombaudrate(fd, speed) != 0) {
perror("setbaudrate");
return -1;
}
return 0;
}
/**
* libtty_open - open tty device
* @devname: the device name to open
*
* In this demo device is opened blocked, you could modify it at will.
*/
static int libtty_open(const char *devname)
{
int fd = open(devname, O_RDWR | O_NOCTTY);
int flags = 0;
if (fd < 0) {
perror("open device failed");
return -1;
}
if (fcntl(fd, F_SETFL, 0) < 0) {
printf("fcntl failed.\n");
return -1;
}
if (isatty(fd) == 0) {
printf("not tty device.\n");
return -1;
}
return fd;
}
/**
* libtty_close - close tty device
* @fd: the device handle
*
* The function return 0 if success, others if fail.
*/
static int libtty_close(int fd)
{
return close(fd);
}
/**
* libtty_tiocmset - modem set
* @fd: file descriptor of tty device
* @bDTR: 0 on inactive, other on DTR active
* @bRTS: 0 on inactive, other on RTS active
*
* The function return 0 if success, others if fail.
*/
static int libtty_tiocmset(int fd, char bDTR, char bRTS)
{
unsigned long controlbits = 0;
if (bDTR)
controlbits |= TIOCM_DTR;
if (bRTS)
controlbits |= TIOCM_RTS;
return ioctl(fd, TIOCMSET, &controlbits);
}
/**
* libtty_tiocmget - modem get
* @fd: file descriptor of tty device
* @modembits: pointer to modem status
*
* The function return 0 if success, others if fail.
*/
static int libtty_tiocmget_check(int fd, unsigned long *modembits, int cmd)
{
int ret = 0;
ret = ioctl(fd, TIOCMGET, modembits);
if (ret == 0) {
switch (cmd) {
case DTR_OFF_CMD: // DTR--DSR/DCD
if ((*modembits & TIOCM_DSR) != 0) {
printf("[error code: %d] DTR--DSR ERROR\n", 0);
ret = -1;
}
if ((*modembits & TIOCM_CD) != 0) {
printf("[error code: %d] DTR--DCD ERROR\n", 1);
ret = -1;
}
break;
case DTR_ON_CMD:
if ((*modembits & TIOCM_DSR) == 0) {
printf("[error code: %d] DTR--DSR ERROR\n", 0);
ret = -1;
}
if ((*modembits & TIOCM_CD) == 0) {
printf("[error code: %d] DTR--DCD ERROR\n", 1);
ret = -1;
}
break;
case RTS_OFF_CMD: // RTS--CTS/RI
if ((*modembits & TIOCM_CTS) != 0) {
printf("[error code: %d] RTS--CTS ERROR\n", 2);
ret = -1;
}
if ((*modembits & TIOCM_RI) != 0) {
printf("[error code: %d] RTS--RI ERROR\n", 3);
ret = -1;
}
break;
case RTS_ON_CMD:
if ((*modembits & TIOCM_CTS) == 0) {
printf("[error code: %d] RTS--CTS ERROR\n", 2);
ret = -1;
}
if ((*modembits & TIOCM_RI) == 0) {
printf("[error code: %d] RTS--RI ERROR\n", 3);
ret = -1;
}
break;
default:
break;
}
}
return ret;
}
static void sig_handler(int signo)
{
printf("capture sign no:%d\n", signo);
if (fp != NULL) {
fflush(fp);
fsync(fileno(fp));
fclose(fp);
}
exit(0);
}
void start_test(int fd)
{
int ret, i, times, num, nwrite, nread, len;
int len_w, pos_w, ret1, ret2, ret3;
int total = 0, off_w, off_r;
char c;
unsigned long modemstatus;
unsigned char buf_write[BUF_SIZE];
unsigned char buf_read[BUF_SIZE];
memset(buf_write, 0x00, BUF_SIZE);
memset(buf_read, 0x00, BUF_SIZE);
for (times = 0; times < 64; times++) {
for (i = 0; i < BUF_SIZE; i++)
buf_write[i] = i + BUF_SIZE * (times % 4);
/* Send 64 bytes */
off_w = 0;
len = BUF_SIZE;
while (len > 0) {
nwrite = write(fd, buf_write + off_w, len);
if (nwrite < 0) {
perror("write");
exit(1);
}
off_w += nwrite;
len -= nwrite;
}
/* Receive and judge */
off_r = 0;
while (nwrite > 0) {
nread = read(fd, buf_read + off_r, nwrite);
if (nread < 0) {
printf("read error!\n");
exit(1);
}
off_r += nread;
nwrite -= nread;
}
total += nread;
/* compare the buffer contents */
if (memcmp(buf_read, buf_write, BUF_SIZE) != 0) {
printf("[error code: %d] TXD/RXD test error\n", 4);
goto exit;
}
}
printf("TXD/RXD test passed\n");
/* Set DTR invalid */
if (libtty_tiocmset(fd, DTR_OFF, RTS_OFF) != 0)
goto exit;
usleep(10000);
ret1 = libtty_tiocmget_check(fd, &modemstatus, DTR_OFF_CMD);
/* Set DTR valid */
if (libtty_tiocmset(fd, DTR_ON, RTS_OFF) != 0)
goto exit;
usleep(10000);
ret2 = libtty_tiocmget_check(fd, &modemstatus, DTR_ON_CMD);
/* Set RTS valid */
if (libtty_tiocmset(fd, DTR_OFF, RTS_ON) != 0)
goto exit;
usleep(10000);
ret3 = libtty_tiocmget_check(fd, &modemstatus, RTS_ON_CMD);
if ((ret1 || ret2 || ret3) == 0)
printf("DTR/RTS/DSR/CTS/DCD/RI test passed\n");
printf("\n");
exit:
return;
}
int main(int argc, char *argv[])
{
int fd, ret, i, num, nwrite, nread;
int len_w, pos_w, ret1, ret2, ret3, ret4;
int total = 0, off = 0;
char c;
unsigned long modemstatus;
unsigned char buf_write[BUF_SIZE];
unsigned char buf_read[BUF_SIZE];
parse_opts(argc, argv);
signal(SIGINT, sig_handler);
fd = libtty_open(device);
if (fd < 0) {
printf("libtty_open: %s error.\n", device);
exit(0);
}
/* 2400bps test */
ret = libtty_setopt(fd, 2400, 8, 1, 'n', hardflow);
if (ret != 0) {
printf("libtty_setopt error.\n");
exit(0);
}
start_test(fd);
/* 9600bps test */
ret = libtty_setopt(fd, 9600, 8, 1, 'n', hardflow);
if (ret != 0) {
printf("libtty_setopt error.\n");
exit(0);
}
start_test(fd);
/* 115200bps test */
ret = libtty_setopt(fd, 115200, 8, 1, 'n', hardflow);
if (ret != 0) {
printf("libtty_setopt error.\n");
exit(0);
}
start_test(fd);
ret = libtty_close(fd);
if (ret != 0) {
printf("libtty_close error.\n");
exit(0);
}
return 0;
}