Протокол XMODEM был разработан довольно давно, в 1977 году (автор Ward Christensen), для предоставления возможности обмениваться данными двум компьютерам через модем (обмен типа точка-точка). В протоколе применен полудуплексный принцип обмена данными, блоками по 128 байт, с кодами квитирования ACK/NAK и проверкой данных с помощью CRC. Благодаря простоте и открытой документации протокол XMODEM нашел широкое применение во многих приложениях. Фактически большинство коммуникационных пакетов, доступных для PC, имеют встроенную поддержку передачи данных через XMODEM.
Из-за своей простоты XMODEM удобно использовать на встраиваемых системах [3], где всегда имеется дефицит по вычислительным ресурсам и памяти. Хорошо получаются загрузчики кода [4], позволяющие перепрошивать программу системы по последовательному каналу связи с помощью готовых утилит терминала. Полудуплексный принцип работы позволяет использовать простейшие физические каналы связи. Например, передатчик и приемник может соединять единственный сигнальный провод (либо это может быть радиоканал на общей частоте для передатчика и приемника), который переключается с приема на передачу, потому что протокол XMODEM не подразумевает одновременной передачи на обоих концах канала связи.
[Немного теории]
XMODEM является полудуплексным (half-duplex) протоколом обмена данными. Протокол не подходит для канала связи, где элементарная посылка состоит менее чем из 8 бит. Приемник после приема пакета либо подтвердит (acknowledge, ACK), либо не подтвердит его (not acknowledge, NAK). Улучшенный по сравнению с оригинальной реализацией XMODEM (которая использовала 1 байт CRC) подсчет контрольной суммы CRC использует более надежную 16-разрядную CRC для проверки целостности принятого блока данных. Протокол XMODEM можно считать протоколом, управляемым со стороны приемника, потому что передатчик не обязан автоматически запускать повторные передачи без участия приемника. Однако все-таки во многих реализациях протокола передатчик самостоятельно запускает передачу пакетов при отсутствии активности приемника.
Кратко обмен данными можно описать следующим образом. Приемник начинает обмен предварительными приглашающими посылками символа NAK или "C" в сторону передатчика, чтобы показать свою готовность к приему данных. После этого передатчик посылает 132-байтный (если посылалось приглашение NAK) или 133-байтный пакет (если посылалось приглашение в виде символа 'C'). Затем приемник проверяет пакет и отвечает кодом ACK или NAK, и в зависимости от этого передатчик либо посылает следующий пакет (если от приемника было подтверждение ACK), либо повторно посылает последний пакет (если от приемника пришел код NAK). Этот процесс продолжается до тех пор, пока приемник не получит код EOT, после чего приемник должен подтвердить завершение приема файла посылкой кода ACK в сторону передатчика. После начальной установки связи (initial handshake) приемник управляет потоком данных с помощью отправки передатчику кодов ACK и NAK.
В таблице представлены управляющие коды (символы) протокола XMODEM.
Таблица 1.1. Специальные символы протокола XMODEM.
Символ
Значение
Описание
SOH
0x01
Start of Header, начало заголовка.
EOT
0x04
End of Transmission, конец передачи.
ACK
0x06
Acknowledge, положительное подтверждение.
NAK
0x15
Not Acknowledge, отрицательное подтверждение.
ETB
0x17
End of Transmission Block, конец передачи блока.
CAN
0x18
Cancel, отмена обмена. Этот символ, посылаемый передатчиком, принуждает приемник отправлять символы 'C' или NAK.
C
0x43
Код ASCII 'C'.
ctrl/z
0x1A
Маркер конца файла. Этим кодом заполняется пустое пространство последнего передаваемого блока, дополняя его по размеру до 128 байт.
Передаваемые полезные данные (например файл) могут быть любой длины, однако они передаются блоками жесткого размера, по 128 байт, и протокол не поддерживает передачу точного размера пересылаемого блока данных (файла). Если содержимое данных не укладываются точно в 128-байтную границу по размеру, то необязательно, но желательно, чтобы остальная часть данных была заполнена так называемыми символами ctl/z (код ASCII 0x1A) конца файла (EOF, End of File). Последний передаваемый блок не отличается от всех остальных, таким образом нет "коротких блоков". Некоторые утилиты или программы пользователя не обрабатывают поддержку окончания файла без наличия символов ctrl/z.
Формат пакета XMODEM с поддержкой CRC8 (оригинальный XMODEM):
Байт 1
Байт 2
Байт 3
Байты 4..131
Байт 132
Start of Header (SOH, начало заголовка)
Номер пакета
Номер пакета (дополнение)
Данные пакета
8-bit CRC
Формат пакета XMODEM с поддержкой CRC16 (XmodemCRC):
Байт 1
Байт 2
Байт 3
Байты 4..131
Байты 132, 133
Start of Header (SOH, начало заголовка)
Номер пакета
Номер пакета (дополнение)
Данные пакета
16-bit CRC
Передатчик определит тип используемого протокола (с CRC8 или с CRC16) по байту приглашения, который отправляет приемник. Если приемник передает байт приглашения NAK, то используется пакет с CRC8, если же приемник передает байт приглашения в виде символа 'C', то это означает использование пакета с CRC16.
Байт 1 пакета передатчика может иметь значения SOH, EOT, CAN или ETB, и никакие другие, иначе произойдет ошибка. Байты 2 и 3 вместе формируют номер пакета с проверкой, так что сумма этих двух байт друг с другом всегда должна быть равна 0xff. Имейте в виду, что номер пакета начинает отсчитываться с 1, и если количество пакетов превысит 255, то номер пакета переваливает через 0, и отсчет продолжается - таким образом пакетов может быть любое количество, большее 255. Байты 4..131 формируют пакет данных, и здесь могут быть любые двоичные данные. Байты 132 и 133 формируют 16-битную CRC. Старший байт CRC размещается в байте 132, младший в байте 133. Контрольная сумма вычисляется только от данных пакета (байты 4..131).
Синхронизация между приемником и передатчиком. Как уже упоминалось, приемник начинает сессию обмена с периодических (с интервалом примерно 3 сек) посылок передатчику символа ASCII “C” (код 0x43), чем показывая передатчику, что ожидается отправка блока с поддержкой проверки алгоритмом CRC. После отправки символа “C” ждет либо истечения таймаута 3 секунды, либо появления в приемном буфере символа от передатчика. При появлении символа он записывается во внутренний буфер, и таймаут сбрасывается. Если символ от передатчика не поступил и таймаут истек, то приемник снова посылает другой символ “C” передатчику, и снова ждет 3 секунды. Этот процесс продолжается, пока приемник не примет полный пакет из 133 байт.
Алгоритм работы приемника. Приемник отслеживает 10-секундный таймаут. Он посылает символ NAK каждый раз, когда интервал таймаута завершился. Первый таймаут приемника приводит к посылке NAK, что сигнализирует передатчику, что можно начать передачу. Опционально приемник может сразу отправить NAK, если передатчик готов к приему, это могло бы экономить 10 секунд времени на начальном таймауте. Однако приемник ДОЛЖЕН продолжить отсчеты таймаута каждые 10 секунд - в случае, если передатчик пока не готов.
Как только приемник вошел в прием блока, для каждого принимаемого символа и контрольной суммы используется таймаут в 1 секунду. Если приемник хотел бы послать NAK по какой-то любой причине (неправильный заголовок, ошибка фрейма, истек таймаут в процессе приема данных), он должен ожидать чистой линии на входе (когда передатчик ничего не передает в течение 1 секунды). Подробнее см. ниже "Советы по программированию".
Обычно (когда передатчик, приемник и канал связи исправны) в протоколе XMODEM коды NAK используются для следующих ситуаций:
1. Ошибка фрейма (Frame error) при приеме любого байта. 2. Переполнение буфера приема (Overrun error) для любого принятого байта. 3. Повторная отправка пакета. 4. Ошибка CRC. 5. Таймаут приемника (не было приема пакета в течение 1 секунды).
При получении любого NAK передатчик заново отправит последний пакет. Ситуации 1 и 2 могут произойти из-за серьезных аппаратных проблем, в этом случае следует проверить соответствие настроек последовательного порта передатчика и приемника (должны быть одинаковые baud rate, количество start bit и stop bit). Ситуация 3 может произойти, когда передатчик ошибочно принял ACK (не принял его), и по этой причине не отправляет новый пакет. В этом случае приемник должен выдать ACK повторно. Также не исключена ситуация, когда передатчик высылает тот же пакет повторно. Приемник может проверить это по изменению номера пакета - если номер пакета тот же самый, то приемник должен просто выдать ACK, после чего отбросить принятые данные пакета. Ситуация 4 может произойти в условиях повышенных помех в линии связи. Последняя проблема 5 должна исправиться сама, после того как приемник передаст код NAK в сторону передатчика. В случае приема блока с ошибкой вызывается специальная процедура очистки приема (см. Советы по программированию далее).
Все ошибки в канале связи исправляются 10-ю повторными попытками передачи блока. Если у принятого блока не те атрибуты, которые ожидались, то приемник посылает NAK, и увеличивает свой счетчик повторных попыток. После того, как было 10 неудачных повторов, сеанс связи считается завершенным из-за потери синхронизации.
Если был принят допустимый номер блока, то это может означать следующее:
1. Этот блок ожидался, в этом случае все в порядке. 2. Это копия ранее принятого блока (номер блока совпал с предыдущим). В этом случае можно считать, что тоже все в порядке, просто показывает, что передатчик по каким-то причинам передатчик принял поврежденный символ ACK, и передал блок заново. 3. Какой-то другой номер блока. Эта ситуация показывает фатальную потерю синхронизации, как например если бы у передатчика были сильные помехи на входе, которые были ошибочно восприняты как символы ACK. В этом случае следует прервать передачу отправкой CAN, после чего запустить сеанс связи повторно.
Алгоритм работы передатчика. Когда передатчик ждет начала передачи, у него есть только один очень большой таймаут, скажем в 1 минуту. Однако во многих текущих реализациях протокола после начала сеанса связи у передатчика есть 10-секундный таймаут, после которого передатчик предпримет повторную передачу блока. Однако разработчик протокола Ward Christensen не рекомендует так делать, оставляя протокол на управление только со стороны приемника. Это сохранит совместимость с уже существующими программами.
Когда у передатчика больше нет данных для отправки, он посылает EOT, ждет ACK, и снова посылает EOT, если ACK не получен. Опять-таки, протокол и в этом случае можно оставить на управление со стороны приемника, и сделать для передатчика единственный таймаут в 1 минуту, после которого передатчик может разорвать сеанс связи.
[Пример сеанса связи]
В таблице для иллюстрации принципа работы протокола XMODEM показан примерный сеанс связи с восстановлением из ситуации с ошибками. Вся передача файла в этом примере состоит из 5 блоков, некоторые из которых передаются повторно.
Некоторые версии протокола используют специальный символ CAN, код ASCII ctrl/x (см. таблицу 1.1), для отмены передачи. Эта опция не вошла в стандартную версию протокола, потому что наличие в протоколе одиночного символа обрыва передачи повышает вероятность ошибочного обрыва передачи из-за случайного превращения символов ACK, NAK или SOH в символ CAN.
[Советы по программированию]
Подпрограмма приема символа должна вызываться с параметром, где указывается таймаут в секундах, в течение которого можно ждать приема символа. Первый вызов этой подпрограммы должен быть сделан с временем 10 секунд, так что после истечения 10 секунд таймаута должен быть отправлен символ NAK, и так должно быть сделано 10 раз. После приема SOH (это первый символ, который передан передатчиком) приемник должен вызывать подпрограмму приема символа с таймаутом в 1 секунду, и так до всей оставшейся части сообщения, включая контрольную сумму CRC. Поскольку данные будут передаваться как непрерывный поток байт, то истечение таймаута 1 сек будет означать серьезную ошибку - скажем, принято 127 символов вместо 128.
Когда приемник хочет передать NAK, он должен вызвать "очищающую" подпрограмму (PURGE), в которой будет реализован прием с 1-секундным таймаутом, и зацикливание, пока таймаут не истечет. Эта процедура в сущности заключается в ожидании "чистой" линии связи на входе, когда передатчик не передает, что будет гарантировать, что передатчик правильно примет и обработает символ NAK. Вспомните, что протокол рассчитан на обмен в полудуплексе, т. е. передатчик на время своей передачи блокирует работу своего входного канала, что гарантирует отсутствие ошибочной интерпретации помех, которые могли бы возникнуть во входном канале передатчика в процессе передачи.
Желательно добавить код для обработки специальных ошибок приема, таких как ошибка фрейма UART, или переполнение входного буфера. Двухбайтная контрольная сумма (CRC16) лучше защищает данные, чем однобайтная (CRC8), потому что, к примеру, CRC8 дает для байт 0x80 и 0x80 тот же результат, что и для байт 0x00 и 0x00.
[Пример кода, вычисляющего CRC8]
Это простейший алгоритм, который просто считает сумму всех байт (нужно вычислять сумму от 128 байт данных пакета, байты 4..131) с отбрасыванием переполнения. Получится байт суммы, который должен совпадать с байтом 132 пакета.
u8 crc8(u8 *buf, int sz)
{
u8 crc =0;
for (int i=0; i < sz; i++)
crc += buf[i];
return crc;
}
[Пример кода, вычисляющего CRC16]
intcalcrc(char*ptr, int count)
{
int crc;
char i;
crc =0;
while (--count >=0)
{
crc = crc ^ (int) *ptr++<<8;
i =8;
do
{
if (crc &0x8000)
crc = crc <<1^0x1021;
else
crc = crc <<1;
} while(--i);
}
return (crc);
}
Подпрограмма вернет 16-битное значение контрольной суммы, где старший байт числа должен быть помещен в 132 байт буфера XMODEM (если считать по индексу от 0, то в 131 байт буфера), а младший байт должен быть помещен в 133 байт буфера XMODEM (если считать по индексу от 0, то в 132 байт буфера). Пример использования программы для подсчета CRC16:
Я правильно понимаю, что 4-й пакет потерян? Из-за двойного АСК после третьего пакета. Ещё: "Пример кода, вычисляющего CRC8" не вычисляет CRC8. Он вычисляет контрольную сумму символов, а CRC это сложнее... Вот CRC16 вычисляется как нужно.
Важное замечание: нельзя отправлять ACK больше одного раза (в таблице две отправки)! Если первый раз отправка не получилась, то все следующие разы стоит отправлять NACK. Эта проблема всплыла при кратковременном отключении линии RX устройства. Устройство попыталось отправить ACK второй раз, а прошивальщик словил оба ACK. Так мы пропускаем один пакет, и больше нет никакого шанса продолжить передачу (XMODEM не предусматривает запрос предыдущего пакета). В случае же NACK худшее что может произойти - мы получим пакет, который уже был получен ранее, в таком случае его просто стоит проигнорировать.
В таблице с примерным сеансом связи 4-й блок вроде как с ошибкой принят, приёмник NAK отправил, а передатчик вместо повтора 4-го блока дальше пошёл передавать... Я чего-то недопонял?
Комментарии
Ещё: "Пример кода, вычисляющего CRC8" не вычисляет CRC8. Он вычисляет контрольную сумму символов, а CRC это сложнее... Вот CRC16 вычисляется как нужно.
Эта проблема всплыла при кратковременном отключении линии RX устройства. Устройство попыталось отправить ACK второй раз, а прошивальщик словил оба ACK. Так мы пропускаем один пакет, и больше нет никакого шанса продолжить передачу (XMODEM не предусматривает запрос предыдущего пакета). В случае же NACK худшее что может произойти - мы получим пакет, который уже был получен ранее, в таком случае его просто стоит проигнорировать .
RSS лента комментариев этой записи