Ваш браузер устарел. Рекомендуем обновить его до последней версии.

Microchip Libraries of Applications: USB HID.

        Этой статьей продолжается цикл примеров по программированию модуля UMM01.

        Мощнейшим средством помощи разработчику, предоставляемым компанией MICROCHIP, является набор бесплатных библиотек, позволяющий легко создавать достаточно трудоемкие приложения, такие как интернет - приложения, WiFi - приложения, USB - программы, и многое другое. Библиотеки построены в виде примеров, которые легко адаптируются под конкретные задачи. В этой статье будет показано, как можно быстро создать свою программу для обмена данными между модулем UMM01 и компьютером через USB HID. При этом никакие специальные знания по шине USB НЕ ТРЕБУЮТСЯ.

        Для начала, необходимо загрузить и установить Microchip Libraries of Applications. Автор пользовался версией v2012-07-18 и средой разработки MPLAB X v1.41. Для компиляции приложения со стороны компьютера (под W7) использовалась среда Microsoft Visual C++ 2010 Express. Она так же распространяется бесплатно с сайта Microsoft. После установки библиотеки будут находиться в каталоге «Microchip Solutions v2012-07-18», в который, в свою очередь, будет включен каталог «USB». Внутри «USB» можно увидеть огромное число примеров применения порта USB, но нас будет интересовать «Device - HID - Custom Demos». Этот пример можно взять за основу для разработки своих программ.

        Шина USB сама по себе - достаточно сложный интерфейс, поэтому ее программирование является трудоемкой задачей, требующей написания драйверов для операционной системы. Однако разработчики USB предусмотрели упрощенный режим ее работы, поддерживаемый стандартными функциями API во всех распространенных операционных системах. Этот режим называется HID – Human Interface Device. Как видно из названия, он изначально предназначался для подключения к компьютеру клавиатуры, мыши и др. аналогичных устройств связи человека и ЭВМ. Этот же режим можно использовать и для обмена данными между компьютером и внешним устрпойством, что позволяет заменить до сих пор широко распространенный COM-порт. Основное ограничение USB HID – это невысокая скорость обмена – до 64кбайт/с. Однако, этой скорости достаточно для полноценной замены COM-порта.

        Откройте в MPLAB X проект «…Device - HID - Custom Demos -> Firmware -> MPLAB.X». В окошке конфигурации выберите «PIC24FJ256GB210_PIM». Это название отладочного модуля, поставляемого Microchip, который может подключаться к основной отладочной плате, например, к Explorer16. В данном случае это не имеет никакого значения, т.к. нам от этой конфигурации нужно только название процессора. Далее необходимо поменять биты конфигурации, т.к. отладочные платы используют другую частоту кварцевого генератора и другие выводы для подключения отладчика. Открываем файл «Main.c» и через поиск находим строку «#elif defined(PIC24FJ256GB210_PIM)». Под этой строкой будет две строки:

_CONFIG1(FWDTEN_OFF & ICS_PGx2 & GWRP_OFF & GCP_OFF & JTAGEN_OFF)

 _CONFIG2(POSCMOD_HS & IOL1WAY_ON & OSCIOFNC_ON & FCKSM_CSDCMD & FNOSC_PRIPLL & PLL96MHZ_ON & PLLDIV_DIV2 & IESO_OFF)

Вместо них надо вставить следующие биты конфигурации:

int CONFIG2 __attribute__((space(prog), address(0x2ABFC))) = 0x3BBE ;

/*    POSCMOD_HS &         // Primary Oscillator Select (HS Oscillator mode is selected)

    IOL1WAY_ON &         // IOLOCK One-Way Set Enable (The IOLOCK bit (OSCCON<6>) can be set once, provided the unlock sequence has been completed. Once set, the Peripheral Pin Select registers cannot be written to a second time.)

    OSCIOFNC_OFF &       // OSCO Pin Configuration (OSCO/CLKO/RC15 functions as CLKO (FOSC/2))

    FCKSM_CSDCMD &       // Clock Switching and Fail-Safe Clock Monitor (Clock switching and Fail-Safe Clock Monitor are disabled)

    FNOSC_PRIPLL &       // Initial Oscillator Select (Primary Oscillator with PLL module (XTPLL, HSPLL, ECPLL))

    PLL96MHZ_ON &        // 96MHz PLL Startup Select (96 MHz PLL is enabled automatically on start-up)

    PLLDIV_DIV4 &        // 96 MHz PLL Prescaler Select (Oscillator input is divided by 4 (16 MHz input))

    IESO_OFF             // Internal External Switchover (IESO mode (Two-Speed Start-up) is disabled)

);*/

 int CONFIG1 __attribute__((space(prog), address(0x2ABFE))) = 0x3F7F ;

/*    WDTPS_PS32768 &      // Watchdog Timer Postscaler (1:32,768)

    FWPSA_PR128 &        // WDT Prescaler (Prescaler ratio of 1:128)

    ALTVREF_ALTVREDIS &  // Alternate VREF location Enable (VREF is on a default pin (VREF+ on RA9 and VREF- on RA10))

    WINDIS_OFF &         // Windowed WDT (Standard Watchdog Timer enabled,(Windowed-mode is disabled))

    FWDTEN_OFF &         // Watchdog Timer (Watchdog Timer is disabled)

    ICS_PGx1 &           // Emulator Pin Placement Select bits (Emulator functions are shared with PGEC1/PGED1)

    GWRP_OFF &           // General Segment Write Protect (Writes to program memory are allowed)

    GCP_OFF &            // General Segment Code Protect (Code protection is disabled)

    JTAGEN_OFF           // JTAG Port Enable (JTAG port is disabled)

);*/

 

        Теперь проект можно скомпилировать, загрузить программатором в процессор и для проверки запустить его на исполнение. При этом модуль UMM01 достаточно подключить только к USB порту, дополнительного питания не требуется. Затем, из каталога «Device - HID - Custom Demos» запускаем файл «GenericHIDSimpleDemo.exe». В появившемся окне нажимаем кнопку «Connect». Программа начнет процесс поиска нашего HID-устройства, и при его обнаружении (а она его должна обнаружить), станут активными кнопки «Toggle LED(s)» и «Get Pushbutton State». Если нажать «Get Pushbutton State», программа контроллера получит команду проверить состояние кнопки отладочной платы. Т.к. сейчас линия процессора, заданная для отладочной платы Microchip, не подключена к кнопке, будет выдано сообщение : «State: Not Pressed». Для проверки общей работоспособности этого достаточно.

        Далее необходимо найти места в обеих программах, где производится считывание и запись в порт USB. Сначала рассмотрим построение программы контроллера.

        Откройте файл «Main.c» и через поиск найдите основную функцию программы «main(void)». Как видно из текста, сначала производится инициализация портов вызовом «InitializeSystem()», затем при использовании прерываний от USB (они используются  по умолчанию), вызывается «USBDeviceAttach()». Далее начинается основной цикл программы. В нем есть функция «USBDeviceTasks()». Ее вызов производится только при отключенных прерываниях, т.е. по умолчанию она не используется. Таким образом, в основном цикле производится вызов только одной функции – «ProcessIO()». Перейдем к ее тексту.

        Данная функция проверяет, принят ли пакет по USB («if(!HIDRxHandleBusy(USBOutHandle))»), и, если принят, определяет номер принятой команды по первому байту пакета («switch(ReceivedDataBuffer[0])»), и обрабатывает принятую команду. После обработки всех команд производится вызов «HIDRxPacket()» для инициализации приема. Таким образом, прием пакета можно осуществить следующим кодом:

#define HID_EP               1            //Конечная точка USB устройства

USB_HANDLE USBOutHandle=0;    //Дескриптор процесса приема

BYTE ReceivedDataBuffer[64];      //Буфер для приема

USBOutHandle = HIDRxPacket(HID_EP,

(BYTE*)&ReceivedDataBuffer,      //Буфер для приема

64);        //Число принимаемых байт. Т.к. прием/передача происходит по 64 байта,

              //это число всегда 64

//Возвращает дескриптор, который используется в следующей функции:

While (1)

{

if(!HIDRxHandleBusy(USBOutHandle))/*Проверка занятости процесса приема. Если свободен, то данные приняты*/

{//Здесь обработка принятых данных. Они находятся в массиве «ReceivedDataBuffer» }

USBOutHandle = HIDRxPacket(HID_EP, (BYTE*)&ReceivedDataBuffer,64);/*Запускаем опять процесс приема.*/

//Здесь исполняем свой код………

}

Передача данных выполняется аналогично:

#define HID_EP               1//Конечная точка USB устройства

USB_HANDLE    USBInHandle=0;

BYTE ToSendDataBuffer[64];

while (1)

{

if(!HIDTxHandleBusy(USBInHandle))     /*Проверка занятости процесса передачи, если свободен – можно передавать*/

{

//Считаем, что данные для передачи готовы (массив ToSendDataBuffer заполнен)

USBInHandle = HIDTxPacket(HID_EP,(BYTE*)&ToSendDataBuffer[0],64);//инициируем передачу

}

//Здесь исполняем свой код.

}

        Разумеется, эти два кода легко объединяются в один. В самом простом варианте рассматриваемого проекта можно ничего не менять, а вставить свой код в функцию «ProcessIO()».

        Еще необходимо отметить, что идентификация USB устройств происходит по уникальной комбинации двух номеров ViD (Vendor ID) и PiD (Product ID). Эти номера задаются в файле «usb_descriptors.c» в дескрипторе «USB_DEVICE_DESCRIPTOR».

        Теперь можно изменить линии портов, которые изначально определены для отладочной платы Microchip, на линии модуля UMM01. У нас используется конфигурация «PIC24FJ256GB210_PIM», поэтому необходимо открыть файл «PIC24FJ256GB210_PIM.h». В нем и содержатся определения линий портов. Для примера подключим светодиоды и кнопку модуля UMM01 вместо используемых по умолчанию. Найдем строки «mLED_1» и «mLED_2» и вставим для них свой код:

#define mLED_1              LATCbits.LATC2//LATAbits.LATA0

#define mLED_2              LATCbits.LATC3//LATAbits.LATA1

Строку инициализации линий светодиодов «mInitAllLEDs» заменим на:

#define mInitAllLEDs()      LATC &= 0xF3; TRISC &= 0xF3;// LATA &= 0xF0; TRISA &= 0xF0;

        В рассматриваемом проекте в функции «ProcessIO()» производится опрос кнопки sw3. Подключим к этой переменной кнопку SB2 (см. схему) модуля UMM01. Найдем «#define sw3» и заменим всю строку на:

#define sw3                 PORTEbits.RE5//PORTDbits.RD7

Строку с «mInitSwitch3» заменим на:

#define mInitSwitch3()      TRISEbits.TRISE5=1;//{TRISDbits.TRISD7=1; ANSDbits.ANSD7 = 0;}

        Теперь можно скомпилировать и выполнить программу. Запустим на компьютере «GenericHIDSimpleDemo.exe» и проверим работу кнопок «Toggle LED(s)» и «Get Pushbutton State». Они должны работать.

        Рассмотрим работу компьютерной программы. Хотелось бы отметить, что Microchip предоставляет примеры компьютерных программ практически для всех распространенных ОС. Далее будет рассмотрена используемая ранее программа «GenericHIDSimpleDemo.exe» под Windows. Запустим Visual C++ Express и откроем проект «…\Microchip Solutions v2012-07-18\USB\Device - HID - Custom Demos\Simple Demo - Windows Software\Microsoft Visual C++ 2005 Express\GenericHIDSimpleDemo.sln» При первом открытии проект будет преобразован в формат новой версии Visual C++ Express.

        Откройте файл «Form1.h»  и найдите строку «MY_DEVICE_ID». Этим определением задается ViD и PiD подключаемого USB устройства. Эти номера должны совпадать с номерами, заданными в программе контроллера. Перейдите в конструктор формы и дважды щелкните на кнопку «Connect». После этого откроется функция обнаружения и установки связи с подключенным USB устройством. Результатом работы этой функции являются два дескриптора WriteHandle и ReadHandle. Эти дескрипторы возвращаются функцией CreateFile() и их в дальнейшем нужно использовать для приема или передачи вызовом WriteFile() и ReadFile(). Те, кто ранее работал с последовательным портом, должны быть знакомы этими функциями. Обмен осуществляется аналогично. Единственное отличие – первый байт в массивах для приема и для передачи должен быть равен 0, и в обмене он не участвует. Поэтому буфер должен задаваться не 64 байта, а 65.

Пример передачи:

 DWORD BytesWritten = 0;

unsigned char OutputPacketBuffer[65];  

 

OutputPacketBuffer[0] = 0; //Должен быть равен 0

OutputPacketBuffer[1] = 0x12;//Данные…

OutputPacketBuffer[2] = 0x34;//

WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0);

 

Пример приема:

DWORD BytesRead = 0;

unsigned char InputPacketBuffer[65];   

 

InputPacketBuffer[0] = 0;//Должен быть равен 0

ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0);

 

        Вызов функции ReadFile() является блокирующим, т.е. выход из нее происходит только после того, как приняты данные. Что бы не останавливать работу программы, можно воспользоваться компонентом «backgroundWorker». Этот компонент позволит производить вызов функции ReadFile() в отдельном потоке, не блокируя программу.