Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

Глава 7 : обычный (Generic) драйвер USB - Все о

Лекция



Это продолжение увлекательной статьи про usb.

...

http://microsin.ru/content/view/1084/44/.

Стандартные запросы

Секция 9.4 спецификации USB подробно описывают запросы "Standard Device", которые необходимо реализовать для каждого устройства USB. Стандарт предоставляет одну таблицу, группирующую запросы. Приняв во внимание, что большинство firmware анализирует пакет setup packet по получателю, мы можем разбить запросы на основе получателя для более простой обработки и реализации.

Стандартные запросы к устройству

В настоящий момент имеется 8 стандартных запросов к устройству, все они указаны в таблице ниже.

bmRequestType

bRequest

wValue

wIndex

wLength

Data

1000 0000b

GET_STATUS (0x00)

0

0

2

Статус устройства

0000 0000b

CLEAR_FEATURE (0x01)

Селектор фичи

0

0

Нет

0000 0000b

SET_FEATURE (0x03)

Селектор фичи

0

0

Нет

0000 0000b

SET_ADDRESS (0x05)

Адрес устройства

0

0

Нет

1000 0000b

GET_DESCRIPTOR (0x06)

Тип дескриптора и индекс

0 или ID языка

Длина дескриптора

Дескриптор

0000 0000b

SET_DESCRIPTOR (0x07)

Тип дескриптора и индекс

0 или ID языка

Длина дескриптора

Дескриптор

1000 0000b

GET_CONFIGURATION (0x08)

0

0

1

Значение конфигурации

0000 0000b

SET_CONFIGURATION (0x09)

Configuration Value

0

0

Нет

  • Запрос Get Status, направленный на устройство, вернет 2 байта на стадии данных в следующем формате:

D15

D14

D13

D12

D11

D10

D9

D8

D7

D6

D5

D4

D3

D2

D1

D0

Зарезервировано

Remote Wakeup

Self Powered

  • Если бит D0 (Self Powered) установлен, то это указывает на самопитаемое устройство (у устройства имеется собственный источник питания, и оно ничего не потребляет от шины USB), а если очищен, то устройство bus powered (питается от провода Vbus шины USB). Если бит D1 установлен, то у устройства разрешена возможность удаленного пробуждения хоста от спячки или приостановки (Remote Wakeup). Бит remote wakeup может быть изменен запросами SetFeature и ClearFeature с селектором фичи равным DEVICE_REMOTE_WAKEUP (0x01)
  • Запросы Clear Feature и Set Feature могут использоваться для сброса или установки булевых (boolean) фич. Когда получатель – устройство, то возможны только два селектора фичи DEVICE_REMOTE_WAKEUP и TEST_MODE. Режим тестирования позволяет устройству выдавать (показывать) некоторые условия. Они позже были документированы в Ревизии 2.0 спецификации USB.
  • Запрос Set Address используется во время энумерации для того, чтобы назначить уникальный адрес устройству USB. Адрес указывается в поле wValue и может принимать значение не более 127. Этот запрос уникален тем, что устройство не меняет свой адрес, пока не завершится стадия статуса (см. Control Transfers). Все другие запросы должны выполниться до стадии статуса.
  • Запросы Set Descriptor/Get Descriptor используются для возврата дескриптора, указанного в wValue. Запрос для дескриптора конфигурации вернет дескриптор устройства и все дескрипторы интерфейса и конечной точки в одном запросе. Endpoint Descriptors (дескрипторы конечной точки) не могут быть запрошены напрямую запросами GetDescriptor/SetDescriptor. Interface Descriptors (дескрипторы интерфейса) не могут быть запрошены напрямую запросами GetDescriptor/SetDescriptor. String Descriptors (строковые дескрипторы) включают Language ID (идентификатор языка) в поле wIndex – для поддержки нескольких языков.
  • Запросы Get Configuration/Set Configuration используются для запроса или установки текущей конфигурации устройства. В случае запроса Get Configuration будет возвращен байт на стадии данных, показывающий статус устройства. Нулевая величина показывает, что устройство не сконфигурировано, и ненулевое значение показывает, что устройство сконфигурировано. Set Configuration используется для разрешения (enable) устройства. Этот запрос должен содержать величину поля bConfigurationValue желаемого configuration descriptor (дескриптора конфигурации) в младшем байте wValue – для выбора разрешаемой конфигурации.

Стандартные запросы к интерфейсу

Спецификация в настоящий момент задает 5 стандартных запросов к интерфейсу, показанные в таблице ниже. Что интересно - только два запроса делают что-нибудь понятное.

bmRequestType

bRequest

wValue

wIndex

wLength

Data

1000 0001b

GET_STATUS (0x00)

0

Интерфейс

2

Состояние интерфейса

0000 0001b

CLEAR_FEATURE (0x01)

Селектор фичи

Интерфейс

0

Нет

0000 0001b

SET_FEATURE (0x03)

Селектор фичи

Интерфейс

0

Нет

1000 0001b

GET_INTERFACE (0x0A)

0

Интерфейс

1

Альтернативный интерфейс

0000 0001b

SET_INTERFACE (0x11)

Альтернативная установка

Интерфейс

0

Нет

  • Поле wIndex используется для указания ссылки на нужный интерфейс для запросов, направленных к интерфейсу. Формат поля следующий:

D15

D14

D13

D12

D11

D10

D9

D8

D7

D6

D5

D4

D3

D2

D1

D0

Зарезервировано

Номер интерфейса

  • Запрос Get Status используется для возврата статуса (состояния интерфейса). Такой запрос к интерфейсу должен возвратить два байта 0x00, 0x00 (оба байта зарезервированы для использования в будущем).
  • Запросы Clear Feature и Set Feature могут использоваться для очистки и сброса булевых фич. Когда назначенный получатель – интерфейс, спецификация USB ревизии 2 не указывает конкретных фич интерфейса.
  • Запросы Get Interface и Set Interface получают и устанавливают Alternative Interface (альтернативный интерфейс), который более подробно описан Interface Descriptor (дескриптором интерфейса).

Стандартные запросы к конечной точке

Запросы Standard Endpoint бывают 4-х видов, перечисленных в таблице ниже.

bmRequestType

bRequest

wValue

Windex

wLength

Data

1000 0010b

GET_STATUS (0x00)

0

Конечная точка

2

Состояние конечной точки

0000 0010b

CLEAR_FEATURE (0x01)

Селектор фичи

Конечная точка

0

Нет

0000 0010b

SET_FEATURE (0x03)

Селектор фичи

Конечная точка

0

Нет

1000 0010b

SYNCH_FRAME (0x12)

0

Конечная точка

2

Номер фрейма

  • Поле wIndex в запросах к конечной точке используется для указания целевой конечной точки и направления запросов. Формат поля следующий:

D15

D14

D13

D12

D11

D10

D9

D8

D7

D6

D5

D4

D3

D2

D1

D0

Зарезервировано

Dir
(направление)

Зарезервировано

Номер конечной точки

  • Запрос Get Status возвращает 2 байта, в которых возвращается статус конечной точки (пока только бит Halted/Stalled, т. е. бит приостановки). Формат двух возвращаемых байт показан ниже:

D15

D14

D13

D12

D11

D10

D9

D8

D7

D6

D5

D4

D3

D2

D1

D0

Зарезервировано

Halt

  • Запрос Clear Feature и Set Feature используется для установки фич конечной точки. Стандарт в настоящее время определяет один селектор фичи конечной точки ENDPOINT_HALT (0x00), который позволяет хосту приостановить и очистить конечную точку. Только оконечным точкам, кроме заданной по умолчанию конечной точки (конечная точка 0) рекомендуют иметь эти функциональные возможности.
  • Запрос Synch Frame используется, чтобы сообщить о кадре синхронизации конечной точки.

Глава 7 : обычный (Generic) драйвер USB

Энумерация

Энумерация – процесс определения факта, что устройство действительно подключено к шине USB и каких параметров это требует – потребляемая мощность, количество и тип конечной точки (или точек), класс устройства и т. д. В процессе энумерации хост назначает устройству адрес и разрешает конфигурацию, позволяющую устройству передавать данные по шине. Обычный процесс энумерации хорошо описан в секции 9.1.2 спецификации USB. Однако когда USB firmware пишется впервые, полезно знать не общий процесс энумерации, как он описан в стандарте, а то, как хост отвечает в процессе энумерации.

Общий процесс энумерации под операционной системой Windows включает в себя следующие шаги:

1. Хост или хаб детектирует подключение нового устройства с помощью pull-up резисторов, которое устройство подключает к паре сигнальных проводов данных (D+ и D-). Хост делает задержку как минимум 100 мс, что позволяет вставить коннектор полностью и застабилизировать питание устройства.
2. Хост выдает на шину сброс, который выводит устройство в состояние по умолчанию. Устройство может теперь ответить на заданный по умолчанию нулевой адрес.
3. Хост MS Windows запрашивает первые 64 байта дескриптора устройства (Device Descriptor).
4. После приема первых 8 байт дескриптора устройства, хост немедленно выдает новый сброс шины.
5. Теперь хост выдает команду Set Address, чем переводит устройство в адресуемое состояние.
6. Хост запрашивает все 18 байт дескриптора устройства.
7. Затем он запрашивает 9 байт дескриптора конфигурации (Configuration Descriptor), чтобы определить полный ее размер.
8. Хост запрашивает 255 байт дескриптора конфигурации.
9. Хост запрашивает все строковые дескрипторы (String Descriptors), если они имеются.

По окончании шага 9 Windows запросит драйвер для Вашего устройства. Обычно он снова запросит все дескрипторы, перед тем как выдаст запрос Set Configuration.

Вышеописанный процесс энумерации работает одинаково в Windows 2000, Windows XP and Windows 98 SE.

При написании firmware в первый раз шаг 4 часто вводит новичков в затруднение. Хост запрашивает первые 64 байта дескриптора устройства, а затем когда хост сбрасывает Ваше устройство после получения первых 8 байт, естественно думать, что что-то не так с Вашим дескриптором устройства, или что firmware неправильно обрабатывает запрос. Однако если Вы осуществили команду Set Address, то она сработает, и затем будут запрошены полные 18 байт дескриптора устройства.

Обычно если что-то неправильно с дескриптором или с тем, как он был отправлен, хост попытается прочитать его 3 раза с большой паузой между запросами. После третьей неудачной попытки хост «сдается» и сообщает об ошибке с Вашим устройством.

Глава 8: пример Firmware

Firmware - PIC16F876, управляющий PDIUSBD11

Мы начнем наши примеры с устройства фирмы Philips PDIUSBD11 (I2C Serial USB Device), подключенного к микроконтроллеру PIC16F876 компании MicroChip (показан на схеме) или Microchip PIC16F877 (чип в бОльшем корпусе на 40 ножек). Несмотря на то, что у Microchip есть два low speed USB микроконтроллера PIC16C745 и PIC16C765, у них память программ только OTP, и нет поддержки внутрисхемной отладки - In-Circuit Debugging (ICD), а это никак не способствует нормальной разработке firmware. В настоящий момент микросхема Philips PDIUSBD11, подключенная к PIC16F876, дает те же возможности - Flash и внутрисхемную отладку.

Все о USB  , Программирование  USB интерфейса и работа с  USB  периферии для программистов


Схема необходимого железа показана выше на рисунке. Пример проходит энумерацию и позволяет прочитать аналоговое напряжение с пяти мультиплексированных входов ADC микроконтроллера PIC16F876. Код совместим с PIC16F877, позволяющим максимум 8 аналоговых каналов. Светодиод LED, подключенный к ножке порта RB3, горит, когда устройство сконфигурировано. Регулятор 3.3V не показан, но он необходим для PDIUSBD11. Если Вы запускаете этот пример схемы от внешнего источника питания, то можете использовать обычный регулятор 78L033 на 3.3V, однако если Вы хотите запустить устройство в режиме Bus Powered USB device (устройство, питаемое от шины USB), то Вам нужен регулятор с низким падением напряжения.

Отладка может быть осуществлена путем соединения TXD (ножка 17) к драйверу RS-232 и подключения к компьютеру на скорости 115,200 бит/сек. Операторы printf добавлены для отображения процесса энумерации.

Код написан на C и скомпилирован компилятор Hi-Tech PICC Compiler . Имеется для загрузки демонстрационная версия компилятора PICC demo version (7.86 PL4), которая работает 30 дней. Скомпилированный HEX-файл имеется в архиве. Он скомпилирован для использования с ICD (или без него).

#include < pic.h >
#include < stdio.h >
#include < string.h >
#include "usbfull.h"

 
const USB_DEVICE_DESCRIPTOR DeviceDescriptor =
{
    sizeof(USB_DEVICE_DESCRIPTOR), /* bLength */
    TYPE_DEVICE_DESCRIPTOR,        /* bDescriptorType */
    0x0110,                        /* bcdUSB USB Version 1.1 */
    0,                             /* bDeviceClass */
    0,                             /* bDeviceSubclass */
    0,                             /* bDeviceProtocol */
    8,                             /* bMaxPacketSize 8 Bytes */
    0x04B4,                        /* idVendor (Cypress Semi) */
    0x0002,                        /* idProduct (USB Thermometer Example) */
    0x0000,                        /* bcdDevice */
    1,                             /* iManufacturer String Index */
    0,                             /* iProduct String Index */
    0,                             /* iSerialNumber String Index */
    1                              /* bNumberConfigurations */
};

Все структуры заданы в заголовочном файле (header, файл с расширением *.h). Мы получили этот пример на основе примера от Cypress - USB термометр, который Вы можете использовать вместе с USB Driver для Cypress USB Starter Kit . Новый generic-драйвер написан для поддержки этого и других примеров, которые могут также скоро появиться. Только одна строка представлена для отображения производителя. Это иллюстрирует, как реализовать строковые дескрипторы без переполнения всей кодовой памяти устройства. Описание дескриптора устройства и его полей могут быть найдено здесь.

const USB_CONFIG_DATA ConfigurationDescriptor =
{
    {                              /* configuration descriptor */
    sizeof(USB_CONFIGURATION_DESCRIPTOR), /* bLength */
    TYPE_CONFIGURATION_DESCRIPTOR, /* bDescriptorType */
    sizeof(USB_CONFIG_DATA),       /* wTotalLength */
    1,                             /* bNumInterfaces */
    1,                             /* bConfigurationValue */
    0,                             /* iConfiguration String Index */
    0x80,                          /* bmAttributes Bus Powered, No Remote Wakeup */
    0x32                           /* bMaxPower, 100mA */
    },
    {                              /* interface descriptor */
    sizeof(USB_INTERFACE_DESCRIPTOR), /* bLength */
    TYPE_INTERFACE_DESCRIPTOR,     /* bDescriptorType */
    0,                             /* bInterface Number */
    0,                             /* bAlternateSetting */
    2,                             /* bNumEndpoints */
    0xFF,                          /* bInterfaceClass (Vendor specific) */
    0xFF,                          /* bInterfaceSubClass */
    0xFF,                          /* bInterfaceProtocol */
    0                              /* iInterface String Index */
    },
    {                              /* endpoint descriptor */
    sizeof(USB_ENDPOINT_DESCRIPTOR), /* bLength */
    TYPE_ENDPOINT_DESCRIPTOR,      /* bDescriptorType */
    0x01,                          /* bEndpoint Address EP1 OUT */
    0x02,                          /* bmAttributes - Interrupt */
    0x0008,                        /* wMaxPacketSize */
    0x00                           /* bInterval */
    },
    {                              /* endpoint descriptor */
    sizeof(USB_ENDPOINT_DESCRIPTOR), /* bLength */
    TYPE_ENDPOINT_DESCRIPTOR,      /* bDescriptorType */
    0x81,                          /* bEndpoint Address EP1 IN */
    0x02,                          /* bmAttributes - Interrupt */
    0x0008,                        /* wMaxPacketSize */
    0x00                           /* bInterval */
    }
};

Описание дескриптора конфигурации и их полей можно найти выше. Мы сделали 2 дескриптора конечной точки в добавок к заданному по умолчанию каналу. Конечная точка EP1 OUT - 8 байт максимум Bulk OUT Endpoint, и конечная точка EP1 IN - 8 байт максимум Bulk IN Endpoint. Наш пример читает данные из конечной точки Bulk OUT и помещает их в кольцевой буфер из 80 байт. Посылка пакета IN к конечной точке EP1 читает 8-байтовые куски памяти из этого кольцевого буфера.

LANGID_DESCRIPTOR LANGID_Descriptor =
{ /* LANGID String Descriptor Zero */
    sizeof(LANGID_DESCRIPTOR),         /* bLenght */
    TYPE_STRING_DESCRIPTOR,            /* bDescriptorType */
    0x0409                             /* LANGID US English */
};

const MANUFACTURER_DESCRIPTOR Manufacturer_Descriptor =
{ /* ManufacturerString 1 */
    sizeof(MANUFACTURER_DESCRIPTOR),                     /* bLenght */
    TYPE_STRING_DESCRIPTOR,                              /* bDescriptorType */
    "B\0e\0y\0o\0n\0d\0 \0L\0o\0g\0i\0c\0"               /* ManufacturerString in UNICODE */
};

Строковый дескриптор с индексом 0 сделан для поддержки требований LANGID для строковых дескрипторов USB. Это указывает, что все описатели находятся на американском английском языке. Дескриптор производителя (Manufacturer Descriptor) может быть небольшим обманом, поскольку размер символьного массива фиксированно задан в заголовке и не динамичный.

#define MAX_BUFFER_SIZE 80

bank1 unsigned char circularbuffer[MAX_BUFFER_SIZE];
unsigned char inpointer;
unsigned char outpointer;

unsigned char *pSendBuffer;
unsigned char BytesToSend;
unsigned char CtlTransferInProgress;
unsigned char DeviceAddress;
unsigned char DeviceConfigured;

#define PROGRESS_IDLE 0
#define PROGRESS_ADDRESS 3

void main (void)
{
    TRISB = 0x03;  /* Int & Suspend Inputs */
    RB3 = 1;       /* Устройство не сконфигурировано (LED) */
    RB2 = 0;       /* Сброс PDIUSBD11 */

    InitUART();
    printf("Initialising\n\r");
    I2C_Init();

    RB2 = 1;       /* Вывод PDIUSBD11 из сброса */

    ADCON1 = 0x80; /* Управление ADC - все 8 каналов разрешены, */
                   /* поддержка обновления для 16F877 */
    USB_Init();
    printf("PDIUSBD11 готова к соединению\n\r");
    while(1)
    {
        if (!RB0)
        {
           D11GetIRQ(); /* Если IRQ в нуле, то на PDIUSBD11
                           имеется событие прерывания */
        }
    }
}

Функция main зависит от конкретного примера. Она отвечает за инициализацию входов и выходов портов ввода/вывода, инициализацию интерфейса I2C, ADC и PDIUSBD11. Как только все сконфигурировано, обработчик D11GetIRQ обрабатывает запросы на прерывание PDIUSBD11.

void D11GetIRQ(void)
{
        unsigned short Irq;
        unsigned char Buffer ;
  
        /* Чтение регистра прерывания, чтобы определить его источник */
          D11CmdDataRead(D11_READ_INTERRUPT_REGISTER, (unsigned char*)&Irq, 2);
  
        if (Irq) printf("Irq = 0x%X: ",Irq);

Функция USB_Init инициализирует PDIUSBD11. Эта процедура инициализации опущена в даташите Philips на PDIUSBD11, но доступна в FAQ. Последняя команда разрешает мягкое подключение pull-up резистора на сигнале D+, что указывает хосту на подключение устройства USB на полной скорости (full speed), и представляет появление устройства USB на шине.

void USB_Init(void)
{
    unsigned char Buffer ;
  
    /* Запрет функции хаба в PDIUSBD11 */
    Buffer  = 0x00;
    D11CmdDataWrite(D11_SET_HUB_ADDRESS, Buffer, 1);
  
    /* Установка адреса в 0 (по умолчанию) и разрешение функционирования */
    Buffer  = 0x80;
      D11CmdDataWrite(D11_SET_ADDRESS_ENABLE, Buffer, 1);
  
    /* Разрешение работы стандартных конечных точек */
    Buffer  = 0x02;
      D11CmdDataWrite(D11_SET_ENDPOINT_ENABLE, Buffer, 1);
  
    /* Установка режима - разрешение SoftConnect */
    Buffer  = 0x97; /* встроенная функция, SoftConnect, Clk Run, No LazyClk, Remote Wakeup */
    Buffer  = 0x0B; /* CLKOut = 4MHz */
    D11CmdDataWrite(D11_SET_MODE, Buffer, 2);
}

Main() осуществляет вызовы D11GetIRQ в цикле. Эта функция читает регистр прерывания PDIUSBD11, если какие-нибудь прерывания находятся в ожидании обработки. В этом случае они обрабатываются, иначе цикл продолжается. Другие устройства USB могут иметь несколько векторов прерывания, назначенных на каждую конечную точку. В этом случае каждый обработчик прерывания ISR будет обрабатывать отдельное прерывание, что позволяет убрать операторы if.

if (Irq & D11_INT_BUS_RESET) 
{
    printf("Bus Reset\n\r");
    USB_Init();
}
  
if (Irq & D11_INT_EP0_OUT) 
{
    printf("EP0_Out: ");
    Process_EP0_OUT_Interrupt();
}
  
if (Irq & D11_INT_EP0_IN) 
{
    printf("EP0_In: \n\r");
    if (CtlTransferInProgress == PROGRESS_ADDRESS) 
    {
        D11CmdDataWrite(D11_SET_ADDRESS_ENABLE,&DeviceAddress,1);
        D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP0_IN, Buffer, 1);
        CtlTransferInProgress = PROGRESS_IDLE;
    }
    else
    {
        D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP0_IN, Buffer, 1);
        WriteBufferToEndPoint();
    }
}

Условные операторы работают вниз в порядке приоритета. Наивысший приоритет у прерывания bus reset (сброс шины). Оно просто вызывает функцию USB_Init, которая переинициализирует функцию USB. Следующий наивысший приоритет – канал по умолчанию, основанный на конечной точке ноль - EP0 OUT и EP0 IN. Здесь обрабатываются энумерация и все управляющие запросы. Мы вызываем другую функцию для обработки запросов EP0_OUT.

Когда запрос сделан хостом, и он хочет принять данные, PIC16F876 отправит чипу PDIUSBD11 пакет из 8 байт. Поскольку шина USB управляется хостом, то микросхема PDIUSBD11 не может отправить данные когда пожелает, она буферизирует данные и ждет токена IN, который будет отправлен хостом. Когда PDIUSBD11 примет токен IN, она генерирует прерывание. В этот момент нужно загрузить новый пакет данных для отправки, что и происходит с помощью дополнительной функции WriteBufferToEndpoint().

Секция CtlTransferInProgress == PROGRESS_ADDRESS обрабатывает установку адреса устройства. Это мы обсудим позднее.

if (Irq & D11_INT_EP1_OUT) 
{
    printf("EP1_OUT\n\r");
    D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP1_OUT, Buffer, 1);
    bytes = D11ReadEndpoint(D11_ENDPOINT_EP1_OUT, Buffer);
    for (count = 0; count < bytes; count++) 
    {
        circularbuffer[inpointer++] = Buffer[count];
        if (inpointer >= MAX_BUFFER_SIZE) 
            inpointer = 0;
    }
    loadfromcircularbuffer(); //Kick Start
}
    
if (Irq & D11_INT_EP1_IN) 
{
    printf("EP1_IN\n\r");
    D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP1_IN, Buffer, 1);
    loadfromcircularbuffer();
}

Конечные точки EP1 OUT и EP1 IN реализованы для чтения и записи данных bulk в и из кольцевого буфера. Настройка позволяет использовать код в соединении с примером BulkUSB из Windows DDK. Кольцевой буфер задан ранее в коде как 80 байт, по длине занимающий весь bank1 PIC16F876 RAM.

if (Irq & D11_INT_EP2_OUT)
{
    printf("EP2_OUT\n\r");
    D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP2_OUT, Buffer, 1);
    Buffer  = 0x01; /* Stall конечной точки */
    D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP2_OUT, Buffer, 1);
}
  
if (Irq & D11_INT_EP2_IN)
{
    printf("EP2_IN\n\r");
    D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP2_IN, Buffer, 1);
    Buffer  = 0x01; /* Stall конечной точки */
    D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP2_IN, Buffer, 1);
    }
  
if (Irq & D11_INT_EP3_OUT)
{
    printf("EP3_OUT\n\r");
    D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP3_OUT, Buffer, 1);
    Buffer  = 0x01; /* Stall конечной точки */
    D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP3_OUT, Buffer, 1);
}
  
if (Irq & D11_INT_EP3_IN)
{
    printf("EP3_IN\n\r");
    D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP3_IN, Buffer, 1);
    Buffer  = 0x01; /* Stall конечной точки */
    D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP3_IN, Buffer, 1);
}

Конечные точки 2 и 3 в настоящий момент не используются, поэтому мы их приостанавливаем (stall), если будут приняты любые данные. PDIUSBD11 имеет команду Set Endpoint Enable, которую можно использовать для разрешения или запрещения обычных конечных точек (т. е. любых конечных точек, отличных от нулевой, составляющей по умолчанию канал управления). Мы можем использовать эту команду, чтобы отключить обычные конечные точки, которые мы не хотим использовать. В настоящий момент код предоставляет фундамент для дальнейшего усовершенствования.

void Process_EP0_OUT_Interrupt(void)
{
    unsigned long a;
    unsigned char Buffer ;
    USB_SETUP_REQUEST SetupPacket;
  
    /* Проверка принятого пакета на Setup или Data, вместе с очисткой IRQ */
    D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP0_OUT, &SetupPacket, 1);
  
    if (SetupPacket.bmRequestType & D11_LAST_TRAN_SETUP) 
    {

Первое, что мы должны проделать – определить, является ли пакет принятый на EP0 Out пакетом данных или пакетом настройки (Setup Packet). Setup Packet содержит запрос, такой как Get Descriptor, где пакет данных содержит данные для предыдущего запроса. Нам повезло, что большинство запросов не отправляют пакеты данных от хоста в устройство. Запрос, который это делает, является запросом SET_DESCRIPTOR, но он редко осуществляется.

        /* Это пакет установки (Setup Packet) - чтение пакета */
        D11ReadEndpoint(D11_ENDPOINT_EP0_OUT, &SetupPacket);
  
        /* Подтверждение Setup Packet в конечную точку EP0_OUT
           & очистка буфера */
        D11CmdDataWrite(D11_ACK_SETUP, NULL, 0);
        D11CmdDataWrite(D11_CLEAR_BUFFER, NULL, 0);

        /* Acknowlegde Setup Packet в конечную точку EP0_IN */
        D11CmdDataWrite(D11_ENDPOINT_EP0_IN, NULL, 0);
        D11CmdDataWrite(D11_ACK_SETUP, NULL, 0);

        /* Парсинг bmRequestType */
        switch (SetupPacket.bmRequestType & 0x7F)
        {

Как мы уже рассматривали в писании управляющих передач (Control Transfers), пакет setup не может получать в ответ NAK или STALL. Когда микросхема PDIUSBD11 получает пакет Setup, она очищает (flush) буфер EP0 IN и запрещает команды Validate Buffer и Clear Buffer. Это гарантирует, что пакет setup будет подтвержден микроконтроллером путем отправки команды подтверждения настройки (Acknowledge Setup) в обе конечные точки EP0 IN и EP0 OUT до того, как эффективна команда Validate или Clear Buffer. Прием пакета setup также выводит управляющую контрольную точку 0 из состояния STALL, если она была остановлена.

Как только пакет прочитался в память и пакет setup подтвержден, мы начинаем анализировать запрос, начиная с определения типа запроса. Пока нас не интересует направление передачи, и мы операцией AND отключаем этот бит. Три запроса, на которые все устройства должны ответить - Standard Device Request, Standard Interface Request и Standard Endpoint Request. Мы реализовали наш функционал (чтение входов ADC) в запросе вендора (Vendor Request) – мы добавили оператор выбора (case) в запросы Standard Vendor. Если наше устройство поддерживает спецификацию стандартного класса USB, нам также необходимо добавить операторы case для Class Device Request, Class Interface Request и/или Class Endpoint Request.

            case STANDARD_DEVICE_REQUEST:
                printf("Standard Device Request ");
                switch (SetupPacket.bRequest) 
                {
                case GET_STATUS:
                    /* Получение Status Request устройства, который должен
                       быть возвращен, состояния Remote Wakeup и Self Powered */
                    Buffer  = 0x01;
                    Buffer  = 0x00;
                    D11WriteEndpoint(D11_ENDPOINT_EP0_IN, Buffer, 2);
                    break;
                case CLEAR_FEATURE:
                case SET_FEATURE:
                    /* Мы не поддерживаем DEVICE_REMOTE_WAKEUP или TEST_MODE */
                    ErrorStallControlEndPoint();
                    break;

Запрос Get Status используется для сообщении о состоянии устройства в смысле питается ли устройство от шины или имеет собственный источник питания, и поддерживает ли устройство функцию удаленного пробуждения хоста (remote wakeup). В нашем устройстве мы сообщаем, что устройство имеет собственный источник питания (self powered, оно не питается от шины USB), и устройство не поддерживает remote wakeup.

На запросы Device Feature это устройство ответит, что не поддерживает ни DEVICE_REMOTE_WAKEUP, ни TEST_MODE, и вернет ошибку запроса USB Request Error.

                case SET_ADDRESS:
                    printf("Set Address\n\r");
                    DeviceAddress = SetupPacket.wValue | 0x80;
                    D11WriteEndpoint(D11_ENDPOINT_EP0_IN, NULL, 0);
                    CtlTransferInProgress = PROGRESS_ADDRESS;
                    break;

Только команда Set Address выполняет обработку после стадии status. Все другие команды должны завершить обработку перед стадией status. Адрес устройства читается, по операции OR на него накладывается константа 0x80, и результат сохраняется в переменной DeviceAddress. Операция OR 0x80 – специфична для микросхемы PDIUSBD11, у нее старший бит адреса задает,

продолжение следует...

Продолжение:


Часть 1 Все о USB , Программирование USB интерфейса и работа с USB периферии для программистов
Часть 2 Цоколевки разъема и кабеля - Все о USB , Программирование
Часть 3 Глава 4: Типы конечных точек (Endpoint Types ) - Все
Часть 4 Глава 5: Дескрипторы USB - Все о USB , Программирование
Часть 5 Глава 7 : обычный (Generic) драйвер USB - Все о
Часть 6 Термины - Все о USB , Программирование USB интерфейса и

создано: 2020-06-24
обновлено: 2024-11-12
42



Рейтиг 9 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей

Комментарии


Оставить комментарий
Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

Операционные системы и системное программировние

Термины: Операционные системы и системное программировние