Администрирование Разное Обзор протокола XMODEM Sat, April 20 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Обзор протокола XMODEM Печать
Добавил(а) microsin   

Протокол 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 блоков, некоторые из которых передаются повторно.

Передатчик
  Приемник
  < C
    Таймаут после 3 секунд
  < C
SOH 0x01 0xFE Данные CRC > Пакет принят успешно
  < ACK
SOH 0x02 0xFD Данные CRC > Повреждение на линии во время передачи
  < NAK
SOH 0x02 0xFD Данные CRC > Пакет принят успешно
  < ACK
SOH 0x03 0xFC Данные CRC > Пакет принят успешно
(ACK не принят из-за помех) < ACK
  < ACK
SOH 0x04 0xFB Данные CRC > (Ошибка фрейма на любом байте)
  < NAK
SOH 0x05 0xFA Данные CRC > (Ошибка переполнения приема на любом байте)
          < NAK
SOH 0x05 0xFA Данные CRC > Пакет принят успешно
  < ACK
EOT > Пакет принят успешно
  < ACK
ETB > Завершение сеанса приема
Завершение сеанса передачи < ACK

Некоторые версии протокола используют специальный символ 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]

int calcrc(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);
}

[Пример кода, вычисляющего CRC16]

static const unsigned short crc16tab[256]= {
    0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
    0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
    0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
    0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
    0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
    0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
    0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
    0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
    0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
    0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
    0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
    0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
    0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
    0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
    0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
    0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
    0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
    0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
    0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
    0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
    0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
    0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
    0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
    0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
    0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
    0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
    0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
    0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
    0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
    0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
    0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
    0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
};
  
unsigned short crc16_ccitt(const void *buf, int len)
{
    int counter;
    unsigned short crc = 0;
    u8 *pnt = (u8*)buf;
 
    for( counter = 0; counter < len; counter++)
        crc = (crc << 8) ^ crc16tab[((crc >> 8) ^ *(char *)pnt++)&0x00FF];
    return crc;
}

Подпрограмма вернет 16-битное значение контрольной суммы, где старший байт числа должен быть помещен в 132 байт буфера XMODEM (если считать по индексу от 0, то в 131 байт буфера), а младший байт должен быть помещен в 133 байт буфера XMODEM (если считать по индексу от 0, то в 132 байт буфера). Пример использования программы для подсчета CRC16:

#define XMODEM_PKT_SIZE (1+1+1+128+2)     // 133 байта стандартного пакета XModem
static unsigned char xbuff[XMODEM_PKT_SIZE];
 
static void xtxpacket_create (u8 *data, u32 packetcnt)
{
   u32 idx = 0;
   u16 crc;
   
   //Признак заголовка:
   xbuff[idx++] = XSOH;
   //Номер пакета:
   xbuff[idx++] = 0xFF & packetcnt;
   xbuff[idx++] = 0xFF - (0xFF & packetcnt);
   //Запись данных:
   memcpy(&xbuff[idx], data, 128);
   //Вставка контрольной суммы:
   crc = crc16_ccitt(&xbuff[idx], 128);
   idx += 128;
   xbuff[idx++] = (crc >> 8);
   xbuff[idx++] = crc&0x00FF;
}

[Linux]

sx, sb, sz - утилиты для отправки файла по протоколам XMODEM, YMODEM, ZMODEM, находятся в пакете lrzsz.

Minicom

Qodem Terminal Emulator

RZSZ - пакет, который предоставляет множество команд для работы по протоколам XMODEM, YMODEM, ZMODEM.

[Windows]

HyperTerminal

SecureCRT

TeraTerm

ExtraPuTTY

[Android]

FTDI UART Terminal. Подробнее см. [5].

[Ссылки]

1. XModem Protocol with CRC site:web.mit.edu.
2. XMODEM Protocol Overview site:techheap.packetizer.com.
3. Динамическая индикация на примере управления светодиодной матрицей 8x8 GNM-7881AUE.
4. Blackfin: практическая реализация загрузчика (bootloader).
5. USB OTG serial port: программы и утилиты.

 

Комментарии  

 
+1 #2 Дмитрий 18.03.2020 13:23
Важное замечание: нельзя отправлять ACK больше одного раза (в таблице две отправки)! Если первый раз отправка не получилась, то все следующие разы стоит отправлять NACK.
Эта проблема всплыла при кратковременном отключении линии RX устройства. Устройство попыталось отправить ACK второй раз, а прошивальщик словил оба ACK. Так мы пропускаем один пакет, и больше нет никакого шанса продолжить передачу (XMODEM не предусматривает запрос предыдущего пакета). В случае же NACK худшее что может произойти - мы получим пакет, который уже был получен ранее, в таком случае его просто стоит проигнорировать .
Цитировать
 
 
+4 #1 Владимир 19.11.2016 00:25
В таблице с примерным сеансом связи 4-й блок вроде как с ошибкой принят, приёмник NAK отправил, а передатчик вместо повтора 4-го блока дальше пошёл передавать... Я чего-то недопонял?
Цитировать
 

Добавить комментарий


Защитный код
Обновить

Top of Page