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文章
8440瀏覽量
284494 -
Linux
+關注
關注
88文章
11760瀏覽量
219032 -
PCI
+關注
關注
5文章
689瀏覽量
134254 -
串口
+關注
關注
15文章
1619瀏覽量
82823 -
PCIe
+關注
關注
16文章
1461瀏覽量
88419
發布評論請先 登錄
相關推薦
熱點推薦
藍牙多串口配置工具
本工具是一種多串口配置工具,基于MCF開發,可以將配置信息同時下發到多個串口,支持配置文件信息下發、文本下發和十六進制下發,一般用于芯片等配置工裝,提高生產效率。
多
發表于 03-25 19:22
【U盤量產問題】常見U盤量產的七大問題
心中有數再動手。另外量產前要先備份U盤的數據,因為一旦量產就會破壞U盤上的所有文件。 2、操作系統:因為量產工具的讀寫需涉及到硬件方面,所以
發表于 06-24 10:54
Linux下串口編程下載
linux 中的串口設備文件存放于/dev 目錄下,其中串口一,串口二對應設備名依次為/dev/ttyS0、/dev/ttyS1。在linux
發表于 11-15 17:39
?108次下載
Linux系統EXAR方案擴展串口
本文以Toradex基于NXP i.MX6D/6Q處理器的Apalisi.MX6D/QARM計算機模塊,在Linux系統下通過EXAR方案擴展8路串口。
發表于 09-18 08:40
?16次下載
linux命令輕松把單個工具變成批量執行工具
我們經常遇到這樣的業務場景,我們開發了一個線上工具,需要在Linux操作系統下面執行處理某些事情,例如我們開發了一個將用戶某個活動數據清0的工具,命令如下:。/clearTools -
從串口驅動到Linux驅動模型
本文通過對Linux下串口驅動的分析。由最上層的C庫。到操作系統系統調用層的封裝。再到tty子系統的核心。再到一系列線路規程。再到最底層的硬
安裝Linux的系統安裝工具
安裝_Linux的系統安裝工具,可以安裝各類Linux操作系統。通過iso鏡像文件的格式安裝。也可以安裝在U盤上,本人親測通過。 ?
發表于 09-11 10:21
?0次下載
Linux系統串口批量產測工具
評論