在 Linux 上用 C 读写串口

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18108932/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:29:40  来源:igfitidea点击:

Reading and writing to serial port in C on Linux

clinuxserial-porttty

提问by Lunatic999

I'm trying to send/receive data over an USB Port using FTDI, so I need to handle serial communication using C/C++. I'm working on Linux(Ubuntu).

我正在尝试使用 FTDI 通过 USB 端口发送/接收数据,因此我需要使用 C/C++ 处理串行通信。我在Linux(Ubuntu) 上工作。

Basically, I am connected to a device which is listening for incoming commands. I need to send those commands and read device's response. Both commands and response are ASCII characters.

基本上,我连接到一个正在侦听传入命令的设备。我需要发送这些命令并读取设备的响应。命令和响应都是ASCII 字符

Everything works fine using GtkTerm but, when I switch to C programming, I encounter problems.

使用 GtkTerm 一切正常,但是当我切换到 C 编程时,我遇到了问题。

Here's my code:

这是我的代码:

#include <stdio.h>      // standard input / output functions
#include <stdlib.h>
#include <string.h>     // string function definitions
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <errno.h>      // Error number definitions
#include <termios.h>    // POSIX terminal control definitions

/* Open File Descriptor */
int USB = open( "/dev/ttyUSB0", O_RDWR| O_NONBLOCK | O_NDELAY );

/* Error Handling */
if ( USB < 0 )
{
cout << "Error " << errno << " opening " << "/dev/ttyUSB0" << ": " << strerror (errno) << endl;
}

/* *** Configure Port *** */
struct termios tty;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 )
{
cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << endl;
}

/* Set Baud Rate */
cfsetospeed (&tty, B9600);
cfsetispeed (&tty, B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;        // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;
tty.c_cflag     &=  ~CRTSCTS;       // no flow control
tty.c_lflag     =   0;          // no signaling chars, no echo, no canonical processing
tty.c_oflag     =   0;                  // no remapping, no delays
tty.c_cc[VMIN]      =   0;                  // read doesn't block
tty.c_cc[VTIME]     =   5;                  // 0.5 seconds read timeout

tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines
tty.c_iflag     &=  ~(IXON | IXOFF | IXANY);// turn off s/w flow ctrl
tty.c_lflag     &=  ~(ICANON | ECHO | ECHOE | ISIG); // make raw
tty.c_oflag     &=  ~OPOST;              // make raw

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );

if ( tcsetattr ( USB, TCSANOW, &tty ) != 0)
{
cout << "Error " << errno << " from tcsetattr" << endl;
}

/* *** WRITE *** */

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '
cout << "I've written: " << n_written << "bytes" << endl; 
'}; int n_written = write( USB, cmd, sizeof(cmd) -1 ); /* Allocate memory for read buffer */ char buf [256]; memset (&buf, '
I've written 6 bytes
', sizeof buf); /* *** READ *** */ int n = read( USB, &buf , sizeof buf ); /* Error Handling */ if (n < 0) { cout << "Error reading: " << strerror(errno) << endl; } /* Print what I read... */ cout << "Read: " << buf << endl; close(USB);

What happens is that read()returns 0 (no bytes read at all) or block until timeout (VTIME). I'm assuming this happens because write()does not send anything. In that case, device wouldn't receive command and I cannot receive response. In fact, turning off the device while my program is blocked on reading actually succeded in getting a response (device sends something while shutting down).

发生的情况是read()返回 0(根本没有读取字节)或阻塞直到超时(VTIME)。我假设发生这种情况是因为write()不发送任何内容。在这种情况下,设备不会收到命令,我也无法收到响应。事实上,当我的程序在读取时被阻止时关闭设备实际上成功地获得了响应(设备在关闭时发送了一些东西)。

Strange thing is that adding this

奇怪的是,添加这个

int USB = open( "/dev/ttyUSB0", O_RDWR| O_NOCTTY );

right after write()call, I receive:

write()打电话后,我收到:

struct termios tty;
struct termios tty_old;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 ) {
   std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;
}

/* Save old tty parameters */
tty_old = tty;

/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B9600);
cfsetispeed (&tty, (speed_t)B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;            // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;

tty.c_cflag     &=  ~CRTSCTS;           // no flow control
tty.c_cc[VMIN]   =  1;                  // read doesn't block
tty.c_cc[VTIME]  =  5;                  // 0.5 seconds read timeout
tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

/* Make raw */
cfmakeraw(&tty);

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );
if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) {
   std::cout << "Error " << errno << " from tcsetattr" << std::endl;
}

which is exactly what I expect. Only my program doesn't work as it should, like my device cannot receive what I'm actually writingon port.

这正是我所期望的。只有我的程序无法正常工作,就像我的设备无法接收我在端口上实际写入的内容一样。

I've tried different things and solution, also regarding data types (I've tried using std::string, such as cmd = "INIT \r"or const char) but nothing really worked.

我尝试了不同的东西和解决方案,也关于数据类型(我尝试过使用 std::string,例如cmd = "INIT \r"or const char),但没有任何效果。

Can someone tell me where I'm wrong?

有人能告诉我我错在哪里吗?

Thank you in advance.

先感谢您。

EDIT:Previously version of this code used

编辑:使用此代码的以前版本

unsigned char cmd[] = "INIT \n"

unsigned char cmd[] = "INIT \n"

and also cmd[] = "INIT \r\n". I changed it because command sintax for my device is reported as

还有cmd[] = "INIT \r\n"。我更改了它,因为我的设备的命令 sintax 报告为

<command><SPACE><CR>.

<command><SPACE><CR>.

I've also tried avoiding the O_NONBLOCKflag on reading, but then I only block until forever. I've tried using select()but nothing happens. Just for a try, I've created a waiting loop until data is avaliable, but my code never exit the loop. Btw, waiting or usleep()is something I need to avoid. Reported one is only an excerpt of my code. Complete code needs to work in a real-time environment(specifically OROCOS) so I don't really want sleep-like function.

我也试过O_NONBLOCK在阅读时避开标志,但后来我只阻止到永远。我试过使用select()但没有任何反应。只是为了尝试,我创建了一个等待循环,直到数据可用,但我的代码从未退出循环。顺便说一句,等待或者usleep()是我需要避免的事情。报告的一个只是我的代码的摘录。完整的代码需要在实时环境(特别是 OROCOS)中工作,所以我真的不想要类似睡眠的功能。

采纳答案by Lunatic999

I've solved my problems, so I post here the correct code in case someone needs similar stuff.

我已经解决了我的问题,所以我在这里发布了正确的代码,以防有人需要类似的东西。

Open Port

开放端口

unsigned char cmd[] = "INIT \r";
int n_written = 0,
    spot = 0;

do {
    n_written = write( USB, &cmd[spot], 1 );
    spot += n_written;
} while (cmd[spot-1] != '\r' && n_written > 0);

Set parameters

设置参数

int n = 0,
    spot = 0;
char buf = '
unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '
unsigned char cmd[] = "INIT\r\n";
'};
'; /* Whole response*/ char response[1024]; memset(response, '
int tmp, serialLines;

cout << "Dropping Reading DTR and RTS\n";
ioctl ( readFd, TIOCMGET, & serialLines );
serialLines &= ~TIOCM_DTR;
serialLines &= ~TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
usleep(100000);
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;
sleep (2);

cout << "Setting Reading DTR and RTS\n";
serialLines |= TIOCM_DTR;
serialLines |= TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;
', sizeof response); do { n = read( USB, &buf, 1 ); sprintf( &response[spot], "%c", buf ); spot += n; } while( buf != '\r' && n > 0); if (n < 0) { std::cout << "Error reading: " << strerror(errno) << std::endl; } else if (n == 0) { std::cout << "Read nothing!" << std::endl; } else { std::cout << "Response: " << response << std::endl; }

Write

##代码##

It was definitely not necessary to write byte per byte, also int n_written = write( USB, cmd, sizeof(cmd) -1)worked fine.

绝对没有必要按字节写入字节,也可以正常int n_written = write( USB, cmd, sizeof(cmd) -1)工作。

At last, read:

最后,阅读

##代码##

This one worked for me. Thank you all!

这个对我有用。谢谢你们!

回答by radarhead

Some receivers expect EOL sequence, which is typically two characters \r\n, so try in your code replace the line

某些接收器需要 EOL 序列,通常是两个字符\r\n,因此请尝试在您的代码中替换该行

##代码##

with

##代码##

BTW, the above way is probably more efficient. There is no need to quote every character.

顺便说一句,上述方式可能更有效。没有必要引用每个字符。

回答by guest

1) I'd add a /n after init. i.e. write( USB, "init\n", 5);

1)我会在init之后添加一个/n。即写(USB,“init\n”,5);

2) Double check the serial port configuration. Odds are something is incorrect in there. Just because you don't use ^Q/^S or hardware flow control doesn't mean the other side isn't expecting it.

2) 仔细检查串口配置。赔率是那里的东西是不正确的。仅仅因为您不使用 ^Q/^S 或硬件流控制并不意味着另一方不期望它。

3) Most likely:Add a "usleep(100000);after the write(). The file-descriptor is set not to block or wait, right? How long does it take to get a response back before you can call read? (It has to be received and buffered by the kernel, through system hardware interrupts, before you can read()it.) Have you considered using select()to wait for something to read()? Perhaps with a timeout?

3)最有可能:write()之后 添加一个"usleep(100000);。文件描述符设置为不阻塞或等待,对吗?在调用 read 之前需要多长时间才能得到响应?(它必须由内核通过系统硬件中断接收和缓冲,然后才能read()它。) 您是否考虑过使用select()来等待read()?也许超时?

Edited to Add:

编辑添加:

Do you need the DTR/RTS lines? Hardware flow control that tells the other side to send the computer data? e.g.

您需要 DTR/RTS 线路吗?告诉对方发送计算机数据的硬件流控?例如

##代码##