Лекция
Это окончание невероятной информации про usb.
...
разрешено устройство или нет. В качестве статуса на хост возвращается пакет нулевой длины, что сигнализирует ему об успешном завершении команды. Однако хост должен отправить токен IN, получить пакет нулевой длины и выдать ACK перед тем, как мы можем поменять адрес. Иначе устройство может не увидеть токена IN, отправленный на адрес по умолчанию (нулевой адрес).
Завершение стадии status сигнализируется прерыванием на конечной точке EP0 IN. Чтобы разделить ответ на установку адреса и обычное прерывание EP0_IN, мы установим переменную CtlTransferInProgress в значение PROGRESS_ADDRESS. В обработчике EP0 IN проверяется переменная CtlTransferInProgress. Если она равна PROGRESS_ADDRESS, то для PDIUSBD11 выдается команда Set Address Enable и CtlTransferInProgress устанавливается в значение PROGRESS_IDLE. Хост дает 2 мс на смену адреса устройством, перед тем, как отправить какую-нибудь другую команду.
case GET_DESCRIPTOR: GetDescriptor(&SetupPacket); break; case GET_CONFIGURATION: D11WriteEndpoint(D11_ENDPOINT_EP0_IN, &DeviceConfigured, 1); break; case SET_CONFIGURATION: printf("Set Configuration\n\r"); DeviceConfigured = SetupPacket.wValue & 0xFF; D11WriteEndpoint(D11_ENDPOINT_EP0_IN, NULL, 0); if (DeviceConfigured) { RB3 = 0; printf("\n\r *** Device Configured *** \n\r"); } else { RB3 = 1; /* устройство не сконфигурировано */ printf("\n\r ** Device Not Configured *** \n\r"); } break; //case SET_DESCRIPTOR: default: /* не поддерживается - ошибка запроса - остановка Stall */ ErrorStallControlEndPoint(); break; } break;
Запросы Get Configuration и Set Configuration используются для того, чтобы "разрешить" устройство USB, что позволит передавать данные на конечные точки, отличные от конечной точки 0. Set Configuration должен быть выдан с полем wValue, равным соответствующему значению поля bConfigurationValue нужной конфигурации, которую Вы хотите разрешить. В нашем случае имеется только одна конфигурация – конфигурация 1. Нулевое значение для конфигурации означает, что устройство не сконфигурировано, а ненулевое значение означает, что устройство сконфигурировано. Код не делает полную проверку типа значения конфигурации, он просто копирует значение в локальную переменную для сохранения - DeviceConfigured. Если поле wValue не соответствует полю bConfigurationValue конфигурации, то необходимо возвратить USB Request Error.
case STANDARD_INTERFACE_REQUEST: printf("Standard Interface Request\n\r"); switch (SetupPacket.bRequest) { case GET_STATUS: /* получение возвращаемого Status Request для интерфейса */ /* нули (зарезервировано для дальнейшего использования) */ Buffer = 0x00; Buffer = 0x00; D11WriteEndpoint(D11_ENDPOINT_EP0_IN, Buffer, 2); break; case SET_INTERFACE: /* по умолчанию поддерживается только устройство, состояние Stall возможно */ /* returned in the status stage of the request */ if (SetupPacket.wIndex == 0 && SetupPacket.wValue == 0) /* Interface Zero, Alternative Setting = 0 */ D11WriteEndpoint(D11_ENDPOINT_EP0_IN, NULL, 0); else ErrorStallControlEndPoint(); break; case GET_INTERFACE: if (SetupPacket.wIndex == 0) { /* Interface Zero */ Buffer = 0; /* Alternative Setting */ D11WriteEndpoint(D11_ENDPOINT_EP0_IN, Buffer, 1); break; } /* иначе падение по RequestError */ //case CLEAR_FEATURE: //case SET_FEATURE: /* На интерфейсе нет заданных опций (features). Возврат RequestError */ default: ErrorStallControlEndPoint(); break; } break;
Для стандартных запросов к интерфейсу (Standard Interface Requests) не выполняется никаких реальных функций. Запрос Get Status должен вернуть нулевое слово – зарезервировано для использования в будущем. Запросы Set Interface и Get Interface используются с альтернативными дескрипторами интерфейса. У нас не заданы никакие альтернативные дескрипторы интерфейса, поэтому Get Interface возвращает 0 и любой запрос на установку интерфейса, не равного интерфейсу 0 с альтернативной установкой 0 обрабатывается с ошибкой запроса Request Error.
case STANDARD_ENDPOINT_REQUEST: printf("Standard Endpoint Request\n\r"); switch (SetupPacket.bRequest) { case CLEAR_FEATURE: case SET_FEATURE: /* особенность Halt(Stall) нужно реализовать на всех прерываниях и */ /* конечных точек Bulk. Это не требуется, не рекомендовано на Default Pipe (канал по умолчанию) */ if (SetupPacket.wValue == ENDPOINT_HALT) { if (SetupPacket.bRequest == CLEAR_FEATURE) Buffer = 0x00; else Buffer = 0x01; switch (SetupPacket.wIndex & 0xFF) { case 0x01: D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + \ D11_ENDPOINT_EP1_OUT, Buffer, 1); break; case 0x81: D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + \ D11_ENDPOINT_EP1_IN, Buffer, 1); break; case 0x02: D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + \ D11_ENDPOINT_EP2_OUT, Buffer, 1); break; case 0x82: D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + \ D11_ENDPOINT_EP2_IN, Buffer, 1); break; case 0x03: D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + \ D11_ENDPOINT_EP3_OUT, Buffer, 1); break; case 0x83: D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + \ D11_ENDPOINT_EP3_IN, Buffer, 1); break; default: /* Invalid Endpoint - RequestError */ ErrorStallControlEndPoint(); break; } D11WriteEndpoint(D11_ENDPOINT_EP0_IN, NULL, 0); } else { /* Нет других опций на конечной точке - ошибка запроса */ ErrorStallControlEndPoint(); } break;
Запросы Set Feature и Clear Feature используются для специфичных фич конечных точек. Стандарт задает один селектор фичи - ENDPOINT_HALT. Мы соответственно проверяем запрос на установку/сброс бита STALL. Фича HALT не требуется для конечной точки по умолчанию (конечной точки 0).
case GET_STATUS: /* Получение Status Request для конечной точки, который нужно вернуть */ /* Halt Status в D0 для Interrupt и Bulk */ switch (SetupPacket.wIndex & 0xFF) { case 0x01: D11CmdDataRead(D11_READ_ENDPOINT_STATUS + \ D11_ENDPOINT_EP1_OUT, Buffer, 1); break; case 0x81: D11CmdDataRead(D11_READ_ENDPOINT_STATUS + \ D11_ENDPOINT_EP1_IN, Buffer, 1); break; case 0x02: D11CmdDataRead(D11_READ_ENDPOINT_STATUS + \ D11_ENDPOINT_EP2_OUT, Buffer, 1); break; case 0x82: D11CmdDataRead(D11_READ_ENDPOINT_STATUS + \ D11_ENDPOINT_EP2_IN, Buffer, 1); break; case 0x03: D11CmdDataRead(D11_READ_ENDPOINT_STATUS + \ D11_ENDPOINT_EP3_OUT, Buffer, 1); break; case 0x83: D11CmdDataRead(D11_READ_ENDPOINT_STATUS + \ D11_ENDPOINT_EP3_IN, Buffer, 1); break; default: /* Invalid Endpoint - RequestError */ ErrorStallControlEndPoint(); break; } if (Buffer & 0x08) Buffer = 0x01; else Buffer = 0x00; Buffer = 0x00; D11WriteEndpoint(D11_ENDPOINT_EP0_IN, Buffer, 2); break; default: /* Unsupported - Request Error - Stall */ ErrorStallControlEndPoint(); break; } break;
Запрос Get Status, направленный на конечную точку, возвращает статус конечной точки, т. е. остановлена ли она (halted), или нет. Как и в запросе Set/Clear feature ENDPOINT_HALT, нам нужно всего лишь сообщить о статусе обычных (не равных 0) конечных точек.
Любые неопределенные стандартные запросы к конечным точкам (Standard Endpoint Request) обрабатываются с ошибкой запроса USB Request Error.
case VENDOR_DEVICE_REQUEST: case VENDOR_ENDPOINT_REQUEST: printf("Vendor Device bRequest = 0x%X, wValue = 0x%X, wIndex = 0x%X\n\r", \ SetupPacket.bRequest, SetupPacket.wValue, SetupPacket.wIndex); switch (SetupPacket.bRequest) { case VENDOR_GET_ANALOG_VALUE: printf("Get Analog Value, Channel %x :",SetupPacket.wIndex & 0x07); ADCON0 = 0xC1 | (SetupPacket.wIndex & 0x07) << 3; /* Wait Acquistion time of Sample and Hold */ for (a = 0; a <= 255; a++); ADGO = 1; while(ADGO); Buffer = ADRESL; Buffer = ADRESH; a = (Buffer << 8) + Buffer ; a = (a * 500) / 1024; printf(" Value = %d.%02d\n\r",(unsignedint)a/100,(unsigned int)a%100); D11WriteEndpoint(D11_ENDPOINT_EP0_IN, Buffer, 2); break;
Теперь мы подошли (наконец!) к функциональной части устройства USB. Запросы (Vendor Requests) выдумываются самим разработчиком. Мы придумали 2 запроса, VENDOR_GET_ANALOG_VALUE и VENDOR_SET_RB_HIGH_NIBBLE. VENDOR_GET_ANALOG_VALUE читает 10-битное значение напряжения ADC канала x, указанного в поле wIndex. На значение x мы накладываем по AND маску 0x07, что разрешает 8 возможных номеров каналов (что поддерживает более крупный чип PIC16F877, если это нужно). Аналоговая величина напряжения возвращается в 2 байтах пакета данных (примечание переводчика – от фазы данных можно было бы отказаться, передавая эти 2 байта в wValue или wIndex. В этом случае wLength нужно указать не 2, а 0).
case VENDOR_SET_RB_HIGH_NIBBLE: printf("Write High Nibble of PORTB\n\r"); PORTB = (PORTB & 0x0F) | (SetupPacket.wIndex & 0xF0); D11WriteEndpoint(D11_ENDPOINT_EP0_IN, NULL, 0); break; default: ErrorStallControlEndPoint(); break; } break;
VENDOR_SET_RB_HIGH_NIBBLE может быть использован для установки бит старшего ниббла PORTB[3:7].
default: printf("UnSupported Request Type 0x%X\n\r", SetupPacket.bmRequestType); ErrorStallControlEndPoint(); break; } } else { printf("Data Packet?\n\r"); /* Это пакет данных (Data Packet) */ } }
Любые не поддерживаемые типы запросов, такие как class device request, class interface request и т. д. обрабатываются с ошибкой запроса USB Request Error.
void GetDescriptor(PUSB_SETUP_REQUEST SetupPacket) { switch((SetupPacket->wValue & 0xFF00) >> 8) { case TYPE_DEVICE_DESCRIPTOR: printf("\n\rDevice Descriptor: Bytes Asked For %d, Size of Descriptor %d\n\r", \ SetupPacket->wLength,DeviceDescriptor.bLength); pSendBuffer = (const unsigned char *)&DeviceDescriptor; BytesToSend = DeviceDescriptor.bLength; if (BytesToSend > SetupPacket->wLength) BytesToSend = SetupPacket->wLength; WriteBufferToEndPoint(); break; case TYPE_CONFIGURATION_DESCRIPTOR: printf("\n\rConfiguration Descriptor: Bytes Asked For %d, Size of Descriptor %d\n\r", \ SetupPacket->wLength,sizeof(ConfigurationDescriptor)); pSendBuffer = (const unsigned char*)&ConfigurationDescriptor; BytesToSend = sizeof(ConfigurationDescriptor); if (BytesToSend > SetupPacket->wLength) BytesToSend = SetupPacket->wLength; WriteBufferToEndPoint(); break;
Запросы Get Descriptor влекут за собой ответы размером больше, чем лимит на максимальный размер пакета 8 байт для конечной точки. Таким образом, ответ должен быть разбит на куски по 8 байт. Оба запроса Device и Configuration загружают адрес соответствующих дескрипторов в pSendBuffer, и устанавливают BytesToSend в значение длины дескриптора. Запрос будет также указывать длину дескриптора в поле wLength, что дает максимум данных для отправки. В любом случае мы проверяем действительную величину, запрашиваемую от хоста, и обрезаем размер по необходимости. Затем делается вызов WriteBuffertoEndpoint, который загружает первые 8 байт в буфер конечной точки и увеличивает указатель, чтобы быть готовым к следующему пакету из 8 байт.
case TYPE_STRING_DESCRIPTOR: printf("\n\rString Descriptor: LANGID = 0x%04x, Index %d\n\r", \ SetupPacket->wIndex, SetupPacket->wValue & 0xFF); switch (SetupPacket->wValue & 0xFF) { case 0: pSendBuffer = (const unsigned char*)&LANGID_Descriptor; BytesToSend = sizeof(LANGID_Descriptor); break; case 1: pSendBuffer = (const unsigned char*)&Manufacturer_Descriptor; BytesToSend = sizeof(Manufacturer_Descriptor); break; default: pSendBuffer = NULL; BytesToSend = 0; } if (BytesToSend > SetupPacket->wLength) BytesToSend = SetupPacket->wLength; WriteBufferToEndPoint(); break;
Если имеется любой строковый дескриптор, должен присутствовать строковый дескриптор 0, описывающий языки, которые поддерживает устройство. Любые запросы строковых дескрипторов с ненулевыми номерами имеют LanguageID в поле wIndex, который говорит о том, какой язык поддерживается. В нашем случае мы немного обманываем и игнорируем значение wIndex (LANGID) возвращаемый в строке, независимо от того, какой язык запрашивался.
default: ErrorStallControlEndPoint(); break; } } void ErrorStallControlEndPoint(void) { unsigned char Buffer[] = { 0x01 }; /* 9.2.7 RequestError - возврат STALL PID в ответ на слеующую DATA Stage Transaction */ D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP0_IN, Buffer, 1); /* или в status stage сообщения. */ D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP0_OUT, Buffer, 1); }
Когда мы сталкиваемся с неправильным запросом, параметром, или запросом, который устройство не поддерживает, мы должны сообщить об ошибке запроса (request error). Это задано в части 9.2.7 стандарта. Request error возвращает STALL PID в ответ на следующие данные стадии транзакции или стадии статуса сообщения. Однако чтобы предотвратить нежелательный трафик шины, сообщение должно возвращаться на следующей стадии данных, вместо того чтобы ждать до стадии состояния (status stage).
unsigned char D11ReadEndpoint(unsigned char Endpoint, unsigned char*Buffer) { unsigned char D11Header ; unsigned char BufferStatus = 0; /* Выбор конечной точки */ D11CmdDataRead(Endpoint, &BufferStatus, 1); /* Проверка - заполнен ли буфер */ if(BufferStatus & 0x01) { /* Чтение dummy-заголовка - указатель на буфер D11 инкрементируется при каждом чтении и сбрасывается только командой Select Endpoint */ D11CmdDataRead(D11_READ_BUFFER, D11Header, 2); if(D11Header ) D11CmdDataRead(D11_READ_BUFFER, Buffer, D11Header ); /* Разрешить принимать новые пакеты. */ D11CmdDataWrite(D11_CLEAR_BUFFER, NULL, 0); } return D11Header ; } void D11WriteEndpoint(unsigned char Endpoint, const unsigned char*Buffer, unsigned char Bytes) { unsigned char D11Header ; unsigned char BufferStatus = 0; D11Header = 0x00; D11Header = Bytes; /* Выбор конечной точки */ D11CmdDataRead(Endpoint, &BufferStatus, 1); /* Запись заголовка */ D11CmdDataWrite(D11_WRITE_BUFFER, D11Header, 2); /* Запись пакета */ if (Bytes) D11CmdDataWrite(D11_WRITE_BUFFER, Buffer, Bytes); /* Проверка буфера на валидность */ D11CmdDataWrite(D11_VALIDATE_BUFFER, NULL, 0); }
Функции D11ReadEndpoint и D11WriteEndpoint специфичны для микросхемы PDIUSBD11. PDIUSBD11 имеет два dummy – байта, которые являются префиксом к любой операции чтения или записи данных. Первый байт зарезервирован, а второй байт показывает количество принимаемых или передаваемых байт. Эти две функции обеспечены своим заголовком.
void WriteBufferToEndPoint(void) { if (BytesToSend == 0) { /* Если BytesToSend равна 0, и процедура была вызвана снова, то предположите, что буфер меньше, чем размер Setup Request, и покажите конец отправкой пакета нулевой длины (Zero Lenght packet) */ D11WriteEndpoint(D11_ENDPOINT_EP0_IN, NULL, 0); } else if (BytesToSend >= 8) { /* Запись других 8 байт в буфер, и отправка */ D11WriteEndpoint(D11_ENDPOINT_EP0_IN, pSendBuffer, 8); pSendBuffer += 8; BytesToSend -= 8; } else { /* Буфер должен иметь в запасе меньше 8 байт. */ D11WriteEndpoint(D11_ENDPOINT_EP0_IN, pSendBuffer, BytesToSend); BytesToSend = 0; } }
Как мы уже упоминали, функция WriteBufferToEndPoint отвечает за загрузку данных в микросхему PDIUSBD11 порциями по 8 байт и подстройку указателей для готовности к следующему пакету. Функция вызывается один раз обработчиком запроса для загрузки первых 8 байт в буфер конечной точки. Затем хост посылает токен IN, читает эти данные и PDIUSBD11 генерирует прерывание. Обработчик конечной точки EP0 IN вызовет затем WriteBufferToEndpoint для загрузки следующего пакета в готовность к следующему токену IN от хоста.
Трансфер считается завершенным, если все запрошенные байты прочитаны, если принят пакет с полезной нагрузкой меньше, чем bMaxPacketSize или если возвращен пакет нулевой длины (zero length packet). Таким образом, если счетчик BytesToSend достигает 0, мы предполагаем что данные были отправлены несколько раз по 8 байт, и отравляем zero length packet, что указывает – это были последние данные. Однако если у нас осталось меньше чем 8 байт для отправки, мы посылаем только оставшиеся байты. Здесь не требуется дополнять данные нулями.
void loadfromcircularbuffer(void) { unsigned char Buffer[10]; unsigned char count; // Чтение состояния заполненности буфера D11CmdDataRead(D11_ENDPOINT_EP1_IN, Buffer, 1); if (Buffer == 0) { // Буфер пуст if (inpointer != outpointer) { // есть байты для отправки count = 0; do { Buffer[count++] = circularbuffer[outpointer++]; if (outpointer >= MAX_BUFFER_SIZE) outpointer = 0; if (outpointer == inpointer) break; // больше нет данных }while (count < 8); // максимальный размер буфера // Теперь загрузим это в EP1_In D11WriteEndpoint(D11_ENDPOINT_EP1_IN, Buffer, count); } } }
Процедура loadfromcircularbuffer() обрабатывает загрузку данных в буфер конечной точки EP1 IN. Процедура нормально вызывается после прерывания EP1 IN для перезагрузки буфера и для готовности к следующему токену IN на конечной точке EP1. Однако для того, чтобы отправить первый пакет, нам надо загрузить данные перед получением прерывания EP1 IN. Таким образом, процедура также вызывается после принятых данных в конечную точку EP1 OUT.
Вызывая также процедуру из обработчика EP1 OUT, мы возможно перезапишем данные в буфере IN независимо от того, были ли уже посланы предыдущие данные буфера IN, или нет. Чтобы это предотвратить, мы проверяем – пуст ли буфер EP1 IN перед попыткой загрузить его новыми данными.
void D11CmdDataWrite(unsigned char Command, const unsigned char*Buffer, unsigned char Count) { I2C_Write(D11_CMD_ADDR, &Command, 1); if(Count) I2C_Write(D11_DATA_ADDR_WRITE, Buffer, Count); } void D11CmdDataRead(unsigned char Command, unsigned char Buffer[], unsigned char Count) { I2C_Write(D11_CMD_ADDR, &Command, 1); if(Count) I2C_Read(D11_DATA_ADDR_READ, Buffer, Count); }
D11CmdDataWrite и D11CmdDataRead – две функции, специфичные для микросхемы PDIUSBD11. Они отвечают за работу интерфейса I2C – за отправку сначала команд Address/Command и затем отправки принятых данных по шине I2C. Дополнительные функции нижнего уровня добавлены в исходный код, но здесь не приведены, чтобы лучше сфокусироваться именно на специфике протокола USB.
Этот пример может использоваться вместе с примером bulkUSB.sys, который является частью Windows DDK. Для загрузки драйвера bulkUSB.sys либо поменяйте код для самоидентификации по VID 0x045E и PID 0x930A, либо поменяйте для bulkUSB.sys файл bulkUSB.inf в соответствии с применяемыми в этом примере VID/PID.
Теперь можно использовать консольную программу пользовательского режима rwbulk.exe для отправки пакетов из кольцевого буфера. Используйте следующую команду для отправки порций по 80 байт от PIC16F876:
rwbulk -r 80 -w 80 -c 1 -i 1 -o 0
Использование полезной нагрузки свыше 80 байт вызовет переполнение кольцевого буфера PIC в памяти BANK1.
Этот пример был закодирован для лучшей удобочитаемости в ущерб размеру кода. Он компилируется в 3250 слов памяти FLASH (39% емкости PIC16F876).
Благодарности
Выражаем особую признательность Michael DeVault из компании DeVaSys Embedded Systems . Этот пример базируется на коде, написанным Michael. Код легко был разработан на плате для USB разработки USBLPT-PD11 компании DeVaSys перед портированием на PIC.
Загрузка исходного кода
История ревизий
ADC Analog-to-Digital Converter, АЦП, аналого-цифровой преобразователь.
bit stuffing вставка бит по специальному алгоритму в последовательном потоке бит на линии. Применяется для целей синхронизации или уменьшения/устранения постоянной составляющей в сигнале.
bus powered devices устройства USB, получающие питание +5V от шины USB (линия VBUS). Питающее напряжение на линии VBUS формирует хост.
daisy chained термин, относящийся к линейной (иногда кольцевой) топологии сети. Иногда такую топологию называют шлейфовой. Дословный перевод daisy chain – «цепочка маргариток», что означает венок из маргариток.
downstream нисходящее соединение. Такое соединение имеет место для хоста по отношению к устройству USB.
Endpoint, конечная точка специфическое понятие стандарта USB, символизирующее источник или приемник потока данных.
EPx EndPoint (конечная точка) номер x.
errata исправления, добавления к стандарту.
Feature фича, какая-нибудь возможность (особенность) устройства.
Feature Selector селектор фичи, число, от которого зависит выбор какой-нибудь возможности (особенности) устройства.
FLASH энергонезависимая память для хранения программ или данных.
FrameWork фреймворк, рабочая среда, рабочее окружение.
handshaking «рукопожатие», процедура установления связи.
Host хост, главное устройство на шине. Обычно это компьютер.
ICD In Circuit Debug, внутрисхемная отладка.
ISR Interrupt Service Routine, подпрограмма обработки прерывания.
Latency латентность, время ожидания на обработку, время задержки.
LDO regulator, Low Dropout регулятор, имеющий маленькое падение напряжения, достаточное для нормальной работы (стабилизации).
OTP ROM однократно программируемая память (обычно для программы).
Padding дополнение байтами до нужного количества (обычно нулевыми байтами).
payload полезная нагрузка, передаваемые в потоке данные.
PCB печатная плата.
peer-to-peer возможность обмена данными между равноправными устройствами.
Pipe(s) буквальный перевод «труба» («трубы»). Означает специальным образом сформированные частные потоки данных через интерфейс USB. Иногда pipe называют каналом.
plug штеккер, коннектор на конце кабеля или на торце устройства USB (например, флешки). Этот коннектор втыкается в сокет, который обычно ставится на корпусе устройства (компьютера или периферии USB).
pull up, pull-up нагрузочный для сигнальной линии резистор, подключенный между линией сигнала и плюсом питания.
pull down, pull-down нагрузочный для сигнальной линии резистор, подключенный между линией сигнала и землей.
self powered devices устройства USB, питающиеся от отдельного источника (не от шины USB).
socket сокет, разъем на корпусе компьютера или периферии USB, куда втыкается plug (коннектор).
status reporting получение информации о статусе устройства.
tiered star топология многоярусная звезда.
token символ.
Token Packet символ, показывающий, какие дальше идут данные.
trade-off компромисс.
upstream восходящее соединение. Такое соединение имеет место для устройства USB по отношению к хосту.
Исследование, описанное в статье про usb, подчеркивает ее значимость в современном мире. Надеюсь, что теперь ты понял что такое usb и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Операционные системы и системное программировние
Часть 1 Все о USB , Программирование USB интерфейса и работа с USB периферии для программистов
Часть 2 Цоколевки разъема и кабеля - Все о USB , Программирование
Часть 3 Глава 4: Типы конечных точек (Endpoint Types ) - Все
Часть 4 Глава 5: Дескрипторы USB - Все о USB , Программирование
Часть 5 Глава 7 : обычный (Generic) драйвер USB - Все о
Часть 6 Термины - Все о USB , Программирование USB интерфейса и
Комментарии
Оставить комментарий
Операционные системы и системное программировние
Термины: Операционные системы и системное программировние