Решено с помощью mlockall() - символы Ascii с определенным значением не читаются через последовательный порт

Я установил удаленный микроконтроллер, подключенный к моему ПК с Linux, который выдает непрерывную строку символов. Я настроил порт с помощью screen (выдав screen /dev/ttyS0 57600).

Я проверил данные с помощью команды od и получил то, что ожидал. Данные следуют как 8-битные шестнадцатеричные значения...

    0006040 03 01 09 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006100 00 00 00 00 00 00 00 00 00 00 00 06 0b a5 00 03
    0006120 01 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006160 00 00 00 00 00 00 00 00 00 00 06 0b a5 00 03 01
    0006200 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006240 00 00 00 00 00 00 00 00 00 06 0b a5 00 02 01 09
    0006260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006320 00 00 00 00 00 00 00 00 0f 0c a5 00 02 01 09 00
    0006340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006400 00 00 00 00 00 00 00 0f 0c a5 00 02 01 09 00 00
    0006420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006460 00 00 00 00 00 00 0f 0c a5 00 01 01 09 00 00 00
    0006500 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006520 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006540 00 00 00 00 00 05 0c a5 00 01 01 09 00 00 00 00
    0006560 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006600 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006620 00 00 00 00 05 0c a5 00 01 01 09 00 00 00 00 00
    0006640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006660 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006700 00 00 00 05 0c a5 01 08 01 09 00 00 00 00 00 00

Затем я создаю простую программу на C, чтобы попытаться прочитать те же данные, но все, что я получаю от своей программы на C, — это следующий вывод:

  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1

Основываясь на этом выводе, кажется, что функция чтения действительно возвращает, что считывается один байт, но каждый раз, когда возвращается значение a5, которое является флаговым байтом, микро отправляет на ПК, чтобы указать новый поток данных фиксированного размера.

Так что моя программа правильно перезаписывает существующее значение "A" в буфере чтения, но ей нужно постоянно обновлять правильные данные (будь то 00 или 01), а не только только a5

Что я делаю не так?

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

  #include <stdio.h>
  #include <stdlib.h>
  #include <fcntl.h>
  #include <time.h>
  #include <unistd.h>

  #define TIMEOUT 5

  int main(){
    char inb[3];  //our byte buffer
    int nread=0;  //number bytes read from port
    int n;        //counter
    int iosz=128; //Lets get 128 bytes
    int fd=open("/dev/ttyS0",O_NOCTTY | O_RDONLY | O_SYNC); //Open port
    for(n=0;n<iosz;n++){
      int s=time(NULL); //Start timer for 5 seconds
      while (time(NULL)-s < TIMEOUT && nread < 1){
        inb[0]='A'; //Fill buffer with bad data
        inb[1]='B';
        inb[2]='C';
        nread=read(fd,inb,1); //Read ONE byte
        if (nread < 0 || time(NULL)-s >= TIMEOUT){
            close(fd); //Exit if read error or timeout
            return -1;
        }
      }
      printf("%x:%d ",inb[0] & 0xFF,nread); //Print byte as we receive it
    }
    close(fd); //program ends so close and exit
    return 0;
  }

person Mike -- No longer here    schedule 30.06.2019    source источник


Ответы (2)


Символы Ascii под определенным значением не читаются через последовательный порт

Это фальшивый анализ недостатков вашей программы.
По какому-то стечению обстоятельств оказывается, что наибольшее полученное значение равно 0xA5, а небольшой образец дампа всегда предшествует этому байту либо с 0x0B, либо с 0x0C.
Если дамп должен был показать больше данных, будет ли в конечном итоге 0x0D (возврат каретки), предшествующий байту 0xA5?

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

Что я делаю не так?

В вашей программе есть две существенные ошибки.

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

Во-вторых, ваша программа многократно выводит одни и те же результаты одного read().
Ваш анализ того, что считываются только определенные значения, неточен, потому что только результат одного read() фактически печатается.
Функция printf() выходит за рамки цикла while (который выполняет чтение), поэтому ваша программа не выводит то, что получил каждый read(), а выводит только то, что получил первый успешный read().
После этого первый успешный read(), переменная nread имеет значение 1, а сравнение nread < 1 в цикле while всегда будет возвращать FALSE.
Следовательно, не более read() будут выполняться после первого успешного, но результаты первого будут повторно напечатаны из-за цикла for.

Таким образом, если неканонический режим активен, когда вы запускаете свою программу, и '0xA5` оказывается первым read(), то это то, что будет напечатано (много раз) вашей программой. .


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

person sawdust    schedule 01.07.2019

Наконец-то я понял это!

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

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

Что буквально решило ситуацию, так это два пункта:

  1. Мне нужно было сделать эту программу высокоприоритетной, так как последовательная синхронизация строгая (1 символ каждые 173 мкс при 56k). Для этого я добавил это в начале основной функции:

    const struct sched_param schedp = {99}; sched_setscheduler(0,SCHED_FIFO,&schedp);

и я также добавил это в включает:

#include <sched.h>

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

    mlockall(MCL_CURRENT);  
    //use read() or write() in here with file descriptor as serial port
    munlockall();

и я также добавил это в включает:

#include <sys/mman.h>

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

И меня не волнует, что остальные процессы в моей системе блокируются на 5 секунд, но, по крайней мере, теперь ввод-вывод данных работает на 100% лучше на последовательном порту.

person Mike -- No longer here    schedule 02.07.2019
comment
Это предполагаемое решение является ошибочным и неправильным использованием mlockall(). . Скорость всего 56 КБ не должна быть проблемой для системы, которая также может поддерживать Linux; например Скорость 115200 бод распространена во многих Linux SBC. Кроме того, прием последовательных данных обрабатывается аппаратным обеспечением и драйвером устройства, а затем буферизуется. Повышение приоритета пользовательского пространства во время выполнения и блокировка страниц никак не влияют на прием данных. Функция read() вашей программы просто извлекает байты из системного буфера и не обращается напрямую к какому-либо последовательному оборудованию. - person sawdust; 03.07.2019