I want to read data from my serial port on Linux with C/C++ code. As I can still read from this serial port with GtkTerm and even with cat /dev/ttyUSB0
, this is not a hardware / driver problem.
我想用C/ c++代码从Linux上的串口读取数据。我仍然可以从这个串行端口读取GtkTerm,甚至使用cat /dev/ ttyusb0,这不是一个硬件/驱动程序问题。
It seems that the serial port is not initiated correctly as reading do works after the use a program like gtkterm.
似乎串行端口没有被正确地启动,因为在使用gtkterm这样的程序之后,读取do是有效的。
Here is the code I use to init the serial port (seconde version) :
下面是我用来初始化串行端口(辅助版本)的代码:
UbiDriver::UbiDriver(const std::string &ttyPort)
{
// Doc : http://www.easysw.com/~mike/serial/serial.html#2_4
m_serialHandle = open(ttyPort.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); // Open perif
if(m_serialHandle < 0)
{
MY_THROW("Impossible d'ouvrir le port '" << ttyPort << "' !\nerrno = " << errno);
}
// Conf
//if(fcntl(m_serialHandle, F_SETFL, 0) == -1) // lecture en mode bloquant
if(fcntl(m_serialHandle, F_SETFL, O_NONBLOCK) == -1) // lecture en mode non bloquant
{
MY_THROW("fcntl failed !\nerrno = " << errno);
}
struct termios options;
tcgetattr(m_serialHandle, &options); // Init struct avec la conf actuelle
cfsetispeed(&options, B9600); // In speed
cfsetospeed(&options, B9600); // Output speed
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_cflag |= (CLOCAL | CREAD); // Enable the receiver and set local mode...
options.c_cflag &= ~PARENB; // Desactive bit de parité
options.c_cflag &= ~CSTOPB; // Désactive 2 stop bits -> Active 1 stop bits
options.c_cflag &= ~CSIZE; // Désactive le bit "CSIZE"
options.c_cflag |= CS8; // Communication sur 8 bits
options.c_oflag &= ~OPOST; // Raw output is selected by resetting the OPOST option in the c_oflag member:
// Application de la conf
if(tcsetattr(m_serialHandle, TCSAFLUSH, &options) == -1) // Vidage buffer & application immédiate
{
MY_THROW("tcsetattr failed !\nerrno = " << errno);
}
}
And to read data from the port
从端口读取数据
std::string UbiDriver::GetAnswer()
{
const int buffSize = 1024;
char buffer[buffSize] = {'\0'};
int count = 0;
std::string wholeAnswer = "";
int noDataTime = 0;
while(noDataTime < 2) // Tant qu'il y a des données à lire
{
count = read(m_serialHandle, buffer, buffSize - 1);
if(count == -1)
{
MY_THROW("Impossible de lire sur le port serie. Verifiez la connexion avec l'imprimante !")
}
if(count > 0)
{
noDataTime = 0;
buffer[count] = '\0';
for(int i = 0; i < count; i++)
{
buffer[i] &= ~128; // Supression du premier 1 du binaire
}
wholeAnswer += std::string(buffer);
std::cout << count << std::endl;
}
else
{
noDataTime++;
usleep(100000);
}
}
cerr << "----------- Answer -----------" << endl;
cerr << "Size = " << wholeAnswer.size() << endl;
cerr << wholeAnswer << endl;
return wholeAnswer;
return std::string("");
}
Note: this code is a second version completed with your comments.
注意:这段代码是用您的评论完成的第二个版本。
2 个解决方案
#1
0
Checking you code, I did not see where you set raw input mode.
检查您的代码,我没有看到您在哪里设置原始输入模式。
You may need to add:
你可能需要补充:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
选项。c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
Maybe what changed on this version of Ubuntu is that default input mode is set to CANONICAL and not raw. You can check this document for full detail:
也许Ubuntu版本的改变在于默认的输入模式设置为CANONICAL而不是raw。详情请参阅本文件:
http://www.easysw.com/~mike/serial/serial.html#2_3_2
http://www.easysw.com/迈克/串行/ _3_2 serial.html # 2
Also check the flow control used by your other device. If it expects hardware flow control, you have to set it when opening the port. The code probably worked in a previous version because the values used by default there were compatible with your program. Your program should set all options it needs to work: port speed, parity, flow control, input mode, etc.
还要检查其他设备使用的流量控制。如果需要硬件流控制,则必须在打开端口时设置它。代码可能在以前的版本中工作,因为默认情况下使用的值与程序兼容。您的程序应该设置它需要工作的所有选项:端口速度、奇偶性、流控制、输入模式等。
#2
0
I opened the gtkterm source code and I finally found a solution : in fact, you need to override the terminos structure (and yes, you should not have to do it normally).
我打开了gtkterm源代码,最终找到了一个解决方案:实际上,您需要重写终止结构(是的,您不应该正常地这么做)。
If someone finds a better solution, feel free to post it. In the meantime, here is the working code, with english comments :
如果有人找到了更好的解决方案,请随意张贴。同时,这里是工作代码,有英文注释:
To open the serial port :
打开串口:
// Doc : http://www.easysw.com/~mike/serial/serial.html#2_4
m_serialHandle = open(ttyPort.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); // Open serial port
if(m_serialHandle < 0)
{
MY_THROW("Impossible d'ouvrir le port '" << ttyPort << "' !\nerrno = " << errno);
}
// Read mode
//if(fcntl(m_serialHandle, F_SETFL, 0) == -1) // Blocking read
if(fcntl(m_serialHandle, F_SETFL, O_NONBLOCK) == -1) // Non-blocking read
{
MY_THROW("fcntl failed !\nerrno = " << errno);
}
// Get current terminos configuration
struct termios options;
tcgetattr(m_serialHandle, &options);
// Force termios values (should not be needed, but is)
options.c_cflag = B9600;
options.c_oflag = 0;
options.c_lflag = 0;
options.c_iflag = IGNPAR | IGNBRK;
options.c_cc[VTIME] = 0;
options.c_cc[VMIN] = 1;
// Set data rate
cfsetispeed(&options, B9600); // In speed
cfsetospeed(&options, B9600); // Output speed
// Set communication flags
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_cflag |= (CLOCAL | CREAD); // Enable the receiver and set local mode...
options.c_cflag &= ~PARENB; // Desactive bit de parité
options.c_cflag &= ~CSTOPB; // Désactive 2 stop bits -> Active 1 stop bits
options.c_cflag &= ~CSIZE; // Désactive le bit "CSIZE"
options.c_cflag |= CS8; // Communication sur 8 bits
options.c_oflag &= ~OPOST; // Raw output is selected by resetting the OPOST option in the c_oflag member:
// Disable flow control
options.c_iflag &= ~(IXON | IXOFF);
// Apply
if(tcsetattr(m_serialHandle, TCSANOW, &options) == -1) // Vidage buffer & application immédiate
{
MY_THROW("tcsetattr failed !\nerrno = " << errno);
}
// Empty buffers
tcflush(m_serialHandle, TCIOFLUSH);
To read (non-blocking)
阅读(阻塞)
const int buffSize = 1024;
char buffer[buffSize] = {'\0'};
int count = 0;
std::string wholeAnswer = "";
int noDataTime = 0;
while(noDataTime < 3) // while there is data to be read
{
count = read(m_serialHandle, buffer, buffSize - 1);
// May fail in NON-BLOCKING mode if there is nothing to be read
/*if(count == -1)
{
MY_THROW("Impossible de lire sur le port serie. Verifiez la connexion avec l'imprimante !")
}*/
if(count > 0)
{
noDataTime = 0;
buffer[count] = '\0';
/*for(int i = 0; i < count; i++)
{
buffer[i] &= ~128; // Delete first binary bit. Could be useful for 7 bit communication, if the first bit is set to 1
}*/
wholeAnswer += std::string(buffer);
}
else
{
noDataTime++;
usleep(100000);
}
}
if(!wholeAnswer.empty())
{
cerr << "----------- Answer -----------" << endl;
cerr << "Size = " << wholeAnswer.size() << endl;
cerr << wholeAnswer << endl;
}
return wholeAnswer;
#1
0
Checking you code, I did not see where you set raw input mode.
检查您的代码,我没有看到您在哪里设置原始输入模式。
You may need to add:
你可能需要补充:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
选项。c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
Maybe what changed on this version of Ubuntu is that default input mode is set to CANONICAL and not raw. You can check this document for full detail:
也许Ubuntu版本的改变在于默认的输入模式设置为CANONICAL而不是raw。详情请参阅本文件:
http://www.easysw.com/~mike/serial/serial.html#2_3_2
http://www.easysw.com/迈克/串行/ _3_2 serial.html # 2
Also check the flow control used by your other device. If it expects hardware flow control, you have to set it when opening the port. The code probably worked in a previous version because the values used by default there were compatible with your program. Your program should set all options it needs to work: port speed, parity, flow control, input mode, etc.
还要检查其他设备使用的流量控制。如果需要硬件流控制,则必须在打开端口时设置它。代码可能在以前的版本中工作,因为默认情况下使用的值与程序兼容。您的程序应该设置它需要工作的所有选项:端口速度、奇偶性、流控制、输入模式等。
#2
0
I opened the gtkterm source code and I finally found a solution : in fact, you need to override the terminos structure (and yes, you should not have to do it normally).
我打开了gtkterm源代码,最终找到了一个解决方案:实际上,您需要重写终止结构(是的,您不应该正常地这么做)。
If someone finds a better solution, feel free to post it. In the meantime, here is the working code, with english comments :
如果有人找到了更好的解决方案,请随意张贴。同时,这里是工作代码,有英文注释:
To open the serial port :
打开串口:
// Doc : http://www.easysw.com/~mike/serial/serial.html#2_4
m_serialHandle = open(ttyPort.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); // Open serial port
if(m_serialHandle < 0)
{
MY_THROW("Impossible d'ouvrir le port '" << ttyPort << "' !\nerrno = " << errno);
}
// Read mode
//if(fcntl(m_serialHandle, F_SETFL, 0) == -1) // Blocking read
if(fcntl(m_serialHandle, F_SETFL, O_NONBLOCK) == -1) // Non-blocking read
{
MY_THROW("fcntl failed !\nerrno = " << errno);
}
// Get current terminos configuration
struct termios options;
tcgetattr(m_serialHandle, &options);
// Force termios values (should not be needed, but is)
options.c_cflag = B9600;
options.c_oflag = 0;
options.c_lflag = 0;
options.c_iflag = IGNPAR | IGNBRK;
options.c_cc[VTIME] = 0;
options.c_cc[VMIN] = 1;
// Set data rate
cfsetispeed(&options, B9600); // In speed
cfsetospeed(&options, B9600); // Output speed
// Set communication flags
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_cflag |= (CLOCAL | CREAD); // Enable the receiver and set local mode...
options.c_cflag &= ~PARENB; // Desactive bit de parité
options.c_cflag &= ~CSTOPB; // Désactive 2 stop bits -> Active 1 stop bits
options.c_cflag &= ~CSIZE; // Désactive le bit "CSIZE"
options.c_cflag |= CS8; // Communication sur 8 bits
options.c_oflag &= ~OPOST; // Raw output is selected by resetting the OPOST option in the c_oflag member:
// Disable flow control
options.c_iflag &= ~(IXON | IXOFF);
// Apply
if(tcsetattr(m_serialHandle, TCSANOW, &options) == -1) // Vidage buffer & application immédiate
{
MY_THROW("tcsetattr failed !\nerrno = " << errno);
}
// Empty buffers
tcflush(m_serialHandle, TCIOFLUSH);
To read (non-blocking)
阅读(阻塞)
const int buffSize = 1024;
char buffer[buffSize] = {'\0'};
int count = 0;
std::string wholeAnswer = "";
int noDataTime = 0;
while(noDataTime < 3) // while there is data to be read
{
count = read(m_serialHandle, buffer, buffSize - 1);
// May fail in NON-BLOCKING mode if there is nothing to be read
/*if(count == -1)
{
MY_THROW("Impossible de lire sur le port serie. Verifiez la connexion avec l'imprimante !")
}*/
if(count > 0)
{
noDataTime = 0;
buffer[count] = '\0';
/*for(int i = 0; i < count; i++)
{
buffer[i] &= ~128; // Delete first binary bit. Could be useful for 7 bit communication, if the first bit is set to 1
}*/
wholeAnswer += std::string(buffer);
}
else
{
noDataTime++;
usleep(100000);
}
}
if(!wholeAnswer.empty())
{
cerr << "----------- Answer -----------" << endl;
cerr << "Size = " << wholeAnswer.size() << endl;
cerr << wholeAnswer << endl;
}
return wholeAnswer;