Как работает usb на физическом уровне

от admin

Компьютерная Энциклопедия

Кабель USB содержит две пары проводов: одну для сигнальных цепей (D+ и D-) и одну пару для схемной земли (GND) и подачи питания +5 В (Vbus). Допустимая длина сегмента (кабеля от устройства до хаба) — до 5 м. Ограничения на длину сегмента диктуются затуханием сигнала и вносимыми задержками. Задержка распространения сигнала по кабельному сегменту не должна превышать 26 нс, так что при большой погонной задержке допустимая длина кабеля может сократиться. Максимальное удаление устройства от хост-контроллера определяется задержкой, вносимой кабелями, промежуточными хабами и самими устройствами.

В кабеле USB 1.x для сигнальных цепей используется витая пара проводов калибра 28AWG с импедансом 90 Ом. Характеристики кабеля нормированы в частотном диапазоне до 16 МГц. Для питания используется неперевитая пара проводов калибра 20AWG–28AWG. Требований к экранированию кабелей в USB 1.x не выдвигалось. Для низкой скорости может использоваться кабель с неперевитой парой сигнальных проводов (он дешевле и тоньше), но его длина не должна превышать 3 м.

В кабелях USB 2.0 используются провода тех же калибров, но в спецификации описана конструкция кабеля, в которую входит обязательный экран и связанный с ним дополнительный проводник. Такой кабель пригоден для работы на любых скоростях, включая и HS (480 Мбит/с).

Разъемы USB сконструированы с учетом легкости подключения и отключения устройств. Для обеспечения возможности «горячего» подключения разъемы обеспечивают более раннее соединение и позднее отсоединение питающих цепей по отношению к сигнальным. В USB определено несколько типов разъемов:

  • тип «A»: гнезда (рисунок а) устанавливаются на нисходящих портах хабов, это стандартные порты подключения устройств. Вилки типа «A» устанавливаются на шнурах периферийных устройств или восходящих портов хабов;
  • тип «B»: используются для шнуров, отсоединяемых от периферийных устройств и восходящих портов хабов (от «мелких» устройств — мышей, клавиатур и т. п. кабели, как правило, не отсоединяются). На устройстве устанавливается гнездо (рисунок б), на кабеле — вилка;
  • тип «Mini-B» (рисунок в): используются для отсоединяемых шнуров малогабаритных устройств;
  • тип «Mini-A»: введен в спецификации OTG, вилки используются для подключения устройств к портам малогабаритных устройств с гнездом «mini-AB».
  • тип «Mini-AB»: гнезда введены в спецификации OTG для портов двухролевых устройств, которые могут вести себя как хост (если в гнездо вставлена вилка miniA) или как периферийное устройство (если в гнездо вставлена вилка mini-B).

Назначение выводов разъемов USB приведено в таблице, нумерация контактов показана на рисунке выше. Штырьковые разъемы, устанавливаемые на системной плате (рисунок г), предназначены для кабелей-«выкидышей», которыми подключаются дополнительные разъемы USB, устанавливаемые на передней или задней стенках корпуса компьютера (иногда и на боковых). На эти разъемы порты выводятся парами, причем у разных производителей подход к универсальности и защите от ошибочных подключений различен. Подключение «выкидыша», не подходящего к разъему, приводит к неработоспособности порта (к счастью, как правило, временной). Ошибка в подключении цепей GND и +5V может приводить к нагреванию кабелей и разъемов из-за короткого замыкания питающей цепи.

Все кабели USB «прямые» — в них соединяются одноименные цепи разъемов, кроме цепи ID, используемой для идентификации роли устройства в OTG. На вилке mini-A контакт 4 (ID) соединен с контактом 5 (GND), что заставляет порт, к которому подсоединена такая вилка, взять на себя роль нисходящего порта хаба. На вилке miniB такого соединения нет.

Ошибка в полярности подводимого питания может повредить подключаемое устройство (и необратимо). По этой причине наиболее безопасными для подключаемого устройства являются внешние разъемы USB, запаянные на системной плате или карте контроллера USB.

Таблица. Назначение выводов разъема USB
Цепь Контакт стандартного разъема Контакт миниразъема
VBus (+5 В) 1 1
D– 2 2
D+ 3 3
GND 4 5
ID 4

Приемопередатчики

Для передачи сигналов используются два провода D+ и D–. На каждой стороне интерфейса (порте хаба и подключенного устройства, см. рисунок ниже) имеются:

  • дифференциальный приемник, выход которого используется при приеме данных;
  • управляемый (отключаемый) дифференциальный FS/LS-передатчик — источник напряжения, позволяющий кроме дифференциального сигнала формировать и «линейный 0» (SE0), а также отключаться для обеспечения полудуплексного обмена;
  • линейные приемники, сообщающие текущее состояние каждого сигнального провода;
  • резисторы, подтягивающие уровни сигналов для обнаружения подключения устройства:
    — Rd1, Rd2 (15 кОм) у хаба;
    — Ruf (у FS/HS-устройства) или Rul (у LS-устройства);
  • Дополнительные элементы для работы на высокой скорости (только для устройств HS):
    — коммутатор, отключающий резистор Ruf при выборе высокой скорости;
    — последовательные резисторы Rz1 и Rz2 на выходах дифференциального передатчика, обеспечивающие согласование с линией и нагрузку;
    — управляемый дифференциальный источник тока;
    — детектор амплитуды сигнала;
    — детектор отключения (только на нисходящих портах хабов).

Уровни сигналов передатчиков FS/LS в статическом режиме должны быть ниже 0,3 В (низкий уровень) или выше 2,8 В (высокий уровень). Приемники должны выдерживать входное напряжение в пределах –0,5. +3,8 В. Чувствительность дифференциальных приемников — 200 мВ при синфазном напряжении 0,8–2,5 В. Линейные приемники должны обладать гистерезисом с нижним порогом 0,8 В и верхним порогом 2 В.

Передача данных

Передача по двум проводам USB не ограничивается лишь дифференциальными сигналами. Приемники и передатчики позволяют использовать множество состояний линий и команд, используемых для организации аппаратного интерфейса. При этом учитываются не только уровни электрических сигналов, но и время нахождения их в том или ином состоянии. По уровням напряжения на входах приемников различают сигналы:

  • Diff0: (D+) — (D-) > 200 мВ при (D+) > 2 В;
  • Diff1: (D-) — (D+) > 200 мВ при (D-) > 2 В;
  • SE0 (single-ended zero): (D+) < 0,8 В и (D-) < 0,8 В.

Для передачи данных используются сигналы Diff0 и Diff1, они кодируют состояния J (Data J State) и K (Data K State). На полной и высокой скорости состояние J соответствует сигналу Diff1, состояние K — сигналу Diff0. На низкой скорости назначение обратное: J — Diff0 и K — Diff1. Последовательная передача информации ведется с использованием кодирования NRZI (см. рисунок ниже): при передачи нулевого бита в начале битового интервала состояние сигнала (J или K) меняется на противоположное; при передаче единичного — не меняется. Длительность битового интервала определяется номинальной частотой передачи: 0,666… мкс для низкой скорости (LS, 1,5 Мбит/с); 83,3… нс для полной (FS, 12 Мбит/с) и 2,0833… нс для высокой (HS, 480 Мбит/с).

Состояние покоя (Bus Idle) на FS/LS соответствует длительному состоянию J, а на HS — состоянию SE0.

Признаком начала пакета является переход из состояния покоя в состояние K, что является первым битом синхропоследовательности (Sync), — последовательности нулей, которая в NRZI кодируется переключением состояний (J и K) в начале каждого битового интервала. Синхропоследовательность позволяет приемнику настроиться на нужною частоту и фазу синхронизации. Синхропоследовательность завершает единичный бит (нет смены состояния), последующие за ним биты относятся к идентификатору и телу пакета. На HS начальная часть синхропоследовательности может быть потеряна хабом (из-за задержки реакции на детектор сигнала). С учетом этого синхросполедовательность для HS удлинена до 32 бит (включая последний единичный бит). Проходя через 5 хабов, каждый из которых может потерять до 4 синхробит, синхропоследовательность может оказаться сокращенной до 12 бит.

Для того чтобы синхронизация не терялась на монотонном сигнале (при передаче длинной последовательности единиц), применяется техника вставки бит (bit stuffing): после каждых 6 подряд следующих единиц передатчик вставляет «0», приемник эти вставленные биты удаляет. Если принимается более 6 единиц подряд, это считается ошибкой вставки бит.

Конец пакета (EOP) на FS/LS обозначается сигналом SE0, длящимся 2 битовых интервала, за которым следует переход в состояние покоя (Bus Idle). На HS для признака EOP используется нарушение правила вставки бит. Здесь в качестве EOP используется передача последовательности 01111111 без вставки бит. Прием седьмой единицы вызовет индикацию ошибки вставки бит, которая на HS и является признаком конца пакета. Нормальный пакет при этом от действительно ошибочного будет отличаться целым количеством принятых байт (это условие может и не проверяться) и верным значением CRC. Начальный нолик (вызывающий смену состояния) в EOP облегчает точное определение границы тела пакета. В пакетах SOF поле EOP удлинено до 40 бит для обнаружения отключения устройства.

Особенности сигналов в режиме HS

Высокая скорость (480 Мбит/с — всего в 2 раза медленнее, чем Gigabit Ethernet) требует тщательного согласования приемопередатчиков и линий связи. На этой скорости может работать только кабель с экранированной витой парой для сигнальных линий. Для высокой скорости аппаратура USB должна иметь дополнительные специальные приемопередатчики. К разводке проводов на печатной плате устройства от интерфейсной микросхемы USB до разъема (или подключения кабеля) предъявляют жесткие требования (максимальная длина, совпадение длин сигнальных проводников, удаленность от других сигнальных цепей, окружение «землей»).

В отличие от формирователей потенциала для режимов FS и LS передатчики HS являются источниками тока, ориентированными на наличие резисторов-терминаторов на обеих сигнальных линиях. Роль терминаторов играют резисторы Rz1 и Rz2 (см. рис. 12.2): при работе на HS дифференциальный передатчик FS/LS формирует SE0, то есть оба его выхода заземляются и эти резисторы оказываются нагрузками для линий D+ и D-. Их сопротивление (с учетом выходного импеданса передатчика) составляет 2×45 = 90 Ом, что и обеспечивает согласование с волновым сопротивлением линии (90 Ом). Устройство и хаб включают свои HS-терминаторы (и отключают Ruf) после успешного взаимного подтверждения режима HS, выполняемого в процессе сброса устройства.

Дифференциальные токовые передатчики формируют импульсы тока с номинальным значением 17,78 мА, который протекает через нагрузку 22,5 Ом (два нагрузочных резистора на обоих концах каждой сигнальной линии соединяются параллельно). При передаче сигнала J ток пропускается в линию D+, при K — в D-. Таким образом обеспечивается дифференциальный сигнал передачи около ±400 мВ.

На вход дифференциального приемника сигнал придет ослабленным; чтобы исключить влияние шумов, в схему устройства введен детектор амплитуды сигнала с порогом 100–150 мВ. Сигнал с дифференциального приемника игнорируется, пока не сработает детектор амплитуды сигнала (в спецификации USB этот прием называется receiver squelch). От срабатывания детектора амплитуды до включения дифференциального приемника может быть задержка до 4 bt, но это приведет лишь к сокращению длины принятой синхропоследовательности в начале пакета.

К статическим (уровни) и динамическим (длительности и время нарастания и спада) параметрам сигналов на HS предъявляются жесткие требования, и существуют специальные шаблоны (Eye Pattern), в которые должны укладываться сигналы. Для тестирования могут быть использованы широкополосные (не уже 1 ГГц) дифференциальные осциллографы и генераторы; выпускаются и специализированные тестеры устройств USB 2.0. Для тестирования HS-устройств (включая и хабы) в USB 2.0 определены специальные управляющие запросы, переводящие выбранный порт в тестовый режим. В стандартных запросах определены следующие тесты:

  • Test_SE0_NAK — перевод порта в HS-режим для тестирования выходного импеданса, уровня сигнала SE0 и нагрузочных характеристик;
  • Test_J — передача состояния J (подача тока в D+) для проверки уровня сигнала, выводимого на линию D+;
  • Test_K — передача состояния K (подача тока в D-) для проверки уровня сигнала, выводимого на линию D-;
  • Test_Packet — передача пакетов фиксированной структуры, позволяющая проверять динамические параметры сигнала и подключенные к нему приемники. Тестовый пакет выглядит как пакет данных DATA0, у которого в поле данных последовательно расположены 6 тестовых образцов (test pattern). Далее приводятся последовательности сигналов, передающихся на шину в этих образцах (вставка бит уже произведена):
    — Pattern 1 — 36 пар «JK»;
    — Pattern 2 — 16 повторов «JJKK»;
    — Pattern 3 — 8 повторов «JJJJKKKK» (4J и 4K);
    — Pattern 4 — 8 повторов «JJJJJJJKKKKKKK» (7J и 7K);
    — Pattern 5 — 8 повторов «JJJJJJJK» (7J и 1K);
    — Pattern 6 — 10 повторов «JKKKKKKK» (1J и 7K).
  • Test_Force_Enable — принудительный перевод нисходящих портов хаба в режим HS, даже при отключенных устройствах (для настройки детектора отключения).

Специальная сигнализация: обнаружение подключения-отключения, сброс устройств, приостановка и пробуждение

Хаб обнаруживает подключение устройства по уровням напряжения на линиях D+ и D–:

  • при отключенном устройстве на линиях D+ и D- уровни сигнала низкие (состояние SE0), что обусловлено резисторами Rd1 и Rd2 хаба;
  • при подключении LS-устройства повышается уровень сигнала D- за счет резистора Rul в устройстве (переход в состояние LS-Idle);
  • при подключении FS/HS-устройства повышается уровень сигнала D+ за счет резистора Rul в устройстве (переход в состояние FS-Idle).

Последовательность обнаружения подключения и сброса устройств FS и LS приведена на рисунках а и б соответственно. Хаб следит за сигналами нисходящего порта и сигнализирует об их смене. После обнаружения смены состояния системное ПО выжидает около 100 мс (время на успокоение сигналов) и проверяет состояния порта. Обнаружив факт подключения и тип устройства (LS или FS/HS), ПО дает для этого порта команду сброса шины.

Для выполнения сброса шины (команда Bus Reset) хаб опускает уровень поднятого устройством сигнала (D+ или D) на 10–20 мс (то есть подает сигнал SE0 в течение 10–20 мс). Считается, что через 10 мс после этого сброса устройство должно быть готово к конфигурированию (отзываться только на обращения к EP0 по нулевому адресу устройства).

Сброс шины для устройства HS запускает протокол согласования скорости. При подключении, как и по сигналу сброса, HS-устройство устанавливает свои схемы в состояние FS (отключая терминаторы и включая Ruf). Таким образом, поначалу HS-устройство выглядит для хаба как FS-устройство. Для согласования скорости используется так называемое «чириканье» (chirp-sequence): в ответ на состояние SE0, введенное хабом для сброса (заземлением линии D+), HS-устройство своим дифференциальным токовым передатчикам вводит состояние «chirp-K» (пуская импульс тока в линию D-). На этот импульс HS-хаб ответит импульсом на линии D+, так что получится состояние «chirp-J». Такой обмен импульсами повторяется еще дважды; после успеха согласования и устройство и хаб принимают режим работы HS (и резистор Ruf отключается). Все это «чириканье» занимает 10–20 мс, после чего шина переходит в состояние покоя HS-Idle (длительный сигнал SE0). Теперь хосту надо снова опросить состояние порта хаба, чтобы уточнить режим подключенного устройства (FS или HS). Если HS-устройство подключено к FSпорту, хаб на «чириканье» устройства не ответит.

Отключение устройств FS/LS обнаруживается хабом просто по длительному (более 2 мкс) состоянию SE0. Этот факт хаб доводит до сведения системного ПО (USBD), чтобы устройство было вычеркнуто из всех рабочих списков. Отключение устройств HS таким способом обнаружить не удается, поскольку состояние шины (SE0) при отключении устройства не изменится. Для обнаружения отключения HS-устройства используют эффект отражения сигнала при потере согласованности линии. Специально для этих целей в схему хаба введен дополнительный детектор отключения, а в маркере микрокадра SOF признак EOP (0111…111) удлинен до 40 битовых интервалов. Транслируя SOF на высокоскоростной порт, детектор отключения следит за уровнем сигнала J, и если он превышает порог (625 мВ дифференциального сигнала), значит, нагрузки на другой стороне нет, то есть устройство отключено. Удлинение EOP необходимо, поскольку устройство может отключиться внутренне, и из-за задержки в кабеле устройства (2×26 нс) отраженный сигнал может задержаться до 25 нс. С целью сокращения накладных расходов это удлинение EOP сделали только для пакетов SOF, появляющихся всего раз в 125 мкс.

Команду приостановки устройства — Suspend хаб сигнализирует длительным состоянием покоя (Bus Idle). При этом он должен переставать транслировать все кадры, включая и маркеры микрокадров на порты, для которых подается эта команда. На порты, работающие в LS-режиме, маркеры кадров не транслируются; чтобы LS-устройство не приостанавливалось при отсутствии полезного трафика, ему вместо маркеров SOF хаб с тем же периодом посылает сигнал LS-EOP (SE0 в течении 1,33 мкс). Приостановка делается не менее чем на 20 мс — за это время устройство должно успеть перейти в приостановленное состояние и стать готовым к получению сигнала возобновления.

Команду приостановки HS-порта хаб сигнализирует покоем (SE0) в течение 3 мс, после чего переключает свои цепи в режим FS (отключает терминаторы), но помнит, что порт находится в режиме HS. Для HS-устройства команда приостановки поначалу неотличима от сброса. Чтобы их различить, через 3–3,125 мс непрерывного состояния SE0 HS-устройство переключает свои цепи в режим FS (отключает терминаторы и включает Ruf). Далее, через 100–875 мкс устройство проверяет состояние линий. Если обе лини D+ и D- оказались в низкоуровневом состоянии, значит, хаб подал команду сброса (и устройство должно выполнить chirp-последовательность). Если уровень D+ высокий, а D- низкий (FS-Idle), то это сигнал к приостановке. Таким образом, по состоянию сигналов на шине приостановка выглядит как покой LS/FS — то есть состояние J.

Сигналом к возобновлению работы (resume) является перевод шины в состояние K на длительное время (20 мс), достаточное для «оживления» устройств, после чего хаб посылает сигнал LS-EOP (SE0 в течение 1,33… мкс). После этого шина переходит в состояние покоя соответствующей скорости и начинает передаваться трафик. Сигнал возобновления может подать как хаб, так и приостановленное устройство; последний случай называется удаленным пробуждением. По сигналу возобновления устройство, работавшее в HS-режиме, и его порт хаба переключают свои цепи в HS-режим без всякого согласования (они помнят свой режим).

Удаленное пробуждение — Remote Wakeup — это единственный случай на USB, когда сигнальную инициативу проявляет устройство (а не хост). Сигнал пробуждения может подать только приостановленное устройство, для которого шина находится в FS/LS-состоянии J (резисторами подтягивается вверх D+ или D-). Для сигнализации пробуждения устройство на некоторое время (1–15 мс) формирует состояние K, которое воспримется хабом как сигнал Resume и транслируется им на восходящий порт и на все разрешенные нисходящие порты, включая и тот порт, с которого пришел данный сигнал.

3.1.2. Физический интерфейс usb

Стандарт USB определяет электрические и механические спецификации шины.

Информационные сигналы и питающее напряжение 5 В передаются по четырехпроводному кабелю. Используется дифференциальный способ передачи сигналов D+ и D- по двум проводам. Уровни сигналов передатчиков в статическом режиме должны быть ниже 0,3 В (низкий уровень) или выше 2,8 В (высокий уровень). Приемники выдерживают входное напряжение в пределах — 0,5. +3,8 В. Передатчики должны уметь переходить в высокоимпедансное состояние для двунаправленной полудуплексной передачи по одной паре проводов.

Передача по двум проводам в USB не ограничивается дифференциальными сигналами. Кроме дифференциального приемника каждое устройство имеет линейные приемники сигналов D+ и D-, а передатчики этих линий управляются индивидуально. Это позволяет различать более двух состояний линии, используемых для организации аппаратного интерфейса. Состояния Diff0 и Diff1 определяются по разности потенциалов на линиях D+ и D- более 200 мВ при условии, что на одной из них потенциал выше порога срабатывания VSE. Состояние, при котором на обоих входах D+ и D- присутствует низкий уровень, называется линейным нулем (SEO — Single-Ended Zero). Интерфейс определяет следующие состояния:

Data J State и Data К State — состояния передаваемого бита (или просто J и К), определяются через состояния Diff0 и Diff1;

Idle State — пауза на шине;

Resume State — сигнал «пробуждения» для вывода устройства из «спящего» режима;

Start of Packet (SOP) — начало пакета (переход из Idle State в К);

End of Packet (EOP) — конец пакета;

Disconnect — устройство отключено от порта;

Connect — устройство подключено к порту;

Reset — сброс устройства.

Состояния определяются сочетаниями дифференциальных и линейных сигналов; для полной и низкой скоростей состояния Diff0 и Diff1 имеют противоположное назначение. В декодировании состояний Disconnect, Connect и Reset учитывается время нахождения линий (более 2,5 мс) в определенных состояниях.

Шина USB rev.1.0 имеет два режима передачи. Полная скорость передачи сигналов USB составляет 12 Мбит/с, низкая — 1,5 Мбит/с. Для полной скорости используется экранированная витая пара с импедансом 90 Ом и длиной сегмента до 5 м, для низкой – не витой не экранированньгй кабель длиной до 3 м. Низкоскоростные кабели и устройства дешевле высокоскоростных. Одна и та же система может одновременно использовать оба режима; переключение для устройств осуществляется прозрачно.

Низкая скорость предназначена для работы с небольшим количеством ПУ, не требующих высокой скорости. Скорость, используемая устройством, подключенным к конкретному порту, определяется хабом по уровням сигналов на линиях D+ и D-, смещаемых нагрузочными резисторами R2 приемопередатчиков (Рис. 20, а и б).

Рис.20 Схема подключения устройств к шине USB.

Сигналы синхронизации кодируются вместе с данными по методу NRZI (Non Return to Zero Invert), его работу иллюстрирует рис. 23в. Каждому пакету предшествует поле синхронизации SYNC, позволяющее приемнику настроиться на частоту передатчика.

Кабель также имеет линии VBus и GND для передачи питающего напряжения 5В к устройствам. Сечение проводников выбирается в соответствии с длиной сегмента для обеспечения гарантированного уровня сигнала и питающего напряжения.

Стандарт определяет два типа разъемов (табл. 2 и рис. 21).

Разъемы типа «А» применяются для подключения к хабам (Upstream Connector). Вилки устанавливаются на кабелях, не отсоединяемых от устройств (например, клавиатура, мышь и т. п.). Гнезда устанавливаются на нисходящих портах (Downstream Port) хабов.

Разъемы типа «В» (Downstream Connector) устанавливаются на устройствах, от которых соединительный кабель может отсоединяться (принтеры и сканеры). Ответная часть (вилка) устанавливается на соединительном кабеле, противоположный конец которого имеет вилку типа «А».

Разъемы типов «А» и «В» различаются механически (рис. 21, а и б), что исключает недопустимые петлевые соединения портов хабов. Четырехконтактные разъемы имеют ключи, исключающие неправильное присоединение. Конструкция разъемов обеспечивает позднее соединение и раннее отсоединение сигнальных цепей по сравнению с питающими. Для распознавания разъема USB на корпусе устройства ставится стандартное символическое обозначение (Рис.21, в).

Рис. 21. Гнезда USB: а — типа «А», б — типа «В», в — символическое обозначение

Питание устройств USB возможно от кабеля (Bus-Powered Devices) или от собственного блока питания (Self-Powered Devices). Хост обеспечивает питанием непосредственно подключенные к нему ПУ. Каждый хаб, в свою очередь, обеспечивает питание устройств, подключенных к его нисходящим портам. При некоторых ограничениях топологии допускается применение хабов, питающихся от шины. На рис. 22 приведен пример схемы соединения устройств USB. Здесь клавиатура, перо и мышь могут питаться от шины.

Физический уровень интерфейса USB 1.1

Физический уровень USB разрабатывался от уровня RS232-RS485. В 90-х годах большинство последовательных интерфейсов использовало физический уровень RS232-RS485. Такие интерфейсы создавались добавлением программного (протокольного) уровня к физическому уровню RS232-RS485. Интерфейсов, построенных по такому принципу, – десятки, а может сотни. Некоторые из них: ModBus, ProFibus, DCON, DH-485, BitBus, HART и многие др. Все эти протоколы сегодня успешно работают во многих отраслях человеческой деятельности. Естественно при разработке физического уровня USB использовались эти наработки. На рисунке 1 показана структура физического уровня RS232-RS485.

Структура состоит из двух драйверов(приёмопередатчиков) и двух физических сред (кабель RS232 и кабель RS485). Связь с программным уровнем осуществляется через системную шину ПК, к которой подключен контроллер RS232 (UART). Кабель RS485 обеспечивает общую физическую многоточечную среду, к которой подключаются драйверы RS485 других устройств. Драйверы RS232 и RS485 выполнены симметрично, то есть структура драйвера одинакова для DTE и DCE устройств RS232, также одинакова структура драйверов для ведущего и ведомого драйвера RS485.

Стык RS232 работает в дуплексном режиме (драйверы имеют равные права). Стыке RS485 в один момент времени может существовать только один ведущий драйвер (передающий), остальные драйвера ведомые (принимающие). Ведущим становиться любой драйвер RS485, который получил право передавать информацию. Алгоритм передачи маркера, который делает точку ведущей, определяет программный уровень интерфейса (протокол). Драйвер RS485 имеет гальваническую развязку с сигналами RS232, что позволяет осуществлять "горячее" подключение и отключение точек сети.

На рисунке 2 показана структура физического уровня USB 1.1.

Как видно на рисунке 2, физический уровень USB напоминает RS485, используется аналогичный полудуплексный балансный сигнал. Для упрощения линии связи было решено отказаться от использования независимых сервисных сигналов, которые управляют обменом данных. Сервисные сигналы RS232 были заменены сигналами и состояниями, которые передаются по балансной линии связи. Тем самым стало возможным переместить физический уровень RS232 в программную область интерфейса. Это придало интерфейсу дополнительную конфигурационную гибкость и упростило его физическую реализацию. Но, при этом интерфейс лишился гальванической развязки, согласованности линии связи, симметричности и многоточечности. Кроме того, включение программного уровня в структуру интерфейса сделало его замкнутым, то есть не способным к модификационному размножению. Несогласованность, асимметричность и увеличенная скорость передачи сократили расстояние связи с 1200 метров (RS485) до 5 метров(USB). В интерфейс USB была добавлена линия питания +5В, что очень увеличило его функциональность.

Общая структура физического уровня. Физический уровень USB 1.1. состоит из двух драйверов и физической среды (кабеля) между ними. Драйверы физического уровня не симметричны, то есть имеют разную структуру. Физическая среда одноточечная и односторонняя (полудуплекс). Существует два типа драйвера:

Downstream (вниз передающий) — этот драйвер всегда ведущий, то есть только он определяет кто, когда и сколько будет передавать данных в линию связи. Драйверы этого типа всегда генерируют информационный сигнал в направлении от хоста. В сети эти драйверы установлены на хосте или вниз передающем порту хаба.

Upstream (вверх передающий) — это драйвер всегда ведомый. Он всегда генерирует информационный сигнал в направлении хоста. Время и порядок его работы определяет ведущий драйвер (Downstream). Эти драйвера устанавливаются в устройствах и верх передающих портах хаба. Драйвер Upstream может быть одного из двух видов:

Upstreeam Full Speed — для работы на скорости 12 Mb/s;

Upstream Low Speed — для работы на скорости 1,5 Mb/s.

Рисунок 1 – Структура физического уровня RS232-RS485

Так как драйверы не симметричны, связь в физическом уровне USB 1.1 возможна только между драйверами разного типа, то есть только между Downstream и Upstreeam. Соответственно не симметричен и кабель для подключения, со стороны Downstream он имеет разъём серии A, а со стороны Upstream разъём серии B. Драйвера и кабели RS232 и RS485 симметричны, поэтому ПК можно соединять между собой через COM порты. Соединить два ПК через USB порты невозможно. Этот факт конечно, ухудшает универсальность порта, но позволяет упростить аппаратную часть, так как односторонние драйвера аппаратно более просты. Кроме того, односторонняя реализации упрощает программный уровень, так как параллельные процессы в ПК трудно реализуемы.

Рисунок 2 – Структура физического уровня USB 1.1

Устройство драйверов. Как видно на рисунке 2 драйвера USB состоят из: контроллера, кодера, декодера, генератора, дифференциального и линейных приёмников, подтягивающих резисторов и источника питания.

Контроллер драйвера. Контроллер соединяет генерирующую и приёмную часть драйвера через системную шину с программным уровнем хоста, концентратора или устройства. Так как весь передаваемый пакет может быть сформирован программным уровнем, то аппаратная реализации контроллера не сложна.

Кодер NRZI. Пакет кодируется методом NRZI с помощью JK-триггера. Алгоритм NRZI кодирования довольно прост, при передаче нуля генератор должен изменить полярность сигнальной линии на противоположную, при передаче единицы оставить полярность сигнала прежней (т.е. ничего не менять). Кодеры для Full Speed и Low Speed отличаются выходным сигналом (противоположны). NRZI кодирование позволяет сократить число синхробитов, вставляемых в пакет данных.

Декодер. Преобразует NRZI закодированные данные к начальному виду и выделяет синхросигнал из принимаемых данных.

Генератор. Генератор устроен аналогично генератору RS485. Генератор передает в линию связи дифференциальные нули и единицы. Когда генератор не передает данные, он отключен от линии связи сигналом OE и не влияет на её работу. Генератор может замыкать линию связи на общий провод сигналом SEO. Это используется для сброса шины. На рисунке 3 представлены напряжения, измеряемые на выходах генератора:

Vdi — дифференциальное напряжение, это напряжение между выводами D+ и D-;

Vd+ — линейное напряжение, это напряжение между Выводом D+ и GND;

Vd- — линейное напряжение, это напряжение между Выводом D- и GND.

ЭДС генератора: +3,6v;

Внутреннее сопротивление: 56..88 Om Макс. допустимое прямое линейное напряжение: +4,6v;

Макс. допустимое обратное линейное напряжение: -1,0v;

Генератор должен статически держать параметры линейного напряжения на выходе:

VOL= не более +0.3v при нагрузке 1,5kOm подключенной к +3,6v для низ. линейного сигнала (0);

VOH= не менее +2.8v при нагрузке 15 kOm подключенной к GND для выс. линейного сигнала (1);

Нарастание фронта линейного сигнала, должно быть в пределах: 4ns.20ns (FS), 75ns.300ns (LS);

Длительность линейного сигнала высокого уровня при передаче бита должна быть не менее: 60ns;

Пересечение линейных сигналов D+ и D-(Vd+=Vd-) должно быть в диапазоне: VCRS= 1, 3v..2,0v;

Нагрузочная ёмкость выхода: CL= 50pF (FS), 50pF.150pF(LS Upstream), 200pF.600pF (LS Downstream).

Рисунок 3 – Напряжения, измеряемые на выходах генератора

Дифференциальный приёмник. Предназначен для приёма данных с линии связи D+D-. Измеряет напряжение между линиями D+ D- (Vdi). Определяет два состояния: дифференциальный "0" и дифференциальную "1".

Параметры диф. приемника:

— чувствительность приёмника должна быть не ниже VDI= 200Mv;

Читать:
Как купить дешево автомобиль с пробегом

— диф. "1": Vdi>+200mV (Vd+ > Vd- более чем на 200mv в диапазоне лин. напряжения VCM= 0,8v..2,5v);

— диф. "0": Vdi<-200mV (Vd- > Vd+ более чем на 200mv в диапазоне лин. напряжения VCM= 0,8v..2,5v).

Линейные приемники. Измеряют линейные напряжения Vd+ и Vd- относительно GND. Комбинация линейных напряжений и времени их удержания определяют состояние линии связи. В результате применения линейных приемников, USB порт может определять несколько состояний линии связи, без применения сервисных сигналов (например, как в RS232, 6 сервисных сигналов, задают 128 состояний линии связи).

— напряжение Vd+ или Vd- низкого уровня (0), VIL= не более +0,8v;

— напряжение Vd+ или Vd- высокого уровня (1), VIH = не менее +2,0v.

— напряжения Vd+ или Vd- могут опускаться ниже VIL, без фиксирования состояния SE0, на время не более: TFST= 14ns (для FS) и TLST= 210ns (для LS).

Подтягивающие резисторы. Служат для идентификации устройств USB при их подключении/отключении к порту Downstream. Для Downstream Vd+ и Vd- подтягиваются к низкому уровню (GND) резисторами 15kOm. Для Upstream Full Speed Vd+ подтягивают к высокому уровню (VTERM=3.0V..3.6V) резистором 1.5kOm. Для Upstream Low Speed Vd- подтягивают к высокому уровню (VTERM=3.0V..3.6V) резистором 1.5kOm.

Источник питания. Как видно из структуры физического уровня USB 1.1 драйвера Downstream имеют источники питания напряжением VBUS=+5V для питания нисходящих устройств сети по шине USB. В спецификации введено понятие "модульная нагрузка", одна модульная нагрузка равна 100mA. Классификация устройств по питанию:

Root port hubs (корневые порты хабов). Эти порты запитаны от источника питания хоста. Например, если хост установлен в ПК, то источником питания является блок питания ПК. Все порты этого хаба должны обеспечивать не менее 5-ти модульных нагрузок (500mA), такие порты называют High-Power port(сильный питающий порт). Обратите внимание, что только High-Power port может обеспечить ток более 500mA, по сути дела вы напрямую подключаетесь к блоку питания ПК и ограничения по току связаны только с мощностью блока питания компьютера. Если питание хоста выполнено на батареях, то такие порты должны обеспечивать не менее одной модульной нагрузки(100mA) и такие порты называют Low-Power port (слабый питающий порт).

Bus-powered hubs (шинозапитанный хаб). Этот хаб имеет питание по шине USB и всю мощность он берет от вышестоящего downstream порта. Такой хаб потребляет одну модульную нагрузку при включении и до 5-ти модульных нагрузок после конфигурации хаба. В результате шинопитающийся хаб должен после конфигурации распределить имеющиеся в его распоряжении 500mA между всеми портами и функциями, входящими в его состав.

Self-powered hubs (Хаб с собственным питанием). Этот хаб имеет собственный внутренний источник питания и не использует питание передаваемое по шине USB.
Однако он может использовать до одной модульной нагрузки с шины USB, когда его собственное питание не включено. После конфигурации этот хаб запитывается от своего источника питания и обеспечивает всем портам, входящим в его состав, до 5-ти модульных нагрузок. Если внутренний источник хаба- батарея, то до одной модульной нагрузки на каждый порт входящий в состав хаба.

Low-power bus-powered functions (слабая шиннозапитанная функция). Это устройство USB, которое берет от Downstream порта не более одной модульной нагрузки в любое время.

High-power bus-powered functions (сильная шиннозапитанная функция). Это устройство USB, которое берет от Downstream порта не более одной модульной нагрузки во время подключения и до 5-ти модульных нагрузок после конфигурирования.

Self-powered functions (функция с собственным питанием). Это устройство USB, которое имеет собственный источник питания, это устройство может брать с шины USB до одной модульной нагрузки при включении устройства.

High-Power Port(сильно питающий порт), VBUS=4,75V..5,25V;

Low-Power Port (слабый питающий порт), VBUS=4, 40V..5,25V;

High-power Hub Port (out) (выход High порта хаба), ICCPRT= не менее 500mА;

Low-power Hub Port (out)) (выход Low порта хаба), ICCUPT= не менее 100mА;

High-power Function (in) (потребление High устройства), ICCHPF= не более 500mА;

Low-power Function (in) (потребление Low устройства), ICCLPF= не более 100mА;

Unconfigured Function/Hub (in) (не сконфигурированное устройство), ICCINIT= не более 100mА;

Suspended High-power Device (High устройство в остановленном состоянии), ICCSH= не более 2,5mА;

Suspended Low-power Device (Low устройство в остановленном состоянии), ICCSL= не более 0,5mА.

Кодирование и синхронизация. Бинарные данные передаваемые через интерфейс USB кодируются методом NRZI. Метод NRZI (Non Return to Zero Invert) заключается в изменении полярности сигнала при кодировании "0". При передаче "1" полярность сигнала остаётся прежней. NRZI кодированные сигналы Low Speed устройств противоположны сигналам Full Speed устройств USB (рисунок 4).

NRZI кодирование используется для увеличения информационной ёмкости сигнала, за счёт уменьшения количества встраиваемых синхробитов.

Синхронизация данных осуществляется от каждого переходного фронта сигнала. Так как каждый "0" бит данных изменяет полярность сигнала при NRZI кодировании на противоположный, то каждый "0" бит создает переходной фронт сигнала, относительно которого синхронизируется приёмник данных. Три метода синхронизации пакета данных в USB:

1) SOP (Start-of-Packet) Переход из состояния Idle в состояние K. В начала каждого пакета вставляется байт 80h, который имеет 7 нулей и одну единицу. Семь фронтов, идущих подряд, позволяют надежно синхронизовать приёмник с началом пакета данных.

2) "0" бит данных. Каждый "0" бит данных дополнительно синхронизует приёмник. В результате NRZI кодирования, все данные содержащие "0" бит не нуждаются в дополнительном синхробите.

3) Stuffed Bit -вставляемый синхробит. Если в пакете данных появляются подряд шесть "1", то чтобы не потерять синхронизацию приёмника вставляют "0", который считается синхробитом. То есть, "0" после шести "1" не является битом данных и программным уровнем игнорируется. Счет единиц начинается с поля SYNC, т.е. с единицы имеющейся в этом поле. Также учтите, что синхробиты не привязаны к байтам, они считаются во всей битовой последовательности пакета. Вставка бита осуществляется всегда, без исключения. Если по правилам требуется вставка бита, нулевой бит будет вставлен, даже если это — последний бит, т.е. бит перед сигналом конец-пакета (EOP).

Рисунок 4 – NRZI кодирование

На рисунке 5 видно, что начальное положение Idle для FS и LS имеют разный знак, поэтому первый бит синхробайта должен изменить начальное положение на противоположное, иначе мы бы потеряли значение первого бита. Отсюда становится понятно, почему кодировка NRZI для FS и LS различаются. Как видно из рис.5 синхронизация USB не хуже синхронизации RS232 (где синхронизируется каждый байт). Причем затрат времени на синхронизацию в USB почти в четыре раза меньше, и соответственно мы можем передать больше информации за единицу времени. Недостатки синхронизации с NRZI:

1) В пакете появляются байты длиною в 9 бит.

2) Каждый Stuff бит задерживает остаток пакета на время равное интервалу бита. Поэтому разные пакеты передаются с разным временем задержки.

3) Пакеты становятся плавающей длины. В RS232 таких проблем нет. Отсюда вывод, что за всё приходиться расплачиваться.

Состояния линии связи. Как видно из структуры физического уровня USB 1.1 (рисунок 2), в ней отсутствует сервисные сигналы. Это сильно ограничивает функциональность интерфейса, так как становиться трудно организовать процедуры: установки, обмена, повтора при ошибке, останова и разрыва связи. Это являлось основным недостатком интерфейса RS-485, в котором можно обнаружить только два состояния: ведется передача или нет. В результате применения связки RS232-RS485 терялись все преимущества COM порта по организации процесса связи. Рассмотрим, как разработчики USB порта решили эту проблему:

1) В структуру добавлены два линейных приёмника. Они могут определить четыре состояния линии связи D+D-:00, 01, 10, 11. Эти состояния определяются, когда по линии связи нет обмена данными. Причем четырех состояний для организации полноценного обмена будет недостаточно.

2) Добавлен анализ временных интервалов состояний линии связи Использование анализа временных интервалов состояний линии связи, позволяет генерировать 9 сервисных сигналов. Сервисные сигналы позволяют организовать управление обменом данных в линии связи.

Разбираем и собираем обратно стек USB

Иллюстрированная проекция модели сетевого взаимодействия OSI на универсальную последовательную шину.

Три «замечательных» уровня стека USB

Меня не устроил вид стека USB, который можно встретить чаще всего на просторах сети:

Уровень шины, логический, функциональный… Это, конечно, замечательные абстракции, но они скорее для тех, кто собирается делать драйвер или прикладной софт для хоста. На стороне же микроконтроллера я ожидаю шаблонный конечный автомат, в узлы которого мы обычно встраиваем свой полезный код, и он сперва будет по всем законам жанра глючить. Или же глючить будет софт на хосте. Или драйвер. В любом случае кто-то будет глючить. В библиотеках МК тоже с наскока не разобраться. И вот я смотрю на трафик по шине USB анализатором, где происходящие события на незнакомом языке с тремя замечательными уровнями вообще не вяжутся. Интересно, это у меня от гриппозной лихорадки в голове такой диссонанс?

Если у читателя бывали сходные ощущения, предлагаю альтернативное, явившееся мне неожиданно ясно в перегретом мозгу видение стека USB, по мотивам любимой 7-уровневой модели OSI. Я ограничился пятью уровнями:

Я не хочу сказать, что весь софт и библиотеки уже сделаны или должны проектироваться, исходя из этой модели. Из инженерных соображений код c уровнями будет сильно перемешан. Но я хочу помочь тем, кто начинает своё знакомство с шиной USB, кто хочет понять протоколы обмена устройств и терминологию предметной области, подобраться поближе к готовым примерам, библиотекам и лучше ориентироваться в них. Эта модель не для загрузки в МК, но в ваши блестящие умы, дорогие друзья. А ваши золотые руки потом всё сами сделают, я не сомневаюсь:)

Итак, поехали, поправляйте, если увидите косяки. Это draft-версия, и если уже такое где-то было нарисовано, прошу простить, я не нашёл и потому скрутил сам. Думаю, картинка никуда не убежит, а я пока объясню почтенной публике, зачем вообще взялся за эту публикацию.

Хотим мы этого или нет, но эволюция микроконтроллеров на месте стоять явно не собирается. Нет, нет, да и мелькнёт в публикациях (http://habrahabr.ru/post/208026/, http://habrahabr.ru/post/233391/) «тяжёлая периферия» — вмонтированные в МК реализации шины USB, с разборами примеров, использованием HID и т.п. Надо воздать должное автору RaJa: из восьми примеров, приведённых в стандартной библиотеке STSW-STM32121 (UM0424) и кое-как документированных, он выбрал самый полезный (Custom HID), портировал его в бесплатную среду Em::Blocks, изложил понятным языком, немного приукрасил, браво! Это сэкономило мне уйму времени.

Как пройти в библиотеку?

Получив на GitHub любезно выложенный автором проект RHIDDemo для Em::Blocks, я начал портировать его в Keil (мой отладчик CoLink на базе FTDI; кто-нибудь, подскажите плагин от Coocox для Em::Blocks). Но никак не мог понять: где, чёрт возьми, автор раздобыл SPL 3.6.1 выпуска 2012г, если на сайте выложен 3.5.0 от 2011г? Я прошёл довольно скучный квест, который к моему удивлению привёл… прямо на готовый проект Custom HID для Keil в составе библиотеки USB FS 4.0.0. Лежит у всех на виду, как мышь под веником. Ну и ладно. Зато я раскурил, наконец, релизы STMicroelectronics, нашёл описание библиотеки USB FS STSW-STM32121 (UM0424) и пресёк попытки разработчика свести меня с ума. Вот скажите, это нормально подкладывать винтажный CMSIS 1.30 образца 2009г в набор SPL 3.5.0 выпуска 2011г, новый SPL 3.6.1 релиза 2012г прятать в USB-FS 4.0.0 релиза 2013г (подложив туда же и CMSIS 3.0.1 от 2012г), при том, что у них же выложена актуальная версия CMSIS 3.30 релиза 2014г? Кстати, в SPL 3.6.x для STM32F10X исправили пару багов с USART, касающихся сигналов о переполнении буфера. Спасибо, хоть release notes оставили…

HID vs SNMP

Итак, взявшись за STM32F103C8T6, я тоже решил слегка задвинуться по теме USB HID, уж больно хорошо абстракция USB HID укладывается в концепцию всяческих датчиков, сенсоров и прочих ШИМ-управляемых драйверов питания. Чем-то напомнило мне SNMP, только в сильно упрощённом виде: дескрипторы HID играют роль SNMP MIB. Когда устройство инициализируется хостом: «Привет, хост! Я кофеварка. У меня есть кнопка [старт], регуляторы [сливки], [сахар], датчики [остаток кофе], [остаток воды], [остаток сахара], [остаток сливок]. Подтягивай драйвера, дави на кнопку, кофейку попьём». Ничего не напоминает? Пример диалога по SNMP: «Ну, привет, управляющая станция с софтом за $100000. А я шасси коммутатора за $200000, и на мне сидят ещё 4 модуля по $100000 за штуку; в каждом ещё по 16 портов с неприличной скоростью, и всех функций тут просто не перечислить… спрашивай отдельно по каждому пункту; ах, да загрузка процессора такая-то, памяти столько-то…». И ещё на дюжину страниц в таком же духе.

Понравилась мне идея HID. Но стоило выйти из Windows за рамки учебных задач мигания светодиодами (вперёд к реальным окружениям UNIX!), как начало сквозить из всех незаделанных щелей, и я почувствовал себя каким-то беспомощным ламером. Отлаживая проект, я инстинктивно схватился за некое подобие tcpdump (так и называется: usbdump(8), или usbmon), но увидел лишь сообщения на незнакомом языке.

Стало очевидно: не хватает фундаментальных знаний о шине USB. Если модель OSI и стек TCP/IP любой тёртый айтишник осознаёт где-то на уровне спинного мозга просто в силу необходимости, то с USB ситуация другая. Оно и понятно: там можно (нужно) подсмотреть трафик через тот же tcpdump да настроить железо с софтом, а тут полный plug and play, и исправить что-то можно, обновив драйвер или прошивку (или переустановив ОС). Но ведь мы тут с вами собрались как раз за тем, чтобы делать хорошие прошивки, не так ли? Почитав некоторые описания USB в сети, я был удивлён, насколько запутанной может быть документация. У меня даже возникло ощущение, что нас специально хотят сбить с пути истинного, напустив туману и избавившись от конкуренции в зародыше. Я не согласен с таким положением вещей!

Ещё одна замечательная схема

На просторах сети встретил ещё такую иллюстрацию (лежало в формате BMP, без шуток):

Сперва выглядит оптимистично. Наконец-то, стек в разобранном виде. Кадры, правда, обозначены неудачно: я бы нарисовал их вертикальными пунктирными линиями, а EOF — это просто пауза, реально данные не передаются. Но начинаем читать контекст и теряем понимаем истинный замысел автора (запутать нас):

Вроде бы и нарисовано всё правильно, но по мере прочтения вопросов становится всё больше. Минимальная передаваемая структура данных по шине — это кадр или пакет? Вообще, это сверху вниз надо смотреть или наоборот? И что кодируется по методу NRZI — кадры, пакеты или просто весь битовый поток по шине? Из транзакций состоит посылка, передача, или, может быть, ценная бандероль какая?
Почему нельзя просто: хост группирует пакеты в транзакции и распределяет их по временным квантам, именуемым кадрами, чтобы давать приоритет критичным по времени данным (видео, аудио) исходя из текущей пропускной способности шины? Да, в USB есть нюансы с планированием передачи пакетов, я их пока не затрагиваю.

Моё видение стека USB

Хорошей документацией считаю упоминавшийся тут на хабре USB in a NutShell (ура, перевод), а также USB Made Simple. По ним я и собрал свою версию стека USB, нарисую её ещё раз.

Физический уровень

На физическом уровне используется набор электрических режимов дифференциальной пары проводников (вместе с землёй) для обозначения состояний, с помощью которых кодируется битовый поток по методу NRZI со вставкой битов (bit stuffing): здесь после шести идущих подряд «1» (ну захотелось передать, скажем, 0xffff) вставляется «0», чтобы приёмник подолгу не залипал в одном состоянии; приёмник узнает вставленный «0» и как данные не засчитает, это довольно распространённый приём в кодировании для лучшей автоподстройки частот. Пара проводов вместе с землёй даёт возможность сформировать, как минимум, четыре статических состояния (они обозначаются J, K, SE0, SE1). В USB 2.0 SE1 не используется, а три оставшихся дополнительно разыгрываются в динамике (с часами и переходами) для передачи ещё нескольких управляющих символов (границы пакетов, сброс, подключение/отключение, энергосбережение/выход). Хорошие иллюстрации есть в USB Made Simple, Part 3 — Data Flow.
Т.е. в итоге передаются данные в виде ноликов и единичек, плюс всякие управляющие символы, чтобы можно было из всей этой электродинамической кухни готовить нормальные пакеты данных.
(дополнено по просьбе читателей)

Пакетный уровень

На пакетном уровне между хостом и устройством передаются безадресные пакеты (пара устройств на полудуплексной линии может обойтись и без адресации). Пакет состоит из маркера SYNC для синхронизации тактов приёмника, последовательности байт и символа EOP. Длина пакета переменная, но оговаривается через верхние уровни стека. Первый байт называется Packet Identifier (PID), имеет простой избыточный формат для помехоустойчивости и пригоден для скармливания автомату следующего уровня (для сборки транзакций из пакетов). Пакеты с начинкой (длиннее одного байта PID) снабжаются контрольной суммой (короткой CRC5 или длинной CRC16, в зависимости от типа пакета). Анализатор протоколов должен, как минимум, показывать нам пакеты.

Уровень транзакций

На следующем уровне из пакетов собираются транзакции. Транзакция — это малый набор пакетов (в Full Speed USB 1, 2 или 3), следующих строго друг за другом, которыми (в полудуплексном режиме) хост обменивается с оконечной точкой (endpoint), и только с одной. Очень важно, что транзакцию открывает только хост, это специфика USB (нам в прошивке МК меньше мороки). На уровне транзакций можно говорить о канале (pipe) между хостом и одной из оконечных точек устройства, но я намеренно избегаю термина «канальный уровень» (Data Link) из модели OSI. Анализатор протоколов должен хотя бы декодировать транзакции.

Уровень передач

Поверх транзакций расположим уровень передач (transfers). Их в USB используется четыре типа: контрольные с оконечной точкой №0 (control transfers), передачи с прерываниями (interrupt transfers), изохронные (isochronous transfers) и крупноблочные передачи (bulk transfers). Последние три являются вариантами потоковых каналов (stream pipe), про которые я ещё скажу несколько слов. Этот уровень тоже должен отобразить хороший анализатор протоколов.

Прикладной уровень

Венчает стек, как обычно, прикладной уровень. Здесь происходят: установка адреса устройству хостом, рассказ устройства о себе на языке дескрипторов, команды хоста на выбор конфигурации (контрольные передачи), обмен данными с HID-устройствами (в примерах пока нашёл передачу с прерываниями, хочу попробовать контрольную), печать на принтере и сканирование, доступ к накопителю USB (крупноблочные), общение через гарнитуры и веб-камеры (изохронные) и многие другие замечательные вещи.

Последний штрих

Сбежав на секунду вниз по уровням, можно добавить, что хост периодически вбрасывает по шине те самые пакеты Start of Frame (SOF), разбивая время на равные интервалы, но так, чтобы не разбить при этом сами транзакции. Поэтому пакеты SOF можно считать самостоятельными транзакциями. Не следует путать кадр (фрейм) USB с омонимом канального уровня модели OSI. Лучше уж вспомнить кадры (фреймы) аудио CD, это просто квант времени: хост «тикает» в шину пакетами SOF, чтобы подключённые устройства заранее планировали участие в т.н. изохронных передачах, гоняющих потоки данных в реальном времени. Ну или вот так: группы транзакций планируются хостом по временным интервалам, именуемым кадрами. Кадр составляет 1мс на Full Speed и 125мкс на High Speed USB, но High Speed — более сложный стандарт, его лучше изучать отдельно.
UPD:
Хороший вопрос задали читатели: а как насчёт фрагментации? Я не нашёл в USB 2.0 признаков фрагментации на уровне транзакций и ниже, т.е. транзакции для того и есть, чтобы передаваться целиком. Передачи же в ряде случаев могут и должны разбиваться на несколько транзакций, особенно с учётом изохронных режимов. И я повторю, что всем планированием у нас пока заведует хост (на стороне МК меньше думать приходится).

Смотрим на трафик по USB

Итак, транзакция всегда инициируется хостом в отношении одной выбранной оконечной точки на устройстве (помимо специальной точки с номером 0, их может быть ещё до 15 штук на одном устройстве, например, комбинированная клавиатура с мышью, термометром, флэшкой, кофеваркой и кнопкой вызова сантехника заказа пиццы).
В случае приёма хостом данных с устройства последнее не может само открыть транзакцию, но может только дождаться нужного момента и поучаствовать в ней. Хост открывает транзакцию устройству пакетом с PID = IN (группа Token) и гарантирует на нужное время свободу шины, устройство вбрасывает пакет из группы Data, в зависимости от типа транзакции хост может подтвердить успех третьим пакетом из группы Handshake (ACK, NAK, STALL, NYET), транзакция закрыта.
При отправке данных на устройство (PID = OUT, группа Token) хост открывает транзакцию, отправляет пакет с данными (Data), также в зависимости от режима может принять пакет Handshake с подтверждением успешности транзакции.
По окончании транзакции всё вернётся на круги своя, устройство снова будет ждать управляющих пакетов от хоста.

Режимы передачи USB в примерах STM32 USB FS

Чтобы по одной паре проводов можно было гнать копирование с диска одновременно с аудио-видео потоком, жестами мышью и сигналом скоростного осциллографа, существуют разные типы сообщений и передач.
Чуть выше я только что описал простой потоковый канал (Stream Pipe) между хостом и оконечной точкой, где пакеты с начинкой (группы Data) не несут никакой специальной или управляющей информации самой подсистеме USB. Полная свобода переписки, библиотека контроллера должны предоставлять примитивы для закачки буфера произвольного размера из памяти МК хосту или обратно. Нарезкой на пакеты, пересылкой и «дефрагментацией» пусть занимаются библиотека МК на пару с драйвером хоста. В STM32 это USB_SIL_Write() и USB_SIL_Read(), описаны в UM0424. Они и есть тот самый логический уровень абстракции. На стороне хоста см. описание соответствующего драйвера (например, во FreeBSD это ugen(4)).
Однако использовать тяжёлую периферию вроде USB для организации простого потокового канала я считаю кощунством (спрашивается: чем USART не угодил?). Но ситуации, конечно, бывают всякие.
В любом случае, чтобы подсистема USB вообще ожила и устройство определилось, требуется обмен контрольными транзакциями.

DISCLAIMER

Дальше будут упоминаться примеры из той самой библиотеки UM0424 для работы с Full Speed USB от STMicroelectronics, но они рассчитаны под их родные демоплаты. Берите пример с автора Raja, проявляйте инженерную смекалку в адаптации проектов под свою демоплату.

По софту всё понятно: это примеры не для промышленного использования, там могут быть баги, некоторые части (типа таблицы ссылок в примере Mass storage) защищены патентом, и вы не имеете прав их использовать в коммерческом проекте. Но это ещё ничего, китайцы ухитряются потом продавать на рынке USB-изделия, у которых даже библиотечные VID и PID не удосужились поменять.

По железу, как я понял, надо начинать с кварца. У меня челябинский PinBoard II с кварцем 12Мгц (все библиотеки заточены под 8МГц), я менял умножитель ФАПЧ с 9 на 6 (ссылка с разъяснениями), иначе МК разгонится до 108МГц вместо 72МГц, а USB на 72МГц вместо положенных 48МГц вообще не поедет. Можно ещё сбавить обороты МК до 48МГц, поменяв делитель шины USB с полутора до единицы. Использовать внутренний генератор МК HSI спецы не любят: частота может слегка уплыть от нагрева, последствия для USB предсказать затрудняюсь. Ну и не забываем о периферии, конечно. Без флэш-памяти SPI/SDIO из примера Mass storage можно сделать разве что аналог /dev/null, но его ведь хрен отформатируешь:-)

Контрольные передачи и каналы сообщений
Передачи с прерываниями

Эта разновидность (interrupt transfer) предназначена для обмена небольшими транзакциями, сходными с контрольными. Нет, устройство не может прерывать хоста, оно ждёт опроса, их частота и размеры пакетов оговариваются заранее в дескрипторе устройства. Хорошо подходят для всевозможных пультов, датчиков, сенсоров, мышек, светодиодов и прочих HID-кофеварок. Канал с прерываниями каждой точки однонаправленный.
Примеры: Custom HID, Joystick mouse, Virtual COM port

Передачи изохронные

Χρόνος по-гречески значит «время». Изохронная передача (isochronous transfer) — местный хайтек, позволяющий управлять потоками данных в реальном времени. Отличается гарантированной (но необязательно широкой) полосой пропускания и отсутствием подтверждающих транзакций, почти как UDP с QoS. Битый пакет? Это бог Хронос толкнул МК по ноге. Не надо пытаться отправить пакет заново, иначе бог огорчится. Контрольные суммы, тем не менее, проверяем втихую от Хроноса. Изохронные передачи хороши для аудио-видео и измерительных систем реального времени, а также прочих игрушек двойного назначения. Хотя на некоторые из них м.б. интереснее повесить какой-нибудь AVR, связав его с нашим ARM по USART или SPI. Изохронные операции участвуют во фреймовой сигнализации (вспомним про тиканье пакетом SOF).
Пример: USB voice speaker

Передачи крупноблочные

Нет, мешки с цементом таскать не будем. Я думаю, все узнали режим работы всевозможных накопителей USB. Передачи bulk transfer имеют цель отправить данных как можно больше и быстрее, обязательно с пересылкой битых пакетов, но без гарантий по полосе пропускания, уступая её изохронным передачам при необходимости (как в TCP без QoS). О внутреннем устройстве USB флэшек я как-то уже рассказывал, теперь можно скачать и запустить действующий прототип. Я сам его не пробовал, но таблица команд SCSI в описании примера (как-бы, между прочим) весьма символизирует. Признаков алгоритма управления износом для NAND-памяти я не нашёл:-)
ВНИМАНИЕ: местами действует патентная защита STM.
Пример: Mass storage

Что осталось нераскрытым

Я не имею цель сделать ещё один учебник по USB, их и без меня хватает, и там хорошо описаны: электрическая часть, подробности протоколов, работа с концентраторами, дескрипторный язык и уровень абстракции HID, проблемы с уникальностью VID/PID, USB 3.0 и многие другие замечательные возможности шины USB, как полезные нам, так и не очень. Айтишникам особо рекомендую экскурсию на тёмную сторону с обзором вражеских девайсов (флэшка с замаскированной HID-клавиатурой, которая будет делать страшные вещи).

Ссылки

Адаптация примера Custom HID к бесплатной среде Em::Blocks и бюджетной демо-плате STM32F103C8T6 производства LC-Tech: habrahabr.ru/post/208026
Битва за ИБП: habrahabr.ru/post/233391 ещё одна битва за ИБП: habrahabr.ru/post/233391/#comment_7944489
Экскурсия на тёмную сторону (шпионский девайс из AVR): habrahabr.ru/post/153571
Инструкции по анализу USB в Wireshark для Windows и Linux: wiki.wireshark.org/CaptureSetup/USB
Книжка USB in a NutShell: www.beyondlogic.org/usbnutshell/usb1.shtml
Перевод USB in a NutShell: microsin.ru/content/view/1107/44
Книжка USB Made Simple (действительно упростили): www.usbmadesimple.co.uk
STSW-STM32121, библиотека STMicroelectronics USB full speed device library и все упомянутые примеры (UM0424) www.st.com/web/en/catalog/tools/PF258157

P.S.
Читая публикации на хабре, посвящённые в той или иной степени микроэлектронике, я разглядел две инженерных касты, назовём их условно: Промэлектронщики и Айтишники. Это своего рода инженерный Инь и Ян, в каждом из нас есть доля того и другого.

Промэлектронщики имеют блестящие знания и навыки по железу, паяют радиодетали толщиной с волос левой рукой с закрытыми глазами (причём потом это работает). Взглянув на электронную схему, почти физически начинают ощущать все её токи с потенциалами, работают также и с силовыми схемами, и с (большими, быстрыми, опасными) промышленными изделиями. Подход к программированию МК соответствующий: он просто должен выдать нужные логические уровни на нужные ножки в нужное время, не столь важно каким способом. Консервативны в технологиях (не влезай — работает), тяжёлую периферию МК не особо жалуют. При обсуждении объектно-ориентированного программирования, информационной безопасности, гигантских проектов в миллион строк кода и всяких навороченных графических интерфейсов скучнеют. Вместо пакетно-ориентированной шины USB предпочитают потоковый режим USART, усиленный либо привычным RS-232, либо более брутальным RS-485 (последовательная шина для промышленных применений, до 10Мбит/с на 15м, до 100кБит/с на 1200м, до 32 устройств).

Айтишники воспитаны на понимании операционных систем, сетевой инфраструктуры и сложных взаимодействий, элита хорошо подкована в информационной безопасности и разбирается во всяких незримых способах проникновения в чужую систему. Некоторые при этом очень любят котиков (ну как их можно не любить? я, правда, не держу, не развожу и не готовлю:-). Многие любят свободу информации, ругать корпорации/правительства и побеждать силы природы усилием мысли. Паталогически ленивы, но обожают новые технологии и закрученные инженерные ребусы с дорогими игрушками (желательно решаемые на уровне софта или, в крайнем случае, перемычек). Отношения с паяльником настороженные: не спрашивайте у айтишника, любит ли он паяльник, может неправильно понять; лучше спросите, любит ли он паять электронные схемы.

К чему я? Мы просто видим этот мир по-разному… Ведь ядро Linux кроили такие же ребята, из модулей на С и ассемблерных вставок для конкретных платформ, и без холиваров вроде обошлись. По-настоящему серьёзный проект я вижу как многоядерную систему, сочетающую современнейшие МК с тяжёлой периферией, но не исключаю связки с классическими моделями типа AVR: ими можно обвесить какие-нибудь критичные быстровращающиеся острия технического прогресса. Если код проверенный годами, то почему нет?

Похожие публикации