Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Тормозит отправка данных по Rs-485  (Прочитано 482 раз)
0 Пользователей и 1 Гость смотрят эту тему.
demon051
Помогающий

ru
Offline Offline

« : 10-11-2020 12:58 » 

Всем привет

Имеется прибор на процессоре imx6UL Под него сборка дебиан от производителя.

В общем надо с этого прибора опрашивать другие по rs-485 на скорости 9600.

Порт открывается настраивается в неблокирующем режиме и т.д. и т.п. Чтение / запись работают через select с заданным таймаутом.

В итоге получается как-то так, что реальная скорость отправки данных существенно ниже установленной 9600.

Команда int bytes_write = write(fd, frame_write, frame_write_size) выполняется моментально. Но дальше получаем жуткий таймаут на получение данных. Проверили осциллографом, что данные на опрашиваемый прибор приходят и он отвечает сразу же без задержек.

Стал разбираться. Вставил после write команду tcdrain( fd ) И вот уже на ее выполнение (ожидание пока данные уйдут) отжирается в среднем 30 миллисекунд. В посылке 7 байт.

Ясно, что функцией write сначала скидывается в буфер, а потом запихивается в физическое устройство.

Но не 30 же миллисекунд на отсылку 56 бит??

Вопрос: можно ли как-то оптимизировать в линуксе всё это дело.

Под виндами написал такой же код, там всё просто летает. Отправка данных на 9600 на той же линии занимает в пределах 5-7 миллисекуд и примерно через столько же приходит ответ.

Что в линуксе не так? Как исправить? Что настроить?
Записан
darkelf
Молодой специалист

ua
Offline Offline

« Ответ #1 : 10-11-2020 17:06 » 

У меня был опыт работы с RS-485 в Linux (правда под архитектурой x86, не ARM). Работало по динамике более-менее так-же как и в Windows.
Я делал так:
открывал через open(, O_RDWR | O_NOCTTY | O_NONBLOCK)
получал текущие настройки через tcgetattr();
переключал их в raw-режим через cfmakeraw();
включал в настройках стартовые стоповые биты, чётности;
устанавливал скорость через cfsetispeed(), cfsetospeed();
отключал ожидание символов и таймирование через [VMIN]=0 и [VTIME]=0
применял настройки через tcsetattr(, TCSAFLUSH, ).

Чтобы не блокироваться на долго, можно попробовать перед read() вызывать ioctl(, FIONREAD, ), чтобы узнать, сколько байтов можно прочитать без блокировки.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 10-11-2020 22:56 » 

Сорри за качество кода, я это писал лет так 18 назад.
Код: (C)
int setup(void) {
    static struct termios       tios;

    tty=open(tty_name,/*O_NONBLOCK|*/O_NOCTTY|O_RDONLY); // !!! read only mode
    if(tty<0) return 0x40;
    if(!isatty(tty)) return 0x50;
    if(log_open()) return 0x90;

    tcgetattr(tty,&tios);
    tios.c_iflag=IGNBRK;
    tios.c_oflag=0;
    tios.c_lflag=0;
    tios.c_cc[VMIN]=1;
    tios.c_cc[VTIME]=0;
    tios.c_cflag=CLOCAL|CREAD;
    switch(tty_bits[0]) {
        case '7': tios.c_cflag|=CS7; break;
        case '8': tios.c_cflag|=CS8; break;
        default: return 0xa0;
        }
    switch(tty_bits[1]) {
        case 'N': break;
        case 'E': tios.c_cflag|=PARENB; break;
        case 'O': tios.c_cflag|=PARENB|PARODD; break;
        default: return 0xa0;
        }
    switch(tty_bits[2]) {
        case '1': break;
        case '2': tios.c_cflag|=CSTOPB; break;
        default: return 0xa0;
        }
    switch(tty_fctrl[0]) {
        case 'N': break;
        case 'S': tios.c_cflag|=IXON|IXOFF; tios.c_cc[VSTOP]=0x13; tios.c_cc[VSTART]=0x11; break;
        case 'H': tios.c_cflag|=CRTSCTS; break;
        default: return 0xa0;
        }
    cfsetspeed(&tios,tty_speed);
    tcsetattr(tty,TCSANOW,&tios);

    signal(SIGTERM,cleanup);
    signal(SIGINT,cleanup);
    signal(SIGQUIT,cleanup);
    signal(SIGHUP,cleanup);
    signal(SIGALRM,log_flush);
    setup_re();
    mlockall(MCL_CURRENT|MCL_FUTURE);
    return 0;
    }
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
demon051
Помогающий

ru
Offline Offline

« Ответ #3 : 11-11-2020 11:00 » 

кусок кода на котором разбираю эту ситуацию

Код:
speed_t portSpeed(int port_speed)
{
if(port_speed == 19200) return B19200;
return B9600;
}

int main(int argc, char *argv[]) {

int baudrate = 9600;

string device = «/dev/ttymxc1»;

fd_set read_fs;
fd_set write_fs;
fd_set error_fs;

int fd = -1;

static const int buf_size = 39;
unsigned char buf[buf_size] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

struct termios options;

try
{
    fd = open(device.data(), O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK );
    if (fd == -1)
    {
        printf("ComPort -> Could not open device %s !!!", device.data());
        return 0;
    }
    else
    {
       printf("ComPort %s is open !!!\n", device.data());

       fcntl(fd, F_SETFL, 0);
    }

    tcgetattr(fd, &options);

    options.c_cflag = CS8 | CLOCAL | CREAD;

    options.c_iflag = IGNPAR;
    options.c_iflag |= IGNBRK;
    options.c_iflag &= ~BRKINT;
    options.c_iflag &= ~ICRNL;

    options.c_oflag = 0;
    options.c_lflag = 0;


    options.c_line = 0;

    options.c_cc[VTIME]    = 0;   
    options.c_cc[VMIN]     = 0;

    if(cfsetispeed(&options, portSpeed(baudrate))<0 ||
       cfsetospeed(&options, portSpeed(baudrate)<0))
    {
         printf("Unable to set com-port baudrate!!!\n");
         close(fd);
         return 0;
    }

    tcflush(fd, TCIOFLUSH);
    tcsetattr(fd, TCSANOW, &options);

}
catch(...)
{
    printf("Device open Exception!\n");
    printf("Exit from thread!\n");
    return 0;
}


struct timeval timeout_wait = { 0, 1000 };


struct timespec sp = { 1, 0 }; //1 секунда


int ret = 0;

static int frame_write_size = 7;
unsigned char frame_write[frame_write_size] = {0x53, 0x03, 0x0F, 0x77, 0x00, 0x2C, 0x0D}; //тестовая посылка

while(1)
{
    try
    {
        if(kbhit()!=0) break;

        FD_ZERO (&read_fs);
        FD_ZERO (&write_fs);
        FD_ZERO (&error_fs);

        FD_SET(fd, &read_fs);
        FD_SET(fd, &write_fs);
        FD_SET(fd, &error_fs);

        if ((ret = select(fd+1, &read_fs, &write_fs, &error_fs, &timeout_wait)) < 0)
        {
            continue;
        }

        if (FD_ISSET(fd, &error_fs))
        {
            printf("ComPort: ERROR DESCRIPTOR SELECTED!!! \n");

            nanosleep(&sp, NULL);
            continue;
        }

        if (FD_ISSET(fd, &read_fs) )
        {
            //memset(buf,0x00, sizeof(unsigned char)*buf_size);
            //buf[buf_size] = {0};
            buf[0] = 0;

            int bytes_read = read(fd, buf, buf_size);
           

            if(bytes_read>0)
            {

                printf("Bytes accepted: ");
                printf_asHex(buf, bytes_read);
               
                usleep(3000);//пауза на линии
            }
            else
            {
                printf("Socket read ERROR!!!\n");
                nanosleep(&sp, NULL);
            }
            continue;
        }

        if (FD_ISSET(fd, &write_fs))
        {


                int bytes_write = write(fd, frame_write, frame_write_size);                   

                tcdrain( fd );                   

                if(bytes_write != frame_write_size)
                {
                   printf("Data write ERROR: bytes_write(%d) != frame_write_size(%d)\n",bytes_write,frame_write_size);
                   nanosleep(&sp, NULL);
                }
                else
                {                                               

                    usleep(15000);//15 миллисекунд - искусственно подобранная задержка, чтобы всё пролезало

                }
            }
            continue;
        }

    }
    catch(...)
    {
        printf("ComPort read/write Exception!\n");
        break;
    }
}

if(fd!=-1)
{
    try
    {
        close(fd);
    }
    catch(...)
    {
    }
}
printf("EXIT MAIN\n!!!");
return 0;
}
Записан
darkelf
Молодой специалист

ua
Offline Offline

« Ответ #4 : 11-11-2020 12:01 » 

Посмотрите пожалуйста здесь - может это Ваша ситуация? В частности зелёный ответ..
Записан
Sla
Модератор

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 13-11-2020 09:09 » 

За скорость передачи отвечает сам порт (т.е. железка)
И повлиять на него может только тактовая частота

Исключение может быть при условии, что отправка бит может формироваться программно ( но это такой изврат, что лучше не обсуждать)


Добавлено через 2 минуты и 35 секунд:
Если что мой ответ связан с

>>> отправки данных существенно ниже установленной 9600
« Последнее редактирование: 13-11-2020 09:11 от Sla » Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
demon051
Помогающий

ru
Offline Offline

« Ответ #6 : 16-11-2020 07:03 » 

помогло отключение DMA для этого порта.
Записан
demon051
Помогающий

ru
Offline Offline

« Ответ #7 : 16-11-2020 07:05 » 

Посмотрите пожалуйста здесь - может это Ваша ситуация? В частности зелёный ответ..

да это именно оно.
отключил DMA заработало быстрее.
дело оказалось в том, что контроллер дма ждет заполнение буфера, который у него 32 байта, и только потом отправляет данные. а если данных в буфере менее 32, то отправка осуществляется по таймауту.
короче, хотели ребята как лучше, а получилось как всегда )
Записан
demon051
Помогающий

ru
Offline Offline

« Ответ #8 : 16-11-2020 07:06 » 

За скорость передачи отвечает сам порт (т.е. железка)
И повлиять на него может только тактовая частота

Исключение может быть при условии, что отправка бит может формироваться программно ( но это такой изврат, что лучше не обсуждать)


Добавлено через 2 минуты и 35 секунд:
Если что мой ответ связан с

>>> отправки данных существенно ниже установленной 9600


не только он за это отвечает.
 как настроено, так и отвечает )))
Записан
darkelf
Молодой специалист

ua
Offline Offline

« Ответ #9 : 16-11-2020 10:10 » new

да это именно оно.
отключил DMA заработало быстрее.

оно у них вообще кривое очень. Мы в iMX6 помучились и с SPI и с DMA.

короче, хотели ребята как лучше, а получилось как всегда )

На самом деле DMA там нет. Есть RISC-процессор самодельной архитектуры с загружаемой микропрограммой, который делает вид, что он DMA. Обычно не очень удачно, хотя вроде как идея и правильная была.
« Последнее редактирование: 16-11-2020 10:12 от darkelf » Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines