C ++串行通信读取数据有效,但写入失败

时间:2018-11-27 20:59:46

标签: linux serial-port file-descriptor termios fcntl

我正在创建一个类,用于嵌入式系统和在Linux环境中运行的C ++应用程序之间的串行通信。因此,我使用了适用于Linux的termios API,其描述为here

构造函数将打开设备的串行端口。在我的情况下,这是我使用的arduino微控制器的“ ttyUSB0”。接下来,它将设置波特率和其他端口选项。

我还添加了用于在串行端口上读取或写入数据的功能。因为read是一个阻塞函数(直到收到数据或超时才返回),所以我添加了一个函数来检查是否有可用的字节,在调用“ Read()”之前可以这样做。

制作完测试用例后,阅读似乎可以正常工作。实际上,函数“ Available()”确实返回了可用的字节数。阅读它们后将它们打印到控制台。

但是,由于某些未知原因,即使我“相信”我正确地遵循了指南中的步骤,我的写入功能也无法正常工作。我为写功能创建了一个测试用例:arduino收到正确的消息后应闪烁其内置的led。一条消息以开始标记“#”开始并以结束标记“ $”结束时是正确的。

当我使用腻子测试工具或arduino的串行监视器发送正确的消息时,指示灯将闪烁。但是,当我通过自己的write函数发送消息时,不会发生这种情况。

arduino具有其他内置的LED,用于指示RX和TX引脚上的数据。一旦我从自己的写入功能发送数据,这些指示灯实际上就会亮起,但是从不调用测试用例中的闪烁功能。然后,我检查了是否读取了所有字节,但是当从我自己的写入函数发送数据时,arduino的“ Serial.available()”从不返回大于0的值。

我认为该错误可能在写函数本身或在串行端口的配置中。到目前为止,我还没弄清楚。是否有人对此有任何经验或知识,或者对我应该如何解决此问题有任何提示?

预先感谢

德克

Linux代码:

main.cpp

#include "serial.h"
#include <iostream>

using namespace std;

int main()
{
    //TEST CASE FOR WRITING DATA
    Serial serial("/dev/ttyUSB0");
    serial.Write("#TEST$"); 

    //TEST CASE FOR READING DATA
    /*while (true)
    {
        char message[100];
        char * ptr = NULL;
        while (serial.Available() > 0)
        {
            char c; 
            serial.Read(&c);
            switch(c)
            {
            case '#':
                ptr = message;
                break;
            case '$':
                if (ptr != NULL)
                {
                    *ptr = '\0';
                }
                std::cout << "received: " << message << std::endl;
                ptr = NULL;
                break;
            default:
                 if (ptr != NULL)
                {
                    *ptr = c;
                    ptr++;
                }
                break;
            }
        }
    }*/
    return EXIT_SUCCESS;
}

Serial.h

#ifndef SERIAL_H
#define SERIAL_H

#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <string>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>

class Serial
{
    private:
        int fd;
    public:
        Serial(std::string device);

        ~Serial()
        {
            close(fd);
        };     

        int Available();
        void Read(char * buffer, int amountOfBytes);
        void Read(char * bytePtr);
        int Write(std::string message);
};

#endif

Serial.cpp

#include "serial.h"
#include <stdexcept>
#include <string.h>

Serial::Serial(std::string device)
{   
    // Open port
    fd = open(device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd < 0)
    {
        throw std::runtime_error("Failed to open port!");
    }

    // Config
    struct termios config;

    tcgetattr(fd, &config);

    // Set baudrate
    cfsetispeed(&config, B9600);
    cfsetospeed(&config, B9600);

    // 9600 8N1
    config.c_cflag &= ~PARENB;
    config.c_cflag &= ~CSTOPB;
    config.c_cflag &= ~CSIZE;
    config.c_cflag |=  CS8;

    // Disable hardware based flow control
    config.c_cflag &= ~CRTSCTS;

    // Enable receiver
    config.c_cflag |= CREAD | CLOCAL;                               

    // Disable software based flow control
    config.c_iflag &= ~(IXON | IXOFF | IXANY);

    // Termois Non Cannoincal Mode 
    config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 

    // Minimum number of characters for non cannoincal read
    config.c_cc[VMIN]  = 1;

    // Timeout in deciseconds for read
    config.c_cc[VTIME] = 0; 

    // Save config
    if (tcsetattr(fd, TCSANOW, &config) < 0)                        
    {
        close(fd);
        throw std::runtime_error("Failed to configure port!");
    }

    // Flush RX Buffer
    if (tcflush(fd, TCIFLUSH) < 0)
    {
        close(fd);
        throw std::runtime_error("Failed to flush buffer!");
    }
}

int Serial::Available()
{
    int bytes = 0;
    if (ioctl(fd, TIOCINQ, &bytes) < 0)
    {
        close(fd);
        throw std::runtime_error("Failed to check buffer!");
    }
    return bytes;
}

void Serial::Read(char * buffer, int amountOfBytes)
{
    if (read(fd, buffer, amountOfBytes) < 0)
    {
        close(fd);
        throw std::runtime_error("Failed to read bytes!");
    }
}

void Serial::Read(char * bytePtr)
{
    return Serial::Read(bytePtr, 1);
}

int Serial::Write(std::string message)
{
    int length = message.size();
    if (length > 100)
    {
        throw std::invalid_argument("Message may not be longer than 100 bytes!");
    }

    char msg[101];
    strcpy(msg, message.c_str());

    int bytesWritten = write(fd, msg, length);

    if (bytesWritten < 0)
    {
        close(fd);
        throw std::runtime_error("Failed to write bytes!");
    }

    return bytesWritten;
}

Arduino代码

void setup() 
{
    Serial.begin(9600);
    pinMode(LED_BUILTIN, OUTPUT);
}

void loop() 
{
    //TEST-CASE FOR WRITING DATA
    /*Serial.print("#TEST$");
    delay(1000);*/

    //TEST-CASE FOR READING DATA
    char message[100];
    char * ptr = NULL;
    while (Serial.available() > 0)
    {
        char c = Serial.read();
        switch(c)
        {
        case '#':
            ptr = message;
            break;
        case '$':
            if (ptr != NULL)
            {
                *ptr = '\0';
            }
            ptr = NULL;
            int messageLength = strlen(message);
            Blink();
            break;
        default:
            if (ptr != NULL)
            {
              *ptr = c;
              ptr++;
            }
            break;
        }
    }
}

void Blink()
{
    digitalWrite(LED_BUILTIN, HIGH);
    delay(1000);
    digitalWrite(LED_BUILTIN, LOW);
    delay(1000);
}

1 个答案:

答案 0 :(得分:0)

已解决。 “打开”功能在串行端口上发送信号,arduino将其解释为重启信号。我通过disabling auto-reset解决了这个问题。

或者,您可以在保存配置后添加两秒钟的延迟。

此问题专门适用于arduino微控制器。