在 Linux 上用 C 语言读写串口 [英] Reading and writing to serial port in C on Linux
问题描述
我正在尝试使用 FTDI 通过 USB 端口发送/接收数据,因此我需要使用 C/C++ 处理串行通信.我正在使用 Linux (Ubuntu).
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).
基本上,我连接到一个正在侦听传入命令的设备.我需要发送这些命令并读取设备的响应.命令和响应都是 ASCII 字符.
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.
使用 GtkTerm 一切正常,但是当我切换到 C 编程时,我遇到了问题.
Everything works fine using GtkTerm but, when I switch to C programming, I encounter problems.
这是我的代码:
#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', ' ', '
', ' '};
int n_written = write( USB, cmd, sizeof(cmd) -1 );
/* Allocate memory for read buffer */
char buf [256];
memset (&buf, ' ', 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);
read()
返回 0(根本没有读取字节)或阻塞直到超时(VTIME
).我假设发生这种情况是因为 write()
没有发送任何内容.在这种情况下,设备不会收到命令,我也无法收到响应.事实上,在我的程序被阻止读取时关闭设备实际上成功地获得了响应(设备在关闭时发送了一些东西).
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).
奇怪的是添加了这个
cout << "I've written: " << n_written << "bytes" << endl;
在 write()
调用之后,我收到:
right after write()
call, I receive:
I've written 6 bytes
这正是我所期望的.只有我的程序无法正常工作,例如 我的设备无法接收我实际在端口上写入的内容.
which is exactly what I expect. Only my program doesn't work as it should, like my device cannot receive what I'm actually writing on port.
我尝试了不同的方法和解决方案,也涉及数据类型(我尝试使用 std::string,例如 cmd = "INIT
"
或 const char
) 但没有任何效果.
I've tried different things and solution, also regarding data types (I've tried using std::string, such as cmd = "INIT
"
or const char
) but nothing really worked.
谁能告诉我哪里错了?
提前谢谢你.
此代码的先前版本使用了
Previously version of this code used
unsigned char cmd[] = "INIT
"
unsigned char cmd[] = "INIT
"
还有 cmd[] = "INIT
"
.我更改了它,因为我的设备的命令 sintax 报告为
and also cmd[] = "INIT
"
. I changed it because command sintax for my device is reported as
<command><SPACE><CR>
.
我也尝试在读取时避免使用 O_NONBLOCK
标志,但我只会一直阻塞.我试过使用 select()
但没有任何反应.只是为了尝试,我创建了一个等待循环,直到数据可用,但我的代码永远不会退出循环.顺便说一句,等待或 usleep()
是我需要避免的.报告的只是我的代码的摘录.完整的代码需要在实时环境中运行(特别是 OROCOS),所以我真的不想要类似睡眠的功能.
I've also tried avoiding the O_NONBLOCK
flag 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.
推荐答案
我已经解决了我的问题,所以我在这里发布正确的代码以防有人需要类似的东西.
I've solved my problems, so I post here the correct code in case someone needs similar stuff.
开放端口
int USB = open( "/dev/ttyUSB0", O_RDWR| O_NOCTTY );
设置参数
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;
}
写
unsigned char cmd[] = "INIT
";
int n_written = 0,
spot = 0;
do {
n_written = write( USB, &cmd[spot], 1 );
spot += n_written;
} while (cmd[spot-1] != '
' && n_written > 0);
绝对没有必要逐字节写入,int n_written = write(USB, cmd, sizeof(cmd) -1)
工作正常.
It was definitely not necessary to write byte per byte, also int n_written = write( USB, cmd, sizeof(cmd) -1)
worked fine.
最后,阅读:
int n = 0,
spot = 0;
char buf = ' ';
/* Whole response*/
char response[1024];
memset(response, ' ', sizeof response);
do {
n = read( USB, &buf, 1 );
sprintf( &response[spot], "%c", buf );
spot += n;
} while( buf != '
' && 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;
}
这个对我有用.谢谢大家!
This one worked for me. Thank you all!
这篇关于在 Linux 上用 C 语言读写串口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!