Usb протокол что такое
Протокол шины USB обеспечивает обмен данными между хостом и устройством. На протокольном уровне решаются такие задачи, как обеспечение достоверности и надежности передачи, управление потоком. Весь трафик на шине USB передается посредством транзакций, в каждой транзакции возможен обмен только между хостом и адресуемым устройством (его конечной точкой).
Все транзакции (обмены) с устройствами USB состоят из двух-трех пакетов, типовые последовательности пакетов в транзакциях приведены на рис. 1. Каждая транзакция планируется и начинается по инициативе хост-контроллера, который посылает пакет-маркер транзакции (token packet). Маркер транзакции описывает тип и направление передачи, адрес выбранного устройства USB и номер конечной точки. Адресуемое маркером устройство распознает свой адрес и готовится к обмену. Источник данных, определенный маркером, передает пакет данных. На этом этапе транзакции, относящиеся к изохронным передачам, завершаются — здесь нет подтверждения приема пакетов. Для остальных типов передач работает механизм подтверждения, обеспечивающий гарантированную доставку данных. Форматы пакетов приведены на рис. 2, типы пакетов — в таблице. Во всех полях пакетов, кроме поля CRC, данные передаются младшим битом вперед (на временных диаграммах младший бит изображается слева). Пакет начинается с синхропоследовательности Sync и завершается признаком конца — EOP. Тип пакета определяется полем PID. Назначение остальных полей раскрывается далее. Длина полей Sync и EOP указана для передач на FS/LS, для высокоскоростных передач поле Sync удлинено до 32 битовых интервалов, а EOP до 8 (в пакетах SOF поле EOP имеет длину 40 бит).
Таблица. Типы пакетов и их идентификаторы PID
Имя | Код PID | Содержимое и назначение |
Пакеты-маркеры (Token) | ||
OUT | 0001 | Маркер транзакции вывода, несет идентификатор конечной точки (адрес устройства и номер точки; направление точки определяется кодом PID) |
IN | 1001 | Маркер транзакции ввода, несет идентификатор конечной точки (адрес устройства и номер точки; направление точки определяется кодом PID) |
SETUP | 1101 | Маркер транзакции управления, несет идентификатор конечной точки (адрес устройства и номер точки) |
SOF | 0101 | Маркер начала микрокадра, несет 11-битный номер кадра (вместо полей Addr и EndP) |
PING | 0100 | Пробный маркер управления потоком (в USB 2.0) |
Пакеты данных | ||
DATA0 | 0011 | Пакеты данных; чередование PID позволяет различать четные и нечетные пакеты для контроля правильности подтверждения |
DATA1 | 1011 | |
DATA2 | 0111 | Дополнительные типы пакетов данных, используемые в транзакциях с широкополосными изохронными точками (в USB 2.0 для HS) |
MDATA | 1111 | |
Пакеты квитирования (Handshake) | ||
ACK | 0010 | Подтверждение безошибочного приема пакета |
NAK | 1010 | Индикация занятости (неготовности конечной точки к обмену данными, незавершенности обработки транзакции управления) |
STALL | 1110 | Конечная точка требует вмешательства хоста |
NYET | 0110 | Подтверждение безошибочного приема, но указание на отсутствие места для приема следующего пакета максимального размера (в USB 2.0) |
Специальные пакеты (Special) | ||
PRE | 1100 | Преамбула (маркер) передачи на низкой скорости (разрешает трансляцию данных на низкоскоростной порт хаба) |
ERR | 1100 | Сигнализация ошибки в расщепленной транзакции (в USB 2.0) |
SPLIT (SS и CS) | 1000 | Маркер расщепленной транзакции (в USB 2.0). В зависимости от назначения обозначается как SS (маркер запуска) и CS (маркер завершения), назначение определяется битом SC в теле маркера |
Контроль и обработка ошибок передачи
Все принимаемые пакеты проверяются на наличие ошибок, что позволяют принятые форматы пакета и некоторые соглашения:
- пакет начинается с синхронизирующей последовательности, за которой следует его идентификатор PID (Packet Identificator). За идентификатором следует его инверсная копия — Check. Несовпадение двух копий считается признаком ошибки;
- тело пакета (все поля пакета, исключая PID и признак EOP) защищается CRCкодом: 5-битным для пакетов-маркеров, 16-битным — для пакетов данных. Несовпадение CRC с ожидаемым значением считается признаком ошибки;
- пакет завершается специальным сигналом EOP; если в пакете оказывается не целое число байт, он считается ошибочным. Ложный EOP, даже на границе байта, не позволит принять пакет из-за практически неизбежной в данной ситуации ошибки по CRC-контролю;
- на физический уровень (в шину) данные пакета передаются с использованием вставки бит (bit stuffing, после шести единичных бит вставляется нолик), что предотвращает потерю битовой синхронизации при монотонном сигнале. Прием более шести единичных бит подряд считается ошибкой (на HS — признаком конца кадра).
Обнаружение любой из перечисленных ошибок в пакете заставляет приемник считать его недействительным. На пакеты, принятые с ошибкой, ни устройство, ни хост-контроллер никак не отвечают. При изохронной передаче данные недействительного пакета должны просто игнорироваться (они теряются); для остальных типов передач используются средства обеспечения надежной доставки.
Для обнаружения отсутствия ответа партнера на пакет каждое устройство имеет счетчик тайм-аута, который прерывает ожидание ответа по истечении некоторого времени. В USB имеется ограничение на время оборота по шине (roundtrip time): время от конца EOP сформированного пакета до получения начала ответного пакета. Для конечного устройства (и хост-контроллера) нормируется максимальная задержка ответа (response time) от конца увиденного EOP до введения им начала пакета. Для хабов нормируется задержка трансляции пакетов, для кабелей — задержка распространения сигналов. Счетчик тайм-аута должен учитывать максимальную задержку, возможную для допустимой конфигурации шины: до 5 промежуточных хабов, до 5 метров каждый кабель. Допустимое значение тайм-аута, выражаемое в битовых интервалах (bt), зависит от скорости:
- для скоростей FS/LS задержка, вводимая одним кабельным сегментом, по сравнению с битовым интервалом (bt) невелика. Исходя из этого в USB 1.0 для расчета допустимых задержек принимается следующая модель. На каждый кабельный сегмент отводится допустимая задержка 30 нс, на хаб — 40 нс. Таким образом, пять промежуточных хабов со своими кабелями вносят во время двойного оборота задержку 700 нс, что на FS соответствует примерно 8,5 bt. Для FS-устройства задержка ответа не должна превышать 6,5 bt (а с учетом его кабеля — 7,5 bt). Исходя из этого спецификация предписывает передатчикам на FS использовать счетчик тайм-аута на 16–18 bt;
- на скорости HS задержка в кабельном сегменте много больше битового интервала, и в USB 2.0 модель расчета несколько иная. Здесь на каждый кабельный сегмент отводится по 26 нс, а на хаб — по 4 нс плюс 36 bt. Таким образом, двукратное прохождение 6 кабельных сегментов (2×6×26 = 312 нс ≈ 150 bt) и пять хабов (2×5×4 = 40 нс ≈ 19 bt плюс 2×5×36 = 360 bt) занимает до 529 bt. Задержка ответа устройства допустима до 192 bt, а полная задержка с учетом кабелей и хабов будет до 721 bt. Исходя из этого спецификация предписывает передатчикам на HS использовать счетчик тайм-аута на 736–816 bt.
У хост-контроллера с каждой конечной точкой всех устройств связан свой счетчик ошибок, обнуляемый при планировании каждой транзакции. Этот счетчик считает все протокольные ошибки (включая и ошибки по тайм-ауту), и если число ошибок превышает порог (3), то канал с данной конечной точкой останавливается, о чем уведомляется его владелец (драйвер устройства или USBD). До превышения порога хост отрабатывает ошибки для неизохронных передач попытками повтора транзакций, без уведомления клиентского ПО. Изохронные передачи не повторяются, об обнаружении ошибок хост сообщает сразу.
Подтверждения, управление потоком и сигнализация ошибок устройства
Для подтверждений приема, управления потоком и сигнализации ошибок используются пакеты квитирования (handshake packets). Из этих пакетов хост-контроллер может посылать устройству только пакет ACK, подтверждающий безошибочный прием пакета данных. Устройство для ответа хосту использует следующие пакеты квитирования:
Управление потоком при выводе данных, основанное только на возможности ответа NAK в случае неготовности устройства, весьма неэффективно расходует пропускную способность шины: чтобы убедиться в неготовности устройства, по шине впустую передается большой пакет данных. В USB 2.0 этой неприятности в транзакциях Bulk-OUT и Control избегают, применив протокол проб (Ping Protocol). Хост может опросить готовность устройства к приему пакета максимального размера, послав ему маркер-пробник PING. На этот маркер устройство может ответить подтверждением ACK (при готовности) или NAK (если не способно принять пакет максимального размера). Отрицательный ответ заставит хост повторить пробу позже, положительный разрешит ему выполнить транзакцию вывода данных. На транзакцию вывода после положительного ответа на пробу ответы устройства более разнообразны:
- ACK означает успешный прием и готовность принять следующий полноразмерный пакет;
- NYET означает успешный прием, но неготовность к следующему пакету;
- NAK — неожиданный ответ (он противоречит успеху пробы), но он возможен, если устройство внезапно стало временно не готово.
Высокоскоростное устройство в дескрипторах конечных точек сообщает о возможной интенсивности посылок NAK: поле bInterval для конечных точек типа Bulk и Control указывает число микрокадров, приходящееся на один NAK (0 означает, что устройство никогда не ответит NAK’ом на транзакцию вывода).
Надёжность и транзакции для разных типов передач
Передачи массивов, прерываний и управления обеспечивают надежную доставку данных. После успешного приема пакета приемник данных посылает подтверждение — пакет квитирования ACK. Если приемник данных обнаружил ошибку, пакет игнорируется и никакого ответа на него не посылается. Источник данных считает, что очередной пакет передан успешно, когда получает от приемника подтверждение ACK. Если подтверждение не приходит, то в следующей транзакции источник повторяет посылку того же пакета. Однако пакет подтверждения может быть потерян из-за помехи; чтобы в этом случае повторная посылка пакета приемником не воспринималась как следующая порция данных, пакеты данных нумеруются. Нумерация ведется по модулю 2 (1-битный номер): пакеты делятся на четные (с идентификатором DATA0) и нечетные (DATA1). Для каждой конечной точки (кроме изохронных) у хоста и в устройстве имеются биты-переключатели (Toggle Bit), их начальные состояния тем или иным способом согласуются. В транзакциях IN и OUT передаются и ожидаются пакеты данных с идентификаторами DATA0 или DATA1, соответствующими текущему состоянию этих бит. Приемник данных переключает свой бит в случае безошибочного приема данных с ожидаемым идентификатором, источник данных — по приему подтверждения. Если приемник получает безошибочный пакет с неожидаемым идентификатором, он посылает подтверждение ACK, но данные пакета игнорирует, поскольку этот пакет — повторная посылка уже принятых данных.
Транзакции для различных типов передач имеют протокольные различия, обусловленные гарантированием или не гарантированием пропускной способности, времени отклика, надежности доставки и синхронизированности ввода и вывода. В зависимости от этих характеристик в транзакциях используются те или иные из вышеописанных протокольных механизмов. Отметим, что обнаружение ошибок передачи работает во всех транзакциях, так что данные, принятые с ошибкой, всегда игнорируются. Какие именно протокольные механизмы используются в текущей транзакции, «знает» и хост-контроллер (по ранее полученному дескриптору конечной точки), и устройство USB, в котором эта конечная точка реализована.
Транзакции изохронных передач
Изохронные транзакции обеспечивают гарантированную скорость обмена, но не обеспечивают надежности доставки. По этой причине в протоколе отсутствуют подтверждения, поскольку повтор пакета приведет к сбою в планах доставки данных. Управление потоком, основанное на подтверждениях, отсутствует — устройство обязано выдерживать темп обмена, заявленный в дескрипторе изохронной конечной точки.
Транзакции изохронного вывода состоят из двух пакетов, посылаемых хост-контроллером, — маркера OUT и пакета данных DATA. В транзакции ввода хост посылает маркер IN, на который устройство отвечает пакетом данных, возможно, и с нулевой длиной поля данных (если нет готовых данных). Любой другой ответ устройства (как и «молчание») хостом расценивается как ошибка, приводящая к остановке данного канала.
При изохронном обмене имеется контроль достоверности (отбрасывание пакетов с ошибками) и целостности данных (обнаружение факта пропажи пакета). Контроль целостности основан на строгой детерминированности темпа обмена — в соответствии со своим дескриптором точка ожидает транзакцию с периодом 2bInterval–1 микрокадров. Для обычной изохронной конечной точки в микрокадре возможна лишь одна транзакция, и ошибка при приеме пакета выражается в отсутствии принятых данных в микрокадре, в котором они ожидаются. Таким образом, нумерация пакетов (переключатель Toggle Bit) не требуется. Полноскоростные устройства и хостконтроллеры должны посылать пакеты только типа DATA01. Для широкополосных изохронных конечных точек (USB 2.0) в каждом микрокадре возможна передача до трех пакетов данных. Любой из этих пакетов может потеряться, и для обнаружения этой ситуации требуется нумерация пакетов внутри микрокадра. Для этой нумерации введено два новых типа пакетов данных: DATA2 и MDATA. Многообразие типов пакетов кроме нумерации позволяет еще и информировать партнера по связи о своих планах на данный микрокадр. В транзакциях IN идентификатором пакета устройство указывает, сколько еще пакетов оно собирается выдать в том же микрокадре, что позволяет хосту не делать лишних попыток ввода. Так, если в микрокадре передается один пакет, то это будет DATA0; если два — последовательность будет DATA1, DATA0; три — DATA2, DATA1, DATA0. В транзакциях OUT для вывода не последнего пакета в микрокадре используется пакет MDATA (More Data), а идентификатор последнего пакета показывает, сколько было до него передано пакетов. Так, при одной транзакции вывода используется пакет DATA0, при двух — последовательность MDATA, DATA1, при трех — MDATA, MDATA, DATA2. Во всех транзакциях, кроме последней в микрокадре, должны использоваться пакеты максимального размера. Отметим, что между широкополосными транзакциями в микрокадре могут вклиниваться другие транзакции.
Вроде мы слышали, что USB 3.0 — это круче, чем USB 2.0. Но чем именно — знают не все. А тут еще появляются какие-то форматы Gen 1, Gen 2, маркировки Superspeed. Разбираемся, что значат все эти маркировки и чем они отличаются друг от друга. Спойлер: версий USB всего четыре.
USB 2.0
Когда-то было слово только USB 1.0. Сейчас это уже практически архаика, которую даже на старых устройствах почти не встретить. Еще 20 лет назад на смену первопроходцу USB 1.0 пришел улучшенный USB 2.0. Как и первая версия, эта спецификация использует два вида проводов. По витой паре идет передача данных, а по второму типу провода — питание устройства, от которого и идет передача информации. Но такой тип подключения подходил только для устройств с малым потреблением тока. Для принтеров и другой офисной техники использовались свои блоки питания.
USB версии 2.0 могут работать в трех режимах:
- Low-speed, 10–1500 Кбит/c (клавиатуры, геймпады, мыши)
- Full-speed, 0,5–12 Мбит/с (аудио и видеоустройства)
- High-speed, 25–480 Мбит/с (видеоустройства, устройства для хранения данных)
USB 3.0
Стандарт USB 3.0 появился в 2008 году и до сих пор используется во многих устройствах. Скорость передачи данных выросла с 480 Мбит/с до 5 Гбит/с. Помимо скорости передачи данных, USB 3.0 отличается от версии 2.0 и силой тока. В отличие от более ранней версии, которая выдавала 500 мА, USB 3.0 способен отдавать до 4.5 Вт (5 В, 900 мА).
Новое поколение USB обратно совместима с предыдущими версиями. То есть USB 3.0 может работать и с разъемами USB 2.0 и даже 1.1. Но в этом случае буду ограничения по скорости. Подключив USB 3.0 к устройству с USB 2.0 скорость, вы получите не больше 480 Мбит/с — стандарт для версии 2.0. И наоборот, кабель 2.0 не станет более скоростным, если подключить его в устройство с USB 3.0. Это связано с количеством проводов, используемых в конкретной технологии. В версии USB 2.0 всего 4 провода, тогда как у USB 3.0 их 8.
Если вы хотите получить скорость передачи, заявленную стандартом USB 3.0, оба устройства и кабель должны быть именно версии 3.0.
USB 3.1
В 2013 году появляется версия USB 3.1 с максимальной заявленной скорость передачи данных до 10 Гбит/с, выходной мощностью до 100 Вт (20 В, 5 А). С появлением USB 3.1 произошла революция в маркировках всех стандартов. Но с ней мы разберемся чуть позже. А пока запомним главное: пропускная способность USB 3.1 увеличилась вдвое по сравнению с версией 3.0. И одновременно с обновленным стандартом появился и принципиально новый разъем — USB type-С. Он навсегда решил проблему неправильного подключения кабеля, так как стал симметричным и универсальным, и теперь все равно, какой стороной подключать провод к устройству.
USB 3.2
В 2017 году появилась информация о новой версии — USB 3.2. Она получила сразу два канала (больше проводов богу проводов) по 10 Гбит/с в каждую сторону и суммарную скорость в 20 Гбит/с. Стандарт USB 3.2 также обратно совместим с режимами USB 3.1, 3.0 и ниже. Поддерживается типом подключения USB-C на более современных гаджетах.
Типы разъемов
Версий разъемов USB несколько, и для каждого есть свое предназначение.
- type-А — клавиатуры, флешки, мышии т. п.
- type-B — офисная техника (принтеры, сканеры) и т. п.
- mini type-B — кардридеры, модемы, цифровые камеры и т. п.
- micro type-B — была наиболее распространенной в последние годы . Большинство смартфонов использовали именно этот тип подключения, пока не появился type-C. До сих пор остается довольно актуальным.
- type-C — наиболее актуальный и перспективный разъем, полностью симметричный и двухсторонний. Появился одновременно со стандартом USB 3.1 и актуален для более поздних версий стандартов USB.
Superspeed, Gen или как разобраться в маркировках стандартов USB
Как только в типах стандартов появилась USB 3.1, привычная цифровая маркировка изменилась и здорово запуталась. Вполне понятный и простой USB 3.0 автоматически превратился в USB 3.1 Gen 1 и ему была присвоена маркировка SuperSpeed. А непосредственно сам USB 3.1 стал называться USB 3.1 Gen 2 с маркировкой SuperSpeed +.
Но и это уже потеряло свою актуальность с выходом стандарта USB 3.2. Он получил название USB 3.2 Gen 2×2 и маркировку SuperSpeed ++. В итоге маркировка всех предшествующих стандартов опять меняется. Теперь USB 3.0, она же USB 3.1 Gen 1, превращается задним числом в USB 3.2 Gen 1 с прежней маркировкой SuperSpeed. А USB 3.1, ставшая USB 3.1 Gen 2, тоже поднялась до USB 3.2 Gen 2. При этом конструктивно все стандарты остались прежними — изменяются только названия. Если вы уже запутались во всех этих цифрах и маркировках, таблица ниже поможет внести ясность в актуальных названиях.
Если еще более кратко, то сейчас опознать стандарты USB можно так:
USB 3.0 — это USB 3.2 Gen 1, он же Superspeed
USB 3.1 — это USB 3.2 Gen 2, он же Superspeed+
USB 3.2 — это USB 3.2 Gen 2x2, он же Superspeed++
Коротко о USB - откуда все начинается. D+ и D- это дифференциальная пара, данные передаются в противофазе с одной лишь целью уменьшить помехи. То есть линия передачи по сути одна ! Есть ведущее устройство (Хост) и ведомое (Device).
Ведущее и ведомое могут одновременно что-то посылать в канал. Поэтому протокол USB очень требовательно распределяет , что ведущий и когда посылает и что (и когда) ведомый должен ответить. Иначе никак нельзя.
Вот на картинке ниже все отчетливо видно (один пакет от ведомого):
Сначала все просто:
Пакет всегда начинается с SYN (10000000).
Завершается пакет всегда EOP (End Of Packet ) . На картинке выше видна единственная ассиметрия в конце пакета, когда : 2 линии DP и DM различаются.
Примерная последовательность пакетов.
Инициализацию устройства пропускаем (запрос дескриптора, интерфейсов , конечных точек и т.д.), чтобы не терять времени (переходим к сути).
Периодические пакеты "НЕ СПАТЬ" SOF (Start Of Frame) - это примерно 1раз/1мс посылка от хоста ведомому ("не спи"). Их лучше сразу как-то отфильтровывать для понимания.
Далее остается три типа пакетов типа . Ниже их PID (Packet Identificator) , он же токен :
SETUP это служебные пакеты стандартного протокола настройки устройства
OUT это хост передает данные
IN это хост запрашивает данные от девайса
Эти пакеты вкладываются между SYNK и EOP .
Получается примерно такая структура [SYNC] [PID] [Address(7 бит)] [EndPoint] (4 бит) [EOP ]. На картинке выше видно как девайс отвечает NAK практически сразу и это нормально. Это означает , что девайсу надо подумать и сразу он не может ответить на команду.
PID это токен или (Program Identificator) SETUP, IN , OUT.
Address - это адрес нашего устройства на шине USB . Сначала он всегда 0 после подключения USB. Потом хост перенумеровывает все устройства на шине и присваивает каждому устройству уникальный адрес (размер всего 1 байт).
EndPoint - хост всегда общается не просто с устройством по адресу , а еще и с конкретной конечной точкой (end-point) устройства , которых может быть несколько. Как же хост узнает какие значения у конечных точек (EP) ? Правильно для этого зарезервировано значение 0 (конечная точка EP0), служебный end-point , через который хост получает первичную информацию от других конечных точках. Как всегда все просто.
Допустим наш хост уже получил всю информацию о конечных точках , интерфейсах, конфигурациях через EP0.
Как происходит дальше работа на примере обычной клавиатуры
Хост долбит периодически PID IN по адресу устройства плюс Endpoint устройства (у нас EndP 0x01), который отвечает за прием данных от клавиатуры (IN для хоста).
Если никакая клавиша не нажата ведомый обязан ответить и отвечает NAK. Такие пакеты хост передает примерно 1 раз в 10ms и устройство если не нажата клавиша передает NAK.
А вот когда на клавиатуре нажимается какая-нибудь клавиша, ведомый ответит сначала DATA0 пакетом и следом пакет ACK.
Количество передаваемых байт в DATA0 зависит от типа клавиатуры, то есть каждый решает сколько использовать байт для передачи скан кода нажатой клавиши. Клавиатура сообщает по стандартному протоколу через EP0 о своих настройках.
Тут есть нюанс , что хост всегда посылает запрос устройству на конкретный EP. Если запрос идет на EP для передачи данных (у нас EP1 ) это одно , если запрос идет на служебный EP0 - это хост хочет подключить , настроить устройство. То есть хост всегда определяет логику обмена , а девайс обязан подстраиваться под запрос.
Вообще кто есть хост? Это драйвер например клавиатура или сетевого адаптера и у каждого драйвера соответственно свой протокол , своя логика.
Таким образом если вы разрабатываете USB устройство и ПК шлет вам все пакеты на EP0 , а до других EP не доходит дело, то значит что-то еще не закончено с настройками устройства, что-то хосту не нравится.
Хост вообще говоря может ждать ответ одновременно от 2 и более конечных точек . Это абсолютно нормально. Выглядит это в логах анализатора LA1010 примерно так:
Видно как хост тупо чередует EP0 и EP2.
Если не возникает какого-то прерывания у девайса
То есть если на шине пакеты бегут, а прерывание необходимое не возникает. Например тупо не возникает прерывание IN bulk у RNDIS адаптера (DataIn у EP2). То есть на шине вижу , что девайс отсылает NAK на IN EP2, но самого прерывания в девайсе не возникает.
Тут надо в регистры лезть и отсрочки уже не будет. Какие мысли возникают в первую очередь. Прерывания маскируются вроде (надо проверить).
Так как у нас есть один рабочий проект но без FreeRTOS , то сначала тупо начинаем сверять регистры USB ( OTG_FS_GLOBAL и OTG_FS_DEVICE ): после инициализации , после открытия конечных точек, после приема нужного пакета и т.д. Эти регистры кстати удобно просматривать на закладке SFRS (в Atollic true Studio), тут видна их внутренняя структура. И еще с момента последней точки остановки подсвечиваются изменения.
В процессе сверки регистров мы находим отличия в OTG_FS_GLOBAL, исправляем, заодно изучаем назначение регистров и в какой-то момент даже ловим __HAL_PCD_IS_INVALID_INTERRUPT (на картинке выше видно). Ура хоть что-то.
На самом деле не знач - не ведая мы подошли к главному моменту. Мы наконец-то обратили внимание на USBD_LL_Init, а точнее на загадочные функции HAL_PCDEx_SetRxFiFo(..) и HAL_PCDEx_SetTxFiFo(..) .
Момент истины
И выяснилось , что мы не понимаем и половины логики работы USB . Не зная регистры вообще нет возможности понять что делать. В данном случае HAL это вред.
Итак HAL_PCDEx_SetRxFiFo / HAL_PCDEx_SetTxFiFo создает таблицу во внутренней памяти контроллера USB. Да именно контроллера USB , а не контроллера STM32. Так как у STM32F имеется как-бы свой встроенный контроллер , отвечающий за USB. И у него есть своя память 512К, в которой надо создать таблицу с буферами приема / передачи для каждой конечной точки.
Где эта таблица, где ее адреса.
А вот сама структура USB_OTG_GlobalTypeDef .
HAL - кий код становится намного прозрачнее теперь.
Опять момент истины
Дальше , если интересно немного о передаваемых скан кода клавиатуры . Проводная клавиатура Low Speed
Скан коды USB HID клавиатур это не ASCII коды и не не коды PS/2 клавы.
Появилось немного свободного времени, и я решил написать небольшую «внеплановую» статью.
Итак, из предыдущей статьи, мы знаем, что для обмена данными используются некие виртуальные каналы – «конечные точки». Давайте рассмотрим, как происходит обмен.
Обмен данными по USB
Нужно помнить, что интерфейс USB предусматривает использование разветвлителей – хабов. Более того, допускается каскадное включение хабов. Следовательно, необходимо как-то идентифицировать конкретное USB устройство в «гирлянде» из хабов и USB устройств. Для этого каждому устройству присваивается адрес.
Здесь остановимся немного подробнее. Адрес кодируется 7 битами. Изначально (в момент подключения), устройство, грубо говоря, само себе назначает адрес 0. Этот адрес зарезервирован стандартом как раз для вновь подключаемых устройств. Далее, в процессе инициализации (об этом поговорим позже), хост присваивает устройству уникальный адрес отличный от 0, а адрес 0 «освобождается» для вновь подключаемых устройств.
И так, для того чтобы передать данные конкретному устройству, нужно знать адрес устройства и номер виртуального канала «внутри» устройства (адрес «конечной точки»).
Как мы уже выяснили, сразу после включения, устройство имеет особый, «нулевой» адрес. Каждое устройство, согласно стандарту, имеет «нулевую конечную точку» типа Control. Соответственно, сразу после подключения, хост может начинать обмениваться данными с новым устройством (адрес = 0, номер конечной точки = 0).
Рассмотрим, как происходит обмен данными.
Сам обмен осуществляется пакетами. Стандартом предусмотрено несколько типов пакетов. «Побайтно» мы пока разбирать пакеты не будем, но коснемся этого вопроса в практической части.
Дело в том, что часть работы по формированию и передаче пакетов (например, вопросы синхронизации, расчет контрольных сумм и т. д.) возьмет на себя USB периферия МК. Для тех, кто хочет сразу углубиться в биты и байты могу порекомендовать ознакомиться с разделом 8 официальной спецификации USB 2.0
Пока нам достаточно знать, что существуют «пакеты данных» и несколько типов «служебных пакетов».
USB и Plug and Play
Давайте рассмотрим, что с нашим устройством будет происходить дальше, после того как хост определил подключение нового устройства и готов начать обмен данными. Нам нужно ненадолго подняться на «высокий» уровень – уровень ОС.
Дело в том, что в стандарт USB поддерживает концепцию Plug and Play (подключи и играй). Данная концепция подразумевает, что пользователю достаточно «воткнуть» устройство в соответствующий порт ПК. Дальше ОС автоматически определит тип подключенного устройства, найдет подходящий для данного устройства драйвер, сконфигурирует устройство и т. д. (правда, это конечно в идеале :))
Для того чтобы вся эта красота работала, стандартом USB предусмотрены некие общие требования для всех устройств:
1. Каждое устройство содержит «собственное описание» (дескриптор устройства).
2. Есть некий, общий для всех USB устройств, механизм который позволяет ОС прочитать дескриптор устройства для того, чтобы идентифицировать устройство, узнать его характеристики.
3. Есть некий, общий для всех USB устройств, механизм который позволяет ОС выполнить первичную конфигурацию устройства (например, присвоить устройству новый адрес, о чем мы говорили выше).
Данными вещами (чтение дескриптора устройства, идентификация устройства) занимается некая служба ОС, которая отвечает за базовую поддержку USB.
После того как устройство будет идентифицировано и проведена некая первичная инициализация, данная служба передаст управление устройством драйверу, который «закреплен» за данным типом устройств (или конкретно за этим устройством).
Что будет, если служба не сможет найти «подходящий» драйвер для данного устройства знают все :)
Теперь возвращаемся на наш «низкий» уровень.
Начало работы с устройством. Стандартные заросы.
На практике, для чтения дескриптора устройства и первичной инициализации используются та самая «нулевая конечная точка». Есть несколько предусмотренных стандартом запросов (Standard Device Requests), которые должны обрабатываться всеми USB устройствами. Пока приведу несколько примеров таких запросов:
GET_DESCRIPTOR – запрос на получения дескриптора устройства. Данный запрос содержит дополнительную информацию о том, какой именно дескриптор должно вернуть (в устройстве «хранится» несколько разных дескрипторов, но об этом позже).
GET_CONFIGURATION – запрос на получение текущей конфигурации устройства.
SET_ADDRESS – данный запрос используется для присвоения устройству «нормального» (отличного от 0) адреса. Сам адрес содержится в запросе.
Нужно понимать, что запрос — это не более чем стандартизированная структура данных, которая содержит код запроса (bRequest) и дополнительные данные. Ответы на каждый из запросов тоже, естественно, стандартизированы.
Кроме стандартных запросов, которые устройство «обязано» поддерживать, можно определить «свои» запросы, специфические для конкретного устройства (класса устройств).
Заодно, для того чтобы показать как выглядит тот самый дескриптор устройства приведу пример:
Пока это просто иллюстрация того, как выглядит дескриптор, вникать в значение полей не стоит, этим мы займемся в следующей статье.
На этом, предлагаю завязывать с голой теорией и постепенно переходить к практике. В следующей статье начнем потихоньку писать код.
Сегодняшняя статья будет посвящена, как уже видно из названия, обсуждению основ интерфейса USB. Рассмотрим основные понятия, структуру данных, разберемся, как происходит передача данных, а в ближайшем будущем реализуем все это на практике. Приступаем!
Существует ряд различных спецификаций USB. Началось все с USB 1.0 и USB 1.1, затем интерфейс эволюционировал в USB 2.0, относительно недавно появилась окончательная спецификация USB 3.0. Но на данный момент наиболее распространенной является реализация USB 2.0.
Ну и для начала основные моменты и характеристики. Интерфейс USB 2.0 поддерживает три режима работы:
Командует на шине USB хост (например, ПК), к которому можно подключить до 127 различных устройств. Если этого мало, то нужно добавить еще один хост. Причем немаловажно, что устройство не может само послать/принять данные хосту/от хоста, необходимо, чтобы хост сам обратился к устройству.
Помимо изображенных на рисунке, существуют также другие варианты исполнения USB-коннекторов, например, mini-USB и другие, ну это вы и так знаете 🙂
Отдельно стоит обсудить питание устройств USB. И тут также возможно несколько вариантов.
Во-первых устройства могут питаться от шины, тогда их можно разделить на два класса:
Разница тут заключается в том, что low-power устройства не могут потреблять больше, чем 100 мА. А устройства high-power должны потреблять не более 100 мА лишь на этапе конфигурации. После того, как они сконфигурированы хостом их потребление может составлять до 500 мА.
Кроме того, устройства могут иметь свой собственный источник питания. В этом случае они могут получать до 100 мА от шины, а все остальное забирать у своего источника.
С этим вроде бы все, давайте потихоньку переходить к структуре передаваемых данных. Все-таки это представляет для нас наибольший интерес!
Структура данных интерфейса USB.
Вся информация передается кадрами, которые отправляются через равные промежутки времени. В свою очередь каждый кадр состоит из транзакций. Вот, пожалуй, так будет нагляднее:
Каждая транзакция имеет следующий вид:
Пакеты Token бывают трех типов:
Пакет In сообщает нашему USB-устройству, что хост готов принять от него информацию. Пакет Out, напротив, сигнализирует о готовности и желании хоста поделиться информацией. Пакет Setup нужен для использования управляющих передач. Ну а пакет Start Of Frame используется для того, чтобы инициировать начало кадра.
Тут есть еще один важный момент. PID включает в себя 4 бита, но при передаче они дополняются еще 4-мя битами, которые получаются путем инвертирования первых 4-ых бит.
Тут все в принципе так же, как и в пакете Token, только вместо адреса устройства и номера конечной точки здесь у нас передаваемые данные.
Осталось нам рассмотреть Status пакеты и пакеты SOF:
Тут PID может принимать всего лишь два значения:
И, наконец, Start Of Frame пакеты:
Давайте в качестве примера рассмотрим процесс записи данных в USB-устройство. То есть рассмотрим пример структуры кадра записи.
Кадр, как вы помните состоит из транзакций и имеет следующий вид:
Что представляют из себя все эти транзакции? Сейчас разберемся! Транзакция SETUP:
Аналогично при чтении данных из USB-устройства кадр выглядит так:
Транзакцию SETUP мы уже видели, посмотрим на транзакцию IN:
Как видите, все эти транзакции имеют такую структуру, как мы обсуждали выше 🙂
В общем, думаю достаточно на сегодня. Довольно-таки длинная статья получилась, в ближайшее время обязательно попробуем реализовать интерфейс USB на практике!
Читайте также: