Последовательное программирование: измерение времени между символами - PullRequest
2 голосов
/ 16 ноября 2009

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

Modbus использует задержку в 3,5 символа для определения границ кадра сообщения. Если задержка превышает 1,5 символа, кадр сообщения объявляется неполным.

Я пишу быструю программу на C, которая в основном

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
// setup newtio
....
tcsetattr(fd, TCSANOW, &newtio);
for(;;) {
    res = read(fs, buf, 1);
    if (res > 0) {
        // store time in milliseconds?
        //do stuff
    }
}

Есть ли способ измерения времени здесь? Или мне нужно посмотреть на получение данных из последовательной линии по-другому?

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

(да, я знаю, что существуют некоторые библиотеки Modbus, но я хочу использовать это в других приложениях)

Ответы [ 4 ]

4 голосов
/ 12 мая 2010

Простой ответ ... Вы не можете (не без написания собственного серийного драйвера)!

Если вы пишете MODBUS master , есть некоторая надежда: вы можете обнаружить конец ответа подчиненного устройства, ожидая любой промежуток времени (при условии, что он длиннее 3,5 символов) ) без получения чего-либо (выберите (2) может помочь вам здесь), или путем анализа ответа на лету, как вы читаете его (второй метод тратит гораздо меньше времени). Вы также должны быть осторожны, ожидая не менее 3,5 символов времени, прежде чем начинать передачу нового запроса, после получения ответа на предыдущий запрос. По крайней мере, здесь действует! Ожидание большего значения не имеет значения. Ожидание меньше делает.

Если вы пишете MODBUS раб , то вам не повезло . Вы просто не можете сделать это надежно из пользовательского пространства Linux. Вы должны написать свой собственный серийный драйвер.

Кстати, это не вина Linux. Это связано с невероятной глупостью метода кадрирования MODBUS.

3 голосов
/ 16 ноября 2009

MODbus похож на множество старых протоколов и действительно ненавидит современное оборудование.

Причина, по которой вы получаете 8 байтов за раз: Ваш компьютер имеет (как минимум) 16-байтовый последовательный FIFO при приеме и передаче в аппаратном обеспечении. Большинство из них 64 байта или больше.

В можно сообщить устройству uart, что время ожидания истекло, и выдает полученное прерывание через несколько раз.

Уровень триггера регулируется, но низкоуровневый драйвер устанавливает его «умно». попробуйте режим с низкой задержкой, используя setserial) Вы можете возиться с кодом в последовательном драйвере, если необходимо. Google это (предупреждение о зрелом содержании) это не красиво.

так что процедура выглядит как псевдокод

int actual=read (packet, timeout of 1.5 chars)

look at actual # of received bytes

if less than a packet, has issues, discard.

не отлично.

1 голос
/ 05 сентября 2016

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

На стороне клиента это не имеет большого значения, поскольку Modbus не отправляет асинхронные сообщения. Поэтому вы не должны отправлять 2 последовательных сообщения в течение тайм-аута в 3,5 символа.

На стороне сервера проблема заключается в том, что если у ваших клиентов очень короткие тайм-ауты и Linux слишком занят, вы не можете написать пуленепробиваемое решение для кадрирования. Есть вероятность, что функция read () вернет более одного пакета. Вот (немного надуманный) пример.

  1. Клиент записывает пакет на сервер. Тайм-аут, скажем, 20 мс.

  2. Скажем, в данный момент Linux очень занят, поэтому ядро ​​не пробуждает ваш поток в течение следующих 50 мс.

  3. Через 20 мс клиент обнаруживает, что не получил никакого ответа, поэтому отправляет другой пакет на сервер (возможно, повторно отправляет предыдущий).

  4. Если Linux пробуждает ваш поток чтения через 50 мс, функция read () может получить 2 пакета или даже 1 с половиной в зависимости от того, сколько байтов было получено драйвером последовательного порта.

В моей реализации я использую простой метод, который пытается анализировать байты на лету - сначала обнаруживая код функции, а затем я пытаюсь прочитать все оставшиеся байты для определенной функции. Если я получаю полтора пакета, я анализирую только первый, а оставшиеся байты остаются в буфере. Если в короткий промежуток времени приходит больше байтов, я добавляю их и пытаюсь проанализировать, иначе я их отбрасываю. Это не идеальное решение (например, некоторые субкоды для функции 8 не имеют фиксированного размера), но, поскольку MODBUS RTU не имеет символов STX ETX, это лучший из тех, которые мне удалось выяснить.

1 голос
/ 18 ноября 2009

Я думаю, вы поступаете неправильно. Существует встроенный механизм, обеспечивающий объединение персонажей.

По сути, вы захотите использовать ioctl() и соответственно установить параметры VMIN и VTIME. В этом случае кажется, что вы бы хотели, чтобы VMIN (минимальное количество символов в пакете) составляло 0 и VTIME (минимальное количество времени между символами составляло 15 (это десятые секунд).

Некоторые действительно базовый пример кода:

struct termio t;
t.c_cc[ VMIN ] = 0;
t.c_cc[ VTIME ] = 15;
if (ioctl( fd, TCSETAW, &t ) == -1)
{
    printf( msg, "ioctl(set) failed on port %s. Exiting...", yourPort);
    exit( 1 );
}

Сделайте это до вашего open(), но до вашего read(). Вот пара ссылок, которые я нашел чрезвычайно полезными:

Руководство по последовательному программированию

Понимание VMIN и VMAX

Я надеюсь, что, по крайней мере, поможет / направит вас в правильном направлении, даже если это не идеальный ответ на ваш вопрос.

...