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

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


person Nick Sonneveld    schedule 16.11.2009    source источник


Ответы (4)


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

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

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

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

так что рутина как псевдокод

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

look at actual # of received bytes

if less than a packet, has issues, discard.

не хорошо.

person Tim Williscroft    schedule 16.11.2009

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

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

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

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

person npat    schedule 12.05.2010
comment
+1 за невероятную глупость метода кадрирования MODBUS. - person leonbloy; 07.01.2011

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

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

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

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

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

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

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

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

person Timmy_A    schedule 05.09.2016

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

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

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

person Morinar    schedule 17.11.2009
comment
VTIME - это максимальное время для блокировки после вызова read (), верно? Я искал способ узнать время ввода символов в UART. Я предполагаю, что тайм-аут в 1,5 символа (скажем, 9600) мог уже пройти до того, как я вызову read (). - person Nick Sonneveld; 18.11.2009
comment
Поначалу я тоже так думал, но на самом деле пришло время подождать между персонажами, прежде чем вернуться. Таким образом, в сценарии VMIN = 0, VTIME = 15 он будет блокироваться в read () навсегда, пока не появится символ. Затем он будет продолжать блокироваться там, пока не пройдет 1,5 секунды без символа. Если он получает другие символы, 1.5s начинается заново. - Все больше и больше походить на то, что это не совсем твоя проблема, просто звучало похоже на проблему, которая возникла у меня недавно. - person Morinar; 18.11.2009
comment
Разрешение в десятые доли секунды на самом деле не сокращает его, когда время для персонажа может быть порядка микросекунд. - person Graeme; 10.12.2015
comment
При 9600 бит / с, с 11 битами на символ (может быть 10 в зависимости от реализации) мы имеем t1,5 (межсимвольный тайм-аут) = 0,0017193 секунды. VTIME здесь не поможет. Cfr. Протокол последовательной линии Modbus и руководство по внедрению, версия 1.02. - person Ludovic Kuty; 04.09.2020