LINUX.ORG.RU

Работа с COM-портом через QSerialPort

 , ,


1

1

Qt: 5.6.1, GNU/Linux: Gentoo. Пишу программку по обмену данными с счётчиком Меркурий 200 по RS-485. Свисток USB->RS-485: Exar XR21B1411.

Пишу используя QSerialPort.

Проблема в следующем, программа не передаёт/принимает данные, при этом никаких сообщений об ошибках нет.

При этом на serial->open( QIODevice::ReadWrite ) на свисте загораться светодиод, а на serail->close(); гаснет.

Метод serial->write( byte, 7 ) возвращает 7 байт как записанных, а сигнал readyRead() вообще не вызывается.

Да, в винде всё работает, но мне надо бы в линуксе.

~ $ lsusb
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 007 Device 011: ID 04e2:1411 Exar Corp. 
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 003: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
Bus 003 Device 002: ID 046d:c505 Logitech, Inc. Cordless Mouse+Keyboard Receiver
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

Какое может быть решение?

★★★★

Последнее исправление: keeper_b (всего исправлений: 1)

QSerialPort

не знаю как сейчас, но раньше в лялихе эта радость не работала, пришлось отказаться.

Какое может быть решение?

по-старинке: open read write close.

Rastafarra ★★★★
()
Ответ на: комментарий от Rastafarra

не знаю как сейчас, но раньше в лялихе эта радость не работала

Сейчас же QSerialPort работает великолепно, как на USB портах так и на всяческих встроенных.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Особенно много я работал через QSerialPort как раз с RS-485, куча разнообразных девайсов и конвертеров опробовано.

I-Love-Microsoft ★★★★★
()

При этом на serial->open( QIODevice::ReadWrite ) на свисте загораться светодиод, а на serail->close(); гаснет. Метод serial->write( byte, 7 ) возвращает 7 байт как записанных, а сигнал readyRead() вообще не вызывается.

Могу предположить что RTS DTR надо дергать (QSerialPort это поддерживает) чтобы переключать направление передачи, может в винде драйвер более совершенен.

по-старинке: open read write close

Внутрях QSerialPort использует все типичные функции Linux-а, прям как по старинке, нет смысла пробовать напрямую работать те же но в профиль.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft
~ $ lsusb
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 007 Device 011: ID 04e2:1411 Exar Corp. 
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 003: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
Bus 003 Device 002: ID 046d:c505 Logitech, Inc. Cordless Mouse+Keyboard Receiver
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
keeper_b ★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

нет смысла пробовать напрямую работать те же но в профиль.

я забыл детали, но у нас тоже что-то там то читалось, то не читалось... где-то внутри кутэ что-то происходило, но бороться с ним смысла особо не было, т.е. по трудозатратам открыть-закрыть девайс получилось набыдлокодить быстрее :)

Rastafarra ★★★★
()

flush() после write() делай, помню что-то еще там было, вроде readyRead нужно заново привязывать после открытия порта

quest ★★★★
()
Ответ на: комментарий от quest

Делал, не помогает, и опять же в венде всё работает и так.

Я всё таки больше на драйвер грешу, но не знаю, что с этим можно сделать.

keeper_b ★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

А я вроде ответил выше, Qt: 5.6.1. В Gentoo пока новее не завезли. =)

keeper_b ★★★★
() автор топика
Последнее исправление: keeper_b (всего исправлений: 1)
Ответ на: комментарий от keeper_b

я юзал его в qt 5.2.1 и в win и в lin, помню были различные проблемы, но решилось все в конце концов

quest ★★★★
()

А может кто подскажет, есть ли какой ни будь способ протестировать сам драйвер?

keeper_b ★★★★
() автор топика
Ответ на: комментарий от Rastafarra

не знаю как сейчас, но раньше в лялихе эта радость не работала, пришлось отказаться.

Вбросил, так вбросил. молодец.

kuzulis ★★
()

Какое может быть решение?

1. На ошибки проверял (коннект к error() был бы уместен)? 2. Параметры у-ва задавал корректно? 3. Как передаешь/принимаешь?

По-хорошему нужно привести хотя-бы часть кода. А еще лучше: переделай пример Terminal под свои нужды и смотри что посылается/принимается, а что нет.

kuzulis ★★
()
Ответ на: комментарий от kuzulis

только хотела дать ссыль на кроссплатформ, а ты уже тут. скорее всего, порядок инициализации. ЕМНИП, там вроде была особенность в том, что установка параметров должна была происходить после коннекта, что несколько странно.

Iron_Bug ★★★★★
()

Сравни что передаётся в/из порта на винде (ProcessMonitor от SysInternals) и линуксе (strace).

tnodir
()
Ответ на: комментарий от kuzulis

Упростил код, проверил в винде работает (счётчик на него отвечает) в linux нет.

mainwindow.hpp

#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP

#include <QMainWindow>
#include <QSerialPort>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void read();
    void errorSerail(QSerialPort::SerialPortError err);

    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    QSerialPort *serial;
};

#endif // MAINWINDOW_HPP

mainwindow.cpp

#include "mainwindow.hpp"
#include "ui_mainwindow.h"

#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    serial(new QSerialPort(this))
{
    ui->setupUi(this);

    serial->setPortName("/dev/ttyXRUSB0");
    serial->setBaudRate(QSerialPort::Baud9600);
    serial->setDataBits(QSerialPort::Data8);
    serial->setParity(QSerialPort::NoParity);
    serial->setStopBits(QSerialPort::OneStop);
    serial->setFlowControl(QSerialPort::NoFlowControl);

    serial->open(QIODevice::ReadWrite);

    if(serial->isWritable())
    {
        qDebug() << "Yes, i can write to port!";
    }

    connect(serial, SIGNAL(readyRead()),
            this, SLOT(read()));
    connect(serial, SIGNAL(error(QSerialPort::SerialPortError)),
            this, SLOT(errorSerail(QSerialPort::SerialPortError)));

    serial->clear();

    QByteArray be = QByteArray::fromHex("000BDF2F2F4A32");
    qint64 bytesWrited = serial->write(be, 7);
    serial->flush();
    qint64 bytesAvailable = serial->bytesAvailable();
    qDebug() << "Bytes writed: " << bytesWrited;
    qDebug() << "Bytes available: " << bytesAvailable;
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::read()
{
    QByteArray bytes = serial->readAll();
    qDebug() << QString(bytes.toHex().toUpper());
}

void MainWindow::errorSerail(QSerialPort::SerialPortError err)
{
    qDebug() << "Error: " << err;
}

void MainWindow::on_pushButton_clicked()
{
    serial->close();
}

keeper_b ★★★★
() автор топика
Последнее исправление: keeper_b (всего исправлений: 1)
Ответ на: комментарий от Iron_Bug

должна была происходить после коннекта, что несколько странно.

Это было дааавныыым даааавнооо. Сейчас (уж точно от 5,6 и выше) по-фиг когда параметры ставить (до или после).

kuzulis ★★
()
Ответ на: комментарий от keeper_b
...
    serial->open(QIODevice::ReadWrite);
...
    connect(serial, SIGNAL(readyRead()),
            this, SLOT(read()));
    connect(serial, SIGNAL(error(QSerialPort::SerialPortError)),
            this, SLOT(errorSerail(QSerialPort::SerialPortError)));
....

Должно быть наоборот (сначала коннекты, а потом уже открывать), а то можно профукать что-нить интересное.

Плюс к этому, неплохо бы проверить код возврата open().

...
serial->flush();
...

Это не нужно (да и вообще flush не нужно использовать).

qint64 bytesWrited = serial->write(be, 7);

1. Здесь всегда bytesWrited будет > 0, т.к. данные просто добавляются к внутреннему буферу, т.е. проверять тут смысла нет. Но чтобы узнать что данные переданы (по крайней мере до кишек самой железки) есть сигнал bytesWritten(qint64).

2. Лучше делать write(ba) сразу.

qint64 bytesAvailable = serial->bytesAvailable();

Здесь данных не было и никогда не будет!

ЗЫ: А по факту, возможно для этого девайса не реализованы select/poll/ в ядре... хотя, это ну невероятно. Т.е. тут надо тебе самому отлаживать... попробуй strace или подобное что-то чтобы примерно посмотреть что передается/принимается.

kuzulis ★★
()
Последнее исправление: kuzulis (всего исправлений: 1)
Ответ на: комментарий от kuzulis

Ах, да!

Сначала замкни Rx/Tx и запусти терминальный пример Terminal из QtSerialPort... и посмотри, будет чо или нет вообще.

kuzulis ★★
()
Ответ на: комментарий от kuzulis

попробуй strace

А можешь пример кинуть как им можно посмотреть?

keeper_b ★★★★
() автор топика
Ответ на: комментарий от kuzulis

Если ты имел ввиду буквально замкнуть, проводом, то сделал и запустил терминал. Эффекта никакого, даже в винде.

keeper_b ★★★★
() автор топика
Ответ на: комментарий от kuzulis

В винде хоть сниферы есть, а линуксе все не рабочие какие то.

keeper_b ★★★★
() автор топика
Ответ на: комментарий от keeper_b

Если ты имел ввиду буквально замкнуть, проводом, то сделал и запустил терминал. Эффекта никакого, даже в винде.

А, да, это же RS485.

kuzulis ★★
()
Последнее исправление: kuzulis (всего исправлений: 1)
Ответ на: комментарий от kuzulis

В общем вчера поздно ночью заработало.

Дело оказалось в драйвере. Я попробовал не родной драйвер, а включил все те, что в ядре были. Пересобрал ядро и заработало.

Теперь устройство называется не /dev/ttyXRUSB0, а /dev/ttyACM0.

Но есть одна особенность, байты читаются от устройства побайтово, а в винде идут блоком.

Можно и тут их как ни будь блоком читать? Подскажи.

keeper_b ★★★★
() автор топика
Ответ на: комментарий от keeper_b

Можно и тут их как ни будь блоком читать?

Foo::onReadyRead()
{
    const qint64 bytesAvailable = serial->bytesAvailable();
    if (bytesAvailable < expected)
        return;
    const QByteArray frame = serial->read(expected); // читаем скок надо
}
kuzulis ★★
()
Ответ на: комментарий от kuzulis

Спасибо за совет.

Пардон, что долго не отвечал, уезжал на дачу.

Но так сделать не получилось, часть байтов теряется и счётчик возвращает массив разной длинны.

Сделал так:

void MainWindow::serialReceived()
{
    bytesAvailable += serial->bytesAvailable();

    bytesArray += serial->readAll();
    qDebug() << "Bytes available: " << bytesAvailable;

    qDebug() << QString(bytesArray.toHex().toUpper());
}

где bytesAvailable и bytes определены как члены класса.

Но в любом случае спасибо!

keeper_b ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.