【问题标题】:Reading from serial port on Ubuntu Linux (11.10) in C++在 C++ 中从 Ubuntu Linux (11.10) 上的串口读取
【发布时间】:2012-02-06 18:38:06
【问题描述】:

我想在 Linux 上使用 C/C++ 代码从我的串口读取数据。 由于我仍然可以使用 GtkTerm 甚至使用 cat /dev/ttyUSB0 从该串行端口读取数据,因此这不是硬件/驱动程序问题。

使用 gtkterm 之类的程序后,似乎没有正确启动串行端口,因为读取确实有效。

这是我用来初始化串口的代码(第二版):

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);
    }
}

并从端口读取数据

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("");
}

注意:此代码是您的 cmets 完成的第二个版本。

【问题讨论】:

  • 你说它不起作用。什么不起作用?会发生什么?
  • 我不会回答你的问题。这可能是一个特定的平台问题。但也许您可能会考虑使用比使用 termios 更高级别的库来执行此操作。也许是 Python 中的 PySerial 或使用 C 中的高级串行端口 I/O 库。
  • 看看这是否适合你stackoverflow.com/questions/9046649/…
  • 为什么检查read()是否返回-1的代码被注释掉了?它应该在那里,并且应该记录 errno,或者最好是 strerror(errno)。您还应该检查 tcgetattr、cfsetospeed 和 cfsetispeed 是否返回错误。
  • man tcsetattr 也这样说:“请注意,如果任何请求的更改可以成功执行,则 tcsetattr() 将返回成功。因此,在进行多项更改时,可能需要在此调用之后使用进一步调用 tcgetattr() 以检查所有更改是否已成功执行。"

标签: c++ c linux serial-port


【解决方案1】:

我打开了gtkterm源代码,终于找到了解决办法:其实你需要重写terminos结构(是的,你不应该正常做)。

如果有人找到更好的解决方案,请随时发布。同时,这里是工作代码,带有英文 cmets:

打开串口:

// 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);

读取(非阻塞)

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;

【讨论】:

    【解决方案2】:

    检查你的代码,我没有看到你在哪里设置原始输入模式。

    您可能需要添加:

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    也许这个版本的 Ubuntu 的改变是默认输入模式设置为 CANONICAL 而不是 raw。 您可以查看此文档以获取完整的详细信息:

    http://www.easysw.com/~mike/serial/serial.html#2_3_2

    同时检查您的其他设备使用的流量控制。如果它需要硬件流控制,你必须在打开端口时设置它。该代码可能在以前的版本中有效,因为默认情况下使用的值与您的程序兼容。您的程序应该设置它需要工作的所有选项:端口速度、奇偶校验、流量控制、输入模式等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多