Введение

Синхронный прием данных программой LabVIEW через линию USB/СОМ работает достаточно хорошо, если принимаемые данные не содержат периодических фрагментов.

При обнаружении таких фрагментов LabVIEW вставляет «шумовые» байты и тем самым искажает принимаемый сигнал.

В этой работе рассмотрена причина появления шумов и вариант обеспечения «бесшумового» приема COM данных.

Состояние проблемы

На Рисунок 1 показана схема LabVIEW интерфейса, который каждый 10 мс такт посылает в СОМ порт запрос (один байт ASCII = 1). На каждый запрос контроллер высылает 6 байт (2 байта от одного датчика и 4 байта от другого). Соответственно, интерфейс должен принимать 6 байт контроллера. В основном, так и происходит, но в отдельные моменты количество принимаемых байт не равно и не кратно 6, в эти же моменты принимаемые сигналы имеют выбросы — искажения, например, как показано на Рисунок 2.

Рисунок 1. Блок схема LabVIEW интерфейса, принимающего данные СОМ порта.

Рисунок 2. Примеры отклонения количества получаемых байт за 1 такт (слева) и искажения принимаемого синусоидального сигнала датчика (в центре и справа).

Вариант разложения амплитуд зашумленного 4-х байтового синусоидального сигнала (Рисунок 3) на байты показан в Таблица 1. Разложение на байты выполнялось командой MATLAB flip(typecast(int32(амплитуда_сигнала),'uint8')).

Рисунок 3. Зашумленный 4-х байтовый синусоидальный сигнал.    

Таблица 1. Разложение амплитуд зашумленного и эталонного 4-х байтного синусоидального сигнала.

Пики появляются в моменты, когда LabVIEW принимает 7 байт вместо шести. Первый пик добавляет в сигнальную последовательность два нулевых байта и заменяет сигнальные байты 0/10 на 13/144, а байты 0/9/197/0 на 3/192. Второй пик также добавляет в сигнальную последовательность два нулевых байта, заменяет сигнальные байты 0/10 на 3/192 и заменяет байты 0/11/56/0 на 13/144.

Результаты тестирования “зашумлений”

В процессе экспериментального тестирования выявлено следующее. 

  1. LabVIEW добавляет “заголовок” (свои байты) если последовательность данных носит периодический характер, например, передаются два синусоидальных сигнала с одинаковыми периодами в формате int16 (2 байта) и int32 (4 байта).

  2. При наличии заголовков теряется часть сигнальных данных. 

  3. Заголовок устанавливается автоматически вначале повторяющейся цепочки данных.

  4. Повторяющиеся цепочки данных не обязательно следуют друг за другом. Они могут идти через промежутки (блоки байт).

  5. Для синусоидального сигнала в формате int16 заголовок состоит из двух слов.

  6. Содержимое заголовка зависит от периода сигнала.

  7. Моменты появления заголовка можно обнаружить по дополнительным байтам в буфере СОМ порта.

  8. Заголовок может включать, например, два байта: 10 (перевод строки) и 9 (горизонтальная табуляция) или 10 (перевод строки) и 11 (вертикальная табуляция) или другие байты, как показано в Таблица 1.

  9. Замена LabVIEW запроса 1 (ASCII код 49) (см. Рисунок 1) на D (ASCII код 68) не устраняет генерацию заголовков.

  10. Применение синхронного или асинхронного режима приема передачи (I/O mode) блоков VISA Write и VISA Read также не устраняет генерацию заголовков.

  11. LabVIEW не создает заголовков если не находит повторяющуюся последовательность. Для этого в примере последовательной передачи данных блоками по 6 байт достаточно второй синусоидальный сигнал заменить неизменным уровнем (константой).

Решение проблемы искажения периодических сигналов

LabVIEW не создает заголовки и не «зашумляет» принимаемые через СОМ порт данные, имеющие периодические фрагменты, если данные принимаются через «очередь», образованную блоками Queues LabVIEW (см. Рисунок 4), хотя и в этом случае количество принимаемых байт на каждом такте может изменяться.

Пример блок схемы LabVIEW, принимающей данные контроллера через очередь, приведен на Рисунок 5. Цикл While: Enqueue Loop отправляет запрос (ASCII код 1) в СОМ порт, принимает байты сигнальных данных и передает их в очередь. Цикл While: Dequeue Loop достает сигнальные байты из очереди, формирует из них два int16 сигнала. Цикл While: Queue Status отображает количество байт в очереди. Максимальный размер очереди в этом примере 300 байт.

Рисунок 4. Прием периодических показаний датчиков контроллера по запросу через “очередь - Queue” LabVIEW: количество принимаемых байт (слева) и принимаемый синусоидальный сигнал (справа). Данные не содержат искажений.

Рисунок 5. Пример приема данных контроллера по запросу через очередь LabVIEW.  

Комментарии (16)


  1. Chupakabra303
    24.10.2023 08:23
    +3

    У Вас используется протокол с фиксированным размером блока и разделением передачи по времени. По-хорошему вам нужно ждать пакет заданного размера в течении гарантированного времени, т.е. ввести тайм-аут на ожидание приема определенного кол-ва байт. Если не дожидаетесь приема ваших 6 байт за это время, считаете, что все что приняли - "битый" пакет и отбрасываете. Дальше выясняете, почему процент битых пакетов выше ожидаемого. Может быть плохой контакт, длинный неэкранированный провод рядом с источником помех, ошибка в программе контроллера. Еще хорошо бы ввести контрольную сумму в пакет, чтобы гарантировать корректность содержимого, а не только количества байт. Смотрите как устроен modbus rtu.

    Я в свое время написал простой протокол, в котором отказался от таймаутов и перешел на квитирование пакета специальным символом и вставку спец. байтов (byte stuffing), что упрощает общую реализацию. Есть реализация мастера для LabVIEW и Python, слейва для arduino / teensy. https://github.com/Chupakabra303/SerialFrameProtocol


  1. NutsUnderline
    24.10.2023 08:23

    стрим c COMпорта какой нибуть другой программой мониторился/записывался ? А то окажется что это источник данных мудрит. Похоже на какой то байт-стаффинг и в любом случае это не шум, а в общем то сбой, аномалия


  1. AndreyDmitriev
    24.10.2023 08:23
    +3

    Я обычно говорю в таких случаях, что любому наблюдаемому феномену есть рациональное объяснение и чудес на свете не бывает. Сколько я с RS232 не работал, вот ни разу не встречал ситуации, когда просто так появлялись бы "мусорные" байты из ниоткуда. Думаю, что их железка шлёт, ну или помехи на линии. Хардкорный метод - подцепиться на Tx Rx осциллографом с памятью прямо у порта, и записать и проанализировать все сигналы, которые пролетают через интерфейс.

    Вы несколько нетипично используете комбинацию Wait (ms) вкупе с Wait Until Next ms Multiple c довольно близкими значениями задержек (8 и 10 мс). Это может приводить к тому, что время между чтением и записью будет "плавать", возможно железка ещё не готова принять байт - команду на чтение и реагирует на это дополнительным "мусорным" байтом. Возможно "двухцикловый" дизайн с очередью чуть меняет тайминги и эффект пропадает. Теоретически можно Timed Loop попробовать, хотя там можно налететь на другие "подводные камни".

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


  1. Borjomy
    24.10.2023 08:23

    Функция "Bytes as Port" вредная команда. Что-то вроде goto в C. Есть еще один нюанс к вышесказанному. Микросхема 18550 последовательного порта имеет буферы на прием и передачу (16 и 14 байт). Тонкость в том, что если количество принятых байт меньше размера буфера, то драйвер получает событие через 55 миллисекунд по таймауту. Отсюда задержки. Возможно, в современных Windows задержки уменьшили, но в Win7 было 55мс. Решение: уменьшить размеры буферов до 1. Тогда принятый символ сразу будет передаваться в буфер драйвера. А уже он подсчитывает количество принятых байт при вызове функции VISA Read


  1. dr_bob_davidov Автор
    24.10.2023 08:23
    +1

    "... стрим c COM порта" проверен. Например, как сказано в статье, если синусоидальные показания одного из датчиков заменить константой (пропадает периодичность суммарного сигнала), то шумы (или аномалия) исчезают, сигнал другого датчика отображается чистым синусом и LabVIEW показывает на мониторе, что принимает каждый такт только 6 байт. Чтобы выделить проблему приема периодических фрагментов в LabVIEW (повторяемости аномалии), вместо показаний датчиков, контроллер (по запросу LabVIEW) выдавал два эталонных синусоидальных сигнала (int16 и int32).


    1. NutsUnderline
      24.10.2023 08:23

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


    1. AndreyDmitriev
      24.10.2023 08:23
      +2

      Да не, я много всякой "мистики" видел, но тут явно дело не в периодичности сигнала.

      LabVIEW добавляет “заголовок” (свои байты) если последовательность данных носит периодический характер

      Я вот смотрю - проблема возникает, когда вам прилетает байт 10. По умолчанию это терминирующий байт и он включён. Попробуйте выключить этот режим при открытии порта:

      Я не уверен, что это причина такого поведения, но я абсолютно уверен, что если я возьму ардуинку и начну генерить синус в COM порт, и принимать его в LabVIEW, то ничего лишнего там вставляться не будет.


      1. dr_bob_davidov Автор
        24.10.2023 08:23
        +2

        Вы правы. "Termination = F" решило проблему. Большое спасибо за дельный совет.


        1. NutsUnderline
          24.10.2023 08:23

          неплохо бы добавить статью счастливым эпилогом


        1. dr_bob_davidov Автор
          24.10.2023 08:23

          Согласны ли Вы со следующими рассуждениями?

          При наличии

          1.      терминала (устанавливается по дефолту) в модуле “VISA Configure Serial Port VI”;

          2.      в принимаемой последовательности байтов, равных терминалу;

          3.      и повторяющихся фрагментов данных

          LabVIEW может искажать принимаемую последовательность, например, как показано в таблице 1.


          1. AndreyDmitriev
            24.10.2023 08:23

            Пункт 3 надо заменить на "и наличии железки, не поддерживающей терминирующий символ".

            Вообще учебник пишет, что "Keep in mind that termination on the physical hardware needs to comply with the appropriate Serial standard. If the termination on the physical component is incorrect then it will cause issues with termination characters on the software side".

            Так что там не всё так просто. У меня не было проблем с терминирующим байтом, но там это было заложено в самих железках (сканер штрих кодов обычно так работает, но там никогда не будет этого байта в нормальных данных, поскольку там просто ASCII текст штрих-кода прилетает, а не двоичные данные).

            Ну и опять же "When performing binary communication, the read can terminate prematurely if one binary data value has the same binary representation as the termination character. Therefore, disable the termination character by setting Termination Character Enabled to false and Serial End Modes for Reads to None (0), as shown below. You must rely on a different method of terminating the read, such as a hardware line or byte count".

            Тот факт, что вы таки получаете терминируюий символ в данных и размер пакета увеличивается с 6 до 7, дополняясь двумя нулями - любопытен (я бы ожидал, что LabVIEW проглотит байт 10 и не отдаст его наружу).

            Но это утверждение легко проверить - собирается тестовый стенд, пересылаются байты в последовательный порт и смотрится, что там получается и так и сяк, заодно с ASRL End In/Out поиграть.


            1. dr_bob_davidov Автор
              24.10.2023 08:23

              С проблемой СЛУЧАЙНЫХ выбросов сигнала столкнулся, когда LabVIEW принимал от реальных датчиков зашумленные (не идеальные) синусоидальные сигналы (отображающие дыхание) с переменными амплитудами, периодами и паузами .

              На стенде с платформой Teensy 4.1 и смоделированными (вычислялись контроллером) идеальными синусоидальными сигналами получил повторяющийся (не случайный) описанный в статье эффект. Эффект с этими сигналами в точности повторяется и с Arduino UNO.

              Помимо отключения терминала, решением (устранением выбросов) является и использование очереди при взаимодействии LabVIEW как c Teensy 4.1 так и с Arduino UNO. Об этом сказано в статье.


              1. dr_bob_davidov Автор
                24.10.2023 08:23

                Дополнение. При наличии терминирующего байта эффект "выбросов" пропадает, если сигнал не периодический или не содержит повторяющихся фрагментов.


  1. dr_bob_davidov Автор
    24.10.2023 08:23

    "... дополнительным "мусорным" байтом." Дополнительный байт и "выбросы" создаются в одни и те же моменты по отношению к ПЕРИОДУ сигнала, как показано на графиках. Преобразования, показанные в таблице, также привязаны к ПЕРИОДУ сигнала, и неизменно повторяются каждый период.


  1. alcotel
    24.10.2023 08:23
    +1

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

    Всякое бывает - и usb/com глючит (особенно на чипах СРхххх), и ось в данные свои правки вставляет (особенно в CR/LF, 13/10), и таймауты меньше 10 мс может не выдерживать ни ос, ни usb/com. Ну а в софте 99% хабра умеют профессионально лажать. И я тоже)

    Исходные сырые данные - это святое! Не надо их "устранять" - можно пропустить что-то важное.


    1. NutsUnderline
      24.10.2023 08:23

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