LINUX.ORG.RU

Протокол обмена Linux -> датчик

 , ,


1

4

Добрый день. Первая программа на С++, ругайте только не сильно. Задача вроде простая, есть датчик температуры с выходом RS485 порта я его через конвертер подключил к RS232. При загрузки датчика в консоль дает ответ «Ready». По документации датчик отвечает на запросы, не сыпет данные в консоль постоянно. Запрос состоит из шестнадцатеричного кода [старт бит] ..... [стоп бит] (набор битов есть). Я написал программу

int fd;
int k;
std::vector<uint8_t> wr;
uint8_t buf[512] = { 0 }; 
struct termios oldtio, newtio;
int open_port();

int main(int argc,char **argv){
        fd = open_port();

        wr.clear();
        wr.insert(wr.begin(), 0x56);
        wr.push_back(0x65);
        wr.push_back(0x01);
        wr.push_back(0x02);
        wr.push_back(0x98);
        wr.push_back(0x56);

        /*WRITE*/
        int m;
        for (m=0; m<=wr.size()-1; m++){
                write(fd, &wr[m], wr.size());
                printf(" for= %x \n", wr[m]);
        }
     
        /*READ*/
        while(k!=-1) {
                k=read(fd,buf,512);
                printf(" k= %d  \n",k);
                printf(" buf= %x  \n",buf[0]);
                sleep(1);
        }
        close(fd);
        return(0);
}
int open_port(){
   struct sigaction saio; 
   fd = open(/dev/ttyS0, O_RDWR | O_NOCTTY |O_NONBLOCK);
   fcntl(fd, F_SETOWN, getpid()); 
   fcntl(fd, F_SETFL, O_ASYNC|O_NONBLOCK|fcntl(fd, F_GETFL));
   tcgetattr(fd,&oldtio); /* save current port settings */
   cfsetispeed(&newtio, B9600); 
   cfsetospeed(&newtio, B9600); 
   newtio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
       | INLCR | IGNCR | ICRNL | IXON);
   newtio.c_oflag &= ~OPOST;
   newtio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
   newtio.c_cflag &= ~(CSIZE | PARENB);
   newtio.c_cflag |= CS8;
   if((tcsetattr(fd,TCSANOW,&newtio)) != 0){ 
        printf("Error setattr\n");
        exit(1);
   }
   return (fd);
}
Ответа нет!
Возможно я не правильно даже посылаю пакет, а может считываю не правильно. Если есть у кого опыт поделитесь!

Функцию open_port() нашел в интернете.
include не стал перечислять, компиляция проходит.

Не знаю, на кой вам тут понадобился vector из-за которого стало С++.

Зачем у вас fd глобальная переменная?

Нужно проверять результаты системных вызовов, в том числе open() и write(). Раз у вас O_NONBLOCK, то, скорее всего read() происходит раньше, чем данные пришли от устройства...

mky ★★★★★
()

датчик температуры с выходом RS485 порта я его через конвертер подключил к RS232

C++ здесь ни при чем. Тебе нужно разбираться с Си, с железом и с драйверами. Если вся эта конструкция работает в полудуплексном режиме, то возможен такой вариант, что данные из UART не успевают передаться до того, как ты переключаешься на приём. С другой стороны если не успеешь переключиться на приём вовремя, то можешь просто пропустить часть данных. То есть здесь получается работа в realtime. Нужно использовать соответствующие возможности драйвера в ядре: https://github.com/torvalds/linux/blob/master/Documentation/serial/serial-rs4...

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 3)

fd = open(/dev/ttyS0, O_RDWR | O_NOCTTY |O_NONBLOCK);

А слабо было привести РЕАЛЬНЫЙ текст?

vodz ★★★★★
()

так вы подключили через конвертер это usb устройство? я про то что если ваш конвертер это доп устройство, то это явно не ttyS0 т.е первый com

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

Он мне отвечает на первом порту «Ready» и номер датчика, Если подключить второй датчик пишет «Ready2».

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

Я отправлял пакет в не цикла так write(fd, &wr, wr.size()); и так write(fd, &wr[0], wr.size());

Я не уверен как правильно. Что касается компиляции файла, нормально проходит ошибок нет. Что не так?

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

Вообще понимаю. Я передаю массив байт в com порт. Как передать правильно векторный массив в com порт я документацию не нашел. Ошибку я пока одну понял в цикле я указываю количества байт всего массива wr.size() а не одного значения. Я читал что векторный массив можно передать пакетом без цикла, указав первый байт цикла и wr.size(), но так у меня тоже не работало.

prostoR
() автор топика

cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);

два раза мало, обычно с пятого-шестого раза встает нужная скорость. Тк скорее всего шлешь не с той скоростью, датчик тупо не понимает что ты передаешь

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

Я проверил подключение просто
Запускаю скрипт и в то же время запустил подключение на этот порт minicom на скорости 9600, у меня скрипт завершает работу.
k= -1

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

Правильно отправлять пакет без цикла.

 
/*WRITE*/ 
write(fd, &wr[0], wr.size()); 
printf(" w= %s \n", &wr[0]); 
Судя по выводу формирует пакет и отправляет на COM порт.

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

Я проверил подключение просто
Запускаю скрипт и в то же время запустил подключение на этот порт minicom на скорости 9600, у меня скрипт завершает работу.
k= -1

об чем и речь, -1 - скорость скорее всего не 9600, выставляй в цикле.

for(int i = 0; i < 15; ++i) {
    cfsetispeed(&newtio, B9600); 
}
anonymous
()
Ответ на: комментарий от prostoR

Вообще понимаю. Я передаю массив байт в com порт. Как передать правильно векторный массив в com порт я документацию не нашел. Ошибку я пока одну понял в цикле я указываю количества байт всего массива wr.size() а не одного значения. Я читал что векторный массив можно передать пакетом без цикла, указав первый байт цикла и wr.size(), но так у меня тоже не работало.

скорее всего ты наступил на стандартные грабли. часто при автонастройке pulseaudio оно по умолчанию берет на себя все ttySx для внутреннего межпроцессного обмена(т.к. их уже мало кто использует, есть же ttyUSB0). Удали все звуковые карты, должно помочь.

anonymous
()

Зачем так сложно? Можно же упростить:

        wr.clear();
        wr.insert(wr.begin(), 0x56);
        wr.push_back(0x65);
        wr.push_back(0x01);
        wr.push_back(0x02);
        wr.push_back(0x98);
        wr.push_back(0x56);

        write(fd, &wr[m], wr.size());

const uint8_t wr[] = { 0x56 0x65, 0x01, 0x02, 0x98, 0x56 };
write(fd, wr, sizeof(wr)/sizeof(wr[0]));

Ну и добавить проверки и избавиться от глобальных переменных.

p.s. Не занл, что в школе задают такое задание. Думал сейчас в школах учат только нажимать кнопочки в ms word.

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

А так работает ?

Кстати, такие вещи надо делать, сгруппировав команды в одно перенаправление, так как по умолчанию переоткрытие сериального порта может сбить настройки, что вы делаете в stty. Типа так:

( stty 9600; echo -n $'\x56\x65\x01\x02\x98\x56'; sleep 1; head -c 1000 | hexdump > /dev/tty ) < /dev/ttyS0 > /dev/ttyS0

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

Да, так будет сильно лучше. А то как-то дебажил свой драйвер и не мог понять почему из питоньего скрипта отсылка команды/получение ответа работает прекрасно, а из shell через echo/cat - нет. Пока не вспомнил что на открытии файла буфер ответа резетится

Сюда бы еще прикрутить что-нибудь чтобы тыщу байтов не ждать ...

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

stty -F /dev/ttyS0 9600; echo -n $'\x56\x65\x01\x02\x98\x56' >/dev/ttyS0; sleep 1; hexdump -C /dev/ttyS0

Выполнил команду, ошибки ввода/вывода нет.
Мне сказали что чтение надо делать из порта, по мимо параметров задать процедуру которая будет запускаться каждый раз когда в буфере чтения появятся данные. Пока изучаю эту тему.

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

А зачем эти задержки на RS485 ? Насколько я понимаю, это задача мастера с протоколом высокого уровня который опрашивает ноды на шине как ему заблагорассудится и следит за тем чтобы одновременно был только один сендер ?

Так что если драйвер реализован корректно, то буферизация и тайминг в обоих направлениях не должны волновать user-space приложение

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

А зачем эти задержки на RS485 ?

Они для адаптера rs232-rs485 и для UART на компе.

Так что если драйвер реализован корректно, то буферизация и тайминг в обоих направлениях не должны волновать user-space приложение

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

asaw ★★★★★
()

А что там за датчик? И какой там протокол обмена? Давайте начнем с этого. Возможно на датчике есть настройка скорости обмена(и др. параметров). И кстати в с++ удобно с rs232 работать с помощью boost asio или qt. Зачем все вручную херачить?

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

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

Можно тут по-подробнее, для лучшего понимания ? Мне всегда казалось что отличие на современном железе между RS232 и RS485 это «Tx enable» сигнал который говорит контроллеру захватить контроль над шиной, причем такой захватчик должен быть только один в каждый момент времени, что является задачей высокопротокольного мастера. Соответственно, это может сделать и драйвер если есть чего посылать. Адаптер RS232/RS485 устроен скорее всего таким же образом - есть чего посылать - устанавливаем «Tx enable», нету - сбрасываем. Зачем драйверу в таком случае быть в курсе что там висит после RS232 - не совсем понятно.

Или я ошибаюсь и все гораздо хуже и страшнее ?

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

Окстись, какие задержки? тебе на вход идёт поток байт, который ложится сначала в FIFO буфер UART, а потом уже в буфер в ОЗУ.

Задержки могут играть роль в протоколах типа modbus, но там задержки между байтами и не более чем.

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

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

Всё зависит от адаптера конечно. Адаптер переключает приём/передачу по сигналу RTS. Может случиться так, что данные ещё не переданы до конца (из буфера UART или адаптера), а драйвер думает, что они переданы и включает RTS. Адаптер при этом сразу же прекращает передачу и ререключается на приём (то есть на приём от устройства и передачу компу). В результате часть данных не передаётся. Это пример реальной ситуации, правда, была она под виндой, но не суть. А решением было как раз введение задержки перед включением RTS. Большой эта задержка при этом тоже не могла быть, иначе терялся ответ от устройства.

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

Там не такой риалтайм, чтобы с этим не мог справиться линукс.

asaw ★★★★★
()

Вот как в ядре для самых популярных UART реализована поддержка RS-485: https://github.com/torvalds/linux/blob/master/drivers/tty/serial/8250/8250_po...

А вот что примерно нужно изобразить автору после установки скорости: http://www.armadeus.org/wiki/index.php?title=RS-485

Разумеется, IXON (и IXOFF) устанавливать не нужно (по крайней мере все адаптеры RS-485, что я видел, имели аппаратный контроль передачи по пину RTS).

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 2)
Ответ на: комментарий от samson
#include <stdio.h>   /* Стандартные объявления ввода/вывода */
#include <string.h>  /* Объявления строковых функций */
#include <unistd.h>  /* Объявления стандартных функций UNIX */
#include <fcntl.h>   /* Объявления управления файлами */
#include <errno.h>   /* Объявления кодов ошибок */
#include <termios.h> /* Объявления управления POSIX-терминалом */
#include <sys/signal.h>
#include <stdlib.h>
#include <stdint.h>
#include <ctime>
#include <vector>
#include <queue>
#include <numeric>

#define BAUDRATE B9600
#define MODEMDEVICE "/dev/ttyS0"

int fd;
uint8_t keepRunning = 1;
uint8_t n;
std::vector<uint8_t> pR;
std::vector<uint8_t> wr;
int res;
struct termios oldtio, newtio;
unsigned char buf[255];
void intHandler(int dummy) {
    keepRunning = 0;
}

//READ ARRAY
void makeRpacket(int n){
    if (n != 0){
        uint8_t fstart = 0; 
        int i;
        for (i=0; i<=n; i++){
            if (buf[i] == 0x56) fstart = 1;
            if (fstart == 1){
                pR.push_back(buf[i]);
            }
            if (buf[i] == 0x56 and fstart == 1) break; 
        }
        //packet_processing();
    }

}

//READ FUNC
void signal_handler_IO (int status){
    res = read(fd,buf,255);
    if (res == 0){
       buf[res]=0;
    }
    printf("111%s\n", buf);
    makeRpacket(res-1);
    usleep(1000*25);
}
//OPEN PORT FINC
int open_port(){
    struct sigaction saio; /* definition of signal action */
    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the device to be non-blocking (read will return immediatly) */
    if (fd <0) { perror(MODEMDEVICE); exit(-1); }
    tcgetattr(fd,&oldtio); /* save current port settings */
    bzero(&newtio, sizeof(newtio));
    sigemptyset(&saio.sa_mask); /* install the signal handler before making the device asynchronous */
    saio.sa_handler = signal_handler_IO;
    saio.sa_flags = 0;
    saio.sa_restorer = NULL;
    //saio.sa_sigaction = signal_handler_IO;
    sigaction(SIGIO,&saio,NULL);
    fcntl(fd, F_SETOWN, getpid()); /* allow the process to receive SIGIO */
    fcntl(fd, F_SETFL, O_ASYNC|O_NONBLOCK|fcntl(fd, F_GETFL));
    cfsetispeed(&newtio, BAUDRATE); /* Set Read  Speed as 115200 */
    cfsetospeed(&newtio, BAUDRATE); /* Set Write Speed as 115200 */
    cfsetospeed(&newtio, BAUDRATE); /* Set Write Speed as 115200 */
    cfsetospeed(&newtio, BAUDRATE); /* Set Write Speed as 115200 */
    newtio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
        | INLCR | IGNCR | ICRNL | IXON | IGNPAR);
    newtio.c_oflag = 0;
    newtio.c_oflag &= ~OPOST;
    newtio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    newtio.c_cflag &= ~(CSIZE | PARENB);
    //newtio.c_cflag |= CS8;
    newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
    if((tcsetattr(fd,TCSANOW,&newtio)) != 0){ /* Set the attributes to the termios structure*/
        printf("Error setattr\n");
        exit(1);
    }
    return fd;
}

//CLOSE FUNC
void closeind(){
    tcsetattr(fd,TCSANOW,&oldtio);
    tcflush(fd, TCIFLUSH);
    close(fd);
}

//MAIN FUNC
int main(int argc,char** argv){

        signal(SIGINT, intHandler);
        fd = open_port();
        //WRITE ARRAY
        wr.clear();
        wr.insert(wr.begin(), 0x56);
        wr.push_back(0x65);
        wr.push_back(0x01);
        wr.push_back(0x02);
        wr.push_back(0x98);
        wr.push_back(0x56);

        while (keepRunning == 1){
                usleep(1000*90);
                write(fd, &wr[0], wr.size());
                printf("1x%s \n", &wr[0]);
                if (res != 0) {
                        printf(" RES= %d  \n", res);
                        printf(" BUF= %s  \n", buf);
                        printf(" w= %X  \n", pR[0]);
                }
        }
        closeind();
        return(0);
}

Всем спасибо кто давал советы, выше рабочий скрипт. Он и пишет и читает данные с буфера COM порта когда буфер не пустой.

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

cfsetispeed(&newtio, BAUDRATE);
cfsetospeed(&newtio, BAUDRATE);
cfsetospeed(&newtio, BAUDRATE);
cfsetospeed(&newtio, BAUDRATE);

с четвертого раза встало, хорошие драйвера, видимо, у меня с 5-6 только

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