Ник:
Пароль:

Контакты

E-mail: info@starterkit.ru
тел.: +7 922 680-21-73
тел.: +7 922 680-21-74
Телеграм: t.me/starterkit_ru

Способы оплаты

User Info


Добро пожаловать,
Guest

Регистрация или входРегистрация или вход
Потеряли пароль?Потеряли пароль?

Ник:
Пароль:

ПользователейПользователей:1
Поисковых ботовПоисковых ботов:3
ГостейГостей:1

ОбновитьПодробнееВсегоВсего:5
Форум » starterkit.ru » Embedded Linux
Задержка при считывании с ttymxc1
SmartRogue
Добавлено 29.01.2020 18:23 Редактировалось 29.01.2020 18:26
0
Сообщение: 1
SmartRogue
0

Пункты: 1449
Регистрация: 27.11.2017
Здравствуйте, товарищи эксперты. У меня очередной затык. Сразу скажу, что я тупенький во всех этих железячных делах...

В наличии:
  • RS-232 устройство, которое непрерывно шлёт данные стандартными пакетами по 10 байт, 115200-8-N-1. Межпакетного интервала нет.
  • SK-iMX6S-OEM-Ind
  • SK-iMX6S/53/50-MB
  • buildroot-2017.08 на базе ядра 4.1.15-2.1.0 для i.mx6 от sasamy (тык).
  • Дебиан 9.3, модифицированный sasamy (тык).

    Что надо:
    Надо из юзерспейса читать посылки и обрабатывать их.

    Что накодил:
    Использую /dev/ttymxc1 , termios.h , fcntl.h

    Получение дескриптора:
    Код

    d = open(portPath, O_NOCTTY | O_NONBLOCK | O_RDWR);


    Настройки, которые задаёт программа через termios:
    Код

    stty --all -F /dev/ttymxc1
    speed 115200 baud; rows 0; columns 0; line = 0;
    intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
    eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
    werase = ^W; lnext = ^V; discard = ^O; min = 0; time = 0;
    -parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts
    -ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
    -iuclc -ixany -imaxbel -iutf8
    -opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
    -isig -icanon -iexten -echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
    echoctl echoke -flusho -extproc


    В чём проблема:
    Данные впервые поступают в read только после накопления 4096 Б, и далее вновь по мере накопления 4096 Б. То есть они валятся огромным массивом примерно раз в 500мс, что неприемлемо. Причём считывание идёт как 4095 + 1.

    Для сравнения, на компьютере (тоже дебиан) в такой же код при передаче данных через usb-rs-232 адаптер (ICPcon i-7561) данные залетают в read по 10-40 байт.

    Что надо:
    Получать и обрабатывать данные по мере поступления. Пусть склеенными даже по 5-10 пакетов, но не по 400...

    Что пробовал:
    Пробовал играть с параметрами VMIN и VTIME, делая их ненулевыми в разных сочетаниях. Эффекта никакого.

    Пожалуйста, спасите-помогите...
  • Спуститься к концу Подняться к началу
    Персональная информация
    sasamy
    Добавлено 29.01.2020 18:40 Сообщение: 2
    sasamy
    4.71

    Пункты: 83540
    Регистрация: 14.08.2009
    Цитата

    Что надо:
    Надо из юзерспейса читать посылки и обрабатывать их.


    в сети есть масса примеров как работать с компортами, например

    https://www.teuniz.net/RS-232/
    Спуститься к концу Подняться к началу
    Персональная информация
    SmartRogue
    Добавлено 30.01.2020 14:44 Сообщение: 3
    SmartRogue
    0

    Пункты: 1449
    Регистрация: 27.11.2017
    Цитата
    в сети есть масса примеров как работать с компортами, например


    Переписал с использованием встроенного в Qt QSerialPort - результат тот же: данные приходят в read по 4096 байт.

    Вытянул репозиторий по предложенной Вами ссылке, скомпилил test_rx, предварительно дополнив перечень портами /tev/ttymx*. Результат вновь тот же: данные приходят слепленными в 4096 байт (4095+1).

    Я давно себя таким идиотом не ощущал...

    По-прежнему нужна помощь.
    Спуститься к концу Подняться к началу
    Персональная информация
    sasamy
    Добавлено 30.01.2020 17:33 Сообщение: 4
    sasamy
    4.71

    Пункты: 83540
    Регистрация: 14.08.2009
    Цитата
    скомпилил test_rx, предварительно дополнив перечень портами /tev/ttymx*. Результат вновь тот же: данные приходят слепленными в 4096 байт (4095+1).


    а кто их должен разлеплять ? сколько есть в буфере порта столько и читается

    n = RS232_PollComport(cport_nr, buf, 4095);

    сделайте тут

    n = RS232_PollComport(cport_nr, buf, 10);

    будет максимум 10 байт за раз читать
    Спуститься к концу Подняться к началу
    Персональная информация
    SmartRogue
    Добавлено 02.02.2020 22:08 Редактировалось 02.02.2020 22:12 Сообщение: 5
    SmartRogue
    0

    Пункты: 1449
    Регистрация: 27.11.2017
    Цитата
    а кто их должен разлеплять ? сколько есть в буфере порта столько и читается


    Проблема не в том, что я не могу их разлепить. Могу. Проблема в том, что количество байт, доступных для чтения (если раз в 2 мс долбить метод read или poll), на плате imx6 меняется как 0-0-0-...-0-0-0-4096-0-0-0-...-0-0-0-4096-0-0-0-...

    Абсолютно тот же самый пример из этой библиотеки на компьютере даёт примерно 20-20-30-20-30-20-40-20-30-20... Числа написаны условно. Главное то, что на компьютере данные выдаются в юзерспейс по мере поступления, а на плате - когда весь входной буфер забьётся.

    АБСОЛЮТНО ОДИНАКОВЫЙ КОД на компьютере работает правильно, а на плате - нет. На компьютере данные обрабатываются сразу, как устройство начинает их слать. На плате первые данные появляются через полсекунды и далее раз в полсекунды.
    Спуститься к концу Подняться к началу
    Персональная информация
    sasamy
    Добавлено 03.02.2020 03:52 Редактировалось 03.02.2020 05:28 Сообщение: 6
    sasamy
    4.71

    Пункты: 83540
    Регистрация: 14.08.2009
    Цитата
    Проблема в том, что количество байт, доступных для чтения (если раз в 2 мс долбить метод read или poll), на плате imx6 меняется как 0-0-0-...-0-0-0-4096-0-0-0-...-0-0-0-4096-0-0-0-...
    ...
    Главное то, что на компьютере данные выдаются в юзерспейс по мере поступления, а на плате - когда весь входной буфер забьётся.


    может я совсем не понимаю о чем вы - ничего подобного я у себя не вижу. OEM у меня нет, тест на sk-imx6q с корневой buildroot-2017.08-sk

    в rs232.c заменил дефолтный порт на ttymxc0

    Цитата

    const char *comports[RS232_PORTNR]={"/dev/ttymxc0","/dev/ttyS1", ...


    в тесте demo_rx.c поменял скорость на 115200

    Цитата

    int main()
    {
    int i, n,
    cport_nr=0, /* /dev/ttyS0 (COM1 on windows) */
    bdrate=115200; /* 9600 baud */


    и задержку на 2 мс

    Цитата

    #else
    usleep(2000); /* sleep for 100 milliSeconds */


    отключил на плате консоль на ttymxc0 и подключил его к PC через переходник USB-serial, установил скорость как наплате

    В терминале PC

    Цитата

    sasa@sasa-Q500A:~$ stty -F /dev/ttyUSB0 115200
    sasa@sasa-Q500A:~$ printf "Hello, world!" > /dev/ttyUSB0
    sasa@sasa-Q500A:~$ printf "Hello" > /dev/ttyUSB0
    sasa@sasa-Q500A:~$ printf "1234567890" > /dev/ttyUSB0


    выхлоп теста на плате

    Цитата

    # ./test_rx
    received 13 bytes: Hello, world!
    received 5 bytes: Hello
    received 10 bytes: 1234567890


    где вы тут видите 0,0, .. ,4096 ?

    Создал на PC тестовый файл

    for i in $(seq 1 4096); do printf "1" >> test.txt; done

    Закинул в порт

    cat test.txt > /dev/ttyUSB0

    Тест на плате принимает 4095 + 1 как и должно быть - читает пока есть данные в буфере порта (буфер tty в ядре 4096 байт https://www.linuxquestions.org/questions/linux-hardware-18/why-the-%27n_tty_buf_size%27-is-restricted-as-4096-a-605107/)

    Цитата

    received 4095 bytes: 1111...
    received 1 bytes: 1


    Поменял в тесте

    n = RS232_PollComport(cport_nr, buf, 100);

    результат - прием по 100 байт максимум за один раз

    Цитата

    received 100 bytes: 11111...
    received 100 bytes: 11111...
    ...
    received 96 bytes: 11111


    Цитата

    АБСОЛЮТНО ОДИНАКОВЫЙ КОД на компьютере работает правильно, а на плате - нет.


    а может наоборот ? Вам просто надо понять что это не микроконтроллер и не РТОС - нет никакой гарантии в юзерспейс что задержка 2 мс - гарантируется только то что она минимум 2 мс, а может быть и секунду, невозможно сказать сколько байт в буфере после такой задержки.

    Если откинуть в сторону Windows - в Linux есть системные вызовы poll/select - с ними не надо никаких задержек делать, выполнение текущей задачи можно приостанвить до появления данных в буфере порта, в данном примере циклические задержки нужны для совместимости с Windows. Для одного порта можно вообще блокирущее чтение использовать без всяких задержек - текущая задача уснет пока нет данных. Эта библиотека просто как пример.
    Спуститься к концу Подняться к началу
    Персональная информация
    SmartRogue
    Добавлено 03.02.2020 09:17 Сообщение: 7
    SmartRogue
    0

    Пункты: 1449
    Регистрация: 27.11.2017
    Цитата
    может я совсем не понимаю о чем вы


    Может, я непонятно объясняю. Ещё раз. Устройство шлёт данные непрерывно пакетами по 10 байт, межпакетного интервала нет. Прикладываю код и лог.

    Модифицированный пример:
    Код

    struct timeval time_val;
    int msecs;
    char time_str[10];
    char full_time_str[20];

    if(RS232_OpenComport(cport_nr, bdrate, mode, 0))
    {
    printf("Can not open comport\n");

    return(0);
    }

    while(1)
    {

    do {
    n = RS232_PollComport(cport_nr, buf, 400);

    gettimeofday(&time_val, 0);
    msecs = trunc(time_val.tv_usec / 1000.0f);

    strftime(time_str, 40, "%H:%M:%S", localtime(&time_val.tv_sec));
    sprintf(full_time_str, "%s.%03d", time_str, msecs);

    if (n > 0) {
    printf("%s: %i bytes read\n", full_time_str, n);
    } else {
    printf("%s: no data available\n", full_time_str);
    }

    } while (n > 0);

    usleep(2000); // sleep for 2 milliseconds
    }


    Фрагмент лога на компьютере (Debian 8.7):
    Код

    09:41:42.775: 20 bytes read
    09:41:42.777: 12 bytes read
    09:41:42.779: 24 bytes read
    09:41:42.781: 12 bytes read
    09:41:42.783: 16 bytes read
    09:41:42.785: 30 bytes read
    09:41:42.787: 16 bytes read
    09:41:42.790: 12 bytes read
    09:41:42.792: 10 bytes read
    09:41:42.794: 28 bytes read
    09:41:42.796: 30 bytes read
    09:41:42.798: 20 bytes read
    09:41:42.800: 12 bytes read
    09:41:42.802: 30 bytes read


    Фрагмент лога на плате (Debian 9.3):
    Код

    09:45:20.900: no data available
    09:45:20.902: no data available
    09:45:20.904: no data available
    09:45:20.906: 400 bytes read
    09:45:20.907: 400 bytes read
    09:45:20.907: 400 bytes read
    09:45:20.907: 400 bytes read
    09:45:20.907: 400 bytes read
    09:45:20.907: 400 bytes read
    09:45:20.907: 400 bytes read
    09:45:20.907: 400 bytes read
    09:45:20.907: 400 bytes read
    09:45:20.907: 400 bytes read
    09:45:20.907: 95 bytes read
    09:45:20.907: no data available
    09:45:20.909: 1 bytes read
    09:45:20.909: no data available
    09:45:20.911: no data available
    09:45:20.914: no data available
    <...>
    09:45:21.889: no data available
    09:45:21.891: no data available
    09:45:21.893: no data available
    09:45:21.895: 400 bytes read
    09:45:21.895: 400 bytes read
    09:45:21.895: 400 bytes read
    09:45:21.896: 400 bytes read
    09:45:21.896: 400 bytes read
    09:45:21.896: 400 bytes read
    09:45:21.896: 400 bytes read
    09:45:21.896: 400 bytes read
    09:45:21.896: 400 bytes read
    09:45:21.896: 400 bytes read
    09:45:21.896: 95 bytes read
    09:45:21.896: no data available
    09:45:21.898: 1 bytes read
    09:45:21.898: no data available
    09:45:21.900: no data available
    09:45:21.903: no data available


    Как видно из логов, на компьютере метод Poll каждый раз считывает некоторое небольшое количество байт, находящихся сейчас во входном буфере. На плате метод Poll возвращает нули до тех пор, пока буфер не заполнится целиком, после чего возвращает весь буфер целиком. То, по сколько байт я хочу считывать, не имеет значения. Посмотрите на временные отметки. Все 4096Б становятся доступны одновременно. Между выдачей данных стабильное время, равное времени полного заполнения буфера. Поскольку поток не блокируемый, я не понимаю, какого лешего так происходит. Я понимаю, что это не реалтайм операционка, но камооон, 10 кБ / сек невозможно обрабатывать чаще одного раза в 900 мс?..

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

    Такое ощущение, что реализация tty в дебиане на плате отчаянно пытается дождаться конца посылки данных (нет межпакетного интервала - нет конца), не может его дождаться, и потому вынужденно отдаёт данные после заполнения буфера.
    Спуститься к концу Подняться к началу
    Персональная информация
    SmartRogue
    Добавлено 03.02.2020 09:20 Сообщение: 8
    SmartRogue
    0

    Пункты: 1449
    Регистрация: 27.11.2017
    Цитата
    Для одного порта можно вообще блокирущее чтение использовать без всяких задержек


    Да, ставил VMIN = 1.

    Код спит-спит-спит-спит-спит, а потом в него разом валится 4096Б данных. Затем всё повторяется.
    Спуститься к концу Подняться к началу
    Персональная информация
    SmartRogue
    Добавлено 03.02.2020 10:41 Сообщение: 9
    SmartRogue
    0

    Пункты: 1449
    Регистрация: 27.11.2017
    Из спортивного интереса воспроизвёл эксперимент на чистом билдрутном линуксе. Результат тот же...
    Спуститься к концу Подняться к началу
    Персональная информация
    sasamy
    Добавлено 03.02.2020 13:11 Редактировалось 03.02.2020 13:12 Сообщение: 10
    sasamy
    4.71

    Пункты: 83540
    Регистрация: 14.08.2009
    Цитата

    Может, я непонятно объясняю. Ещё раз. Устройство шлёт данные непрерывно пакетами по 10 байт, межпакетного интервала нет


    это понятно - непонятно с чего вы решили что система обязана в таком случае принимать их кусками размером какой вам нужен - какая разница, 4096 байт вы будете потом парсить или 80 ?

    Цитата

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


    отключите DMA у порта и будет вам "как на компьютере"

    imx6qdl.dtsi

    для порта ttymxc0 - uart1

    Цитата

    uart1: serial@02020000 {
    compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
    reg = <0x02020000 0x4000>;
    interrupts = <0 26 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6QDL_CLK_UART_IPG>,
    <&clks IMX6QDL_CLK_UART_SERIAL>;
    clock-names = "ipg", "per";
    /*
    dmas = <&sdma 25 4 0>, <&sdma 26 4 0>;
    dma-names = "rx", "tx";
    */

    status = "disabled";
    };
    Спуститься к концу Подняться к началу
    Персональная информация
    Форум » starterkit.ru » Embedded Linux