1、說明
本文針對Linux系統上如何對各類串口硬件進行出廠測試進行硬件連接和軟件使用說明,提供的軟件測試工具wchsertest,適用于USB、PCI、PCIe轉串口設備等、同樣也適用于原生ttyS串口。
2、串口測試硬件連接
在測試前,需要制作單獨的硬件治具,按下表連接信號線:
引腳連接示意圖:
?
3、軟件使用方法
(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
4、測試錯誤碼說明
根據輸出的錯誤碼和終端輸出信息可判斷故障信號線,下表為錯誤碼和說明。
錯誤碼 | 錯誤碼說明 |
---|---|
0 | DTR--DSR線錯誤 |
1 | DTR--DCD線錯誤 |
2 | RTS--CTS線錯誤 |
3 | RTS--RI線錯誤 |
4 | TXD--RXD線錯誤 |
5、測試實例
(1)測試成功實例
軟件分別以2400bps、9600bps、115200bps各測試一次。
? ?
(2)測試錯誤實例
?
根據輸出信息可知,DTR—DSR信號通訊存在錯誤,錯誤碼:0。
6、wchsertest工具源碼
/*
* serial port factory test utility.
*
* Copyright (C) 2023 Nanjing Qinheng Microelectronics Co., Ltd.
* Web: http://wch.cn
* Author: WCH
#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]
", prog);
puts(" -D --device tty device to use
");
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
");
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
");
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
");
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.
");
return -1;
}
if (isatty(fd) == 0) {
printf("not tty device.
");
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
", 0);
ret = -1;
}
if ((*modembits & TIOCM_CD) != 0) {
printf("[error code: %d] DTR--DCD ERROR
", 1);
ret = -1;
}
break;
case DTR_ON_CMD:
if ((*modembits & TIOCM_DSR) == 0) {
printf("[error code: %d] DTR--DSR ERROR
", 0);
ret = -1;
}
if ((*modembits & TIOCM_CD) == 0) {
printf("[error code: %d] DTR--DCD ERROR
", 1);
ret = -1;
}
break;
case RTS_OFF_CMD: // RTS--CTS/RI
if ((*modembits & TIOCM_CTS) != 0) {
printf("[error code: %d] RTS--CTS ERROR
", 2);
ret = -1;
}
if ((*modembits & TIOCM_RI) != 0) {
printf("[error code: %d] RTS--RI ERROR
", 3);
ret = -1;
}
break;
case RTS_ON_CMD:
if ((*modembits & TIOCM_CTS) == 0) {
printf("[error code: %d] RTS--CTS ERROR
", 2);
ret = -1;
}
if ((*modembits & TIOCM_RI) == 0) {
printf("[error code: %d] RTS--RI ERROR
", 3);
ret = -1;
}
break;
default:
break;
}
}
return ret;
}
static void sig_handler(int signo)
{
printf("capture sign no:%d
", 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!
");
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
", 4);
goto exit;
}
}
printf("TXD/RXD test passed
");
/* 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
");
printf("
");
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.
", device);
exit(0);
}
/* 2400bps test */
ret = libtty_setopt(fd, 2400, 8, 1, 'n', hardflow);
if (ret != 0) {
printf("libtty_setopt error.
");
exit(0);
}
start_test(fd);
/* 9600bps test */
ret = libtty_setopt(fd, 9600, 8, 1, 'n', hardflow);
if (ret != 0) {
printf("libtty_setopt error.
");
exit(0);
}
start_test(fd);
/* 115200bps test */
ret = libtty_setopt(fd, 115200, 8, 1, 'n', hardflow);
if (ret != 0) {
printf("libtty_setopt error.
");
exit(0);
}
start_test(fd);
ret = libtty_close(fd);
if (ret != 0) {
printf("libtty_close error.
");
exit(0);
}
return 0;
}
?
-
usb
+關注
關注
60文章
7959瀏覽量
265084 -
Linux
+關注
關注
87文章
11320瀏覽量
209851 -
PCI
+關注
關注
4文章
670瀏覽量
130337 -
串口
+關注
關注
14文章
1555瀏覽量
76665 -
PCIe
+關注
關注
15文章
1243瀏覽量
82782
發布評論請先 登錄
相關推薦
評論