Последовательное программирование:измерение времени между символами

StackOverflow https://stackoverflow.com/questions/1739826

  •  20-09-2019
  •  | 
  •  

Вопрос

Я отправляю / получаю данные по последовательной линии в 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, но я хочу использовать это в других приложениях)

Это было полезно?

Решение

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

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

IT является возможно указать устройству uart время ожидания и выдать принятое прерывание после нескольких повторений символов.

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

таким образом, процедура выполняется в виде псевдокода

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

look at actual # of received bytes

if less than a packet, has issues, discard.

не очень.

Другие советы

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

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

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

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

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

По сути, вы захотите использовать 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

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

Вы не можете использовать тайм-ауты.При более высоких скоростях передачи данных тайм-аут в 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, это лучшее, что я смог придумать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top