W tym poście chciałbym opisać sposób wykonania oraz zastosowanie biblioteki HID (Human Interface Device) w mikrokontrolerze STM32F4. Można ją włączyć tylko dla mikrokontrolerów posiadających wbudowany interfejs USB. W przykładach będę przesyłał określony ciąg znaków poprzez USB do komputera. Dane zostaną wysłane po naciśnięciu klawisza,
Wspomniana powyżej biblioteka czy też klasa została opracowana dla urządzeń typu klawiatura czy myszka. Ma ona tą zaletę, że dzięki temu sterowniki zostały zawarte w każdym komputerze i urządzenie jest gotowe do działania zaraz po jego podłączeniu do komputera.
Aby urządzenie działało w nieco inny sposób to należałoby przygotować odpowiedni program na komputer PC np. w języku Delphi czy C#. I bibliotekę aktywować jako Custom HID. Program dla mikrokontrolera jest bardzo prosty w implementacji zwłaszcza z wykorzystaniem bibliotek HAL-a oraz programu Cube Mx.
Bardzo ważne jest także dobranie taktowania. Zegar dla USB powinien wynosić 48MHz.
Jednym z czołowych plików w bibliotece jest usbd_desc.c. W nim zawarte są deskryptory za pomocą których opisano konfigurację urządzenia poprzez USB.
Pierwszym z nich jest deskryptor urządzenia w którym zawarto takie informacje jak wersję standardu USB, numery pozwalające na identyfikację producenta oraz urządzenia.
Kolejnym elementem jest deklaracja adresu urządzenia oraz pozostałych parametrów:
Drugi przykład będzie zawierał wykonanie klawiatury za pomocą biblioteka HID oraz API. W pierwszej kolejności należy wgrać bibliotekę USBLib.
Następnie idąc od początku należy zdeklarować zmienną zewnętrzną na początku funkcji głównej, któa będzie informowała o definicji struktury:
Kolejnym elementem jest funkcja przesyłająca dane z STM-a na komputer:
Do przesyłania danych musi zostać przygotowany buffor zawierający 9 bajtów. Podczas przesyłania danych na linii może się pojawić jeden klawisz specjalny oraz 6 klawiszy znakowych.
Włączenie biblioteki dla USB_HID:
Poniżej lista wskaźników i ich numer jaki należy wprowadzić do bufora na pozycji 1. Poniżej znajduje się informacja, który z bitów ma być ustawiony aby była możliwość zaznaczenia klawisza jako włączonego:
Kody wszystkich klawiszy jakie są dostępne z tą biblioteką zostały przedstawione pod tym linkiem linkiem lub tutaj. Dodatkowo na internecie można jeszcze znaleźć taką książkę:
Klawisze numeryczne wraz ze znakami specjalnymi:
Dane dla poszczególnych klawiszy można zdefiniować przy użyciu #define:
Funkcja wysyłająca oraz kasująca przyciski wygląda następująco:
Obie muszą być zdeklarowane, spowodowane jest to tym, że komputer musi być informowany o tym że dany klawisz został wciśnięty oraz, że został puszczony. Jeśli dane zostaną tylko wysłane bez skasowania, to będą one cały czas odczytywane jako włączone.
Wspomniana powyżej biblioteka czy też klasa została opracowana dla urządzeń typu klawiatura czy myszka. Ma ona tą zaletę, że dzięki temu sterowniki zostały zawarte w każdym komputerze i urządzenie jest gotowe do działania zaraz po jego podłączeniu do komputera.
Aby urządzenie działało w nieco inny sposób to należałoby przygotować odpowiedni program na komputer PC np. w języku Delphi czy C#. I bibliotekę aktywować jako Custom HID. Program dla mikrokontrolera jest bardzo prosty w implementacji zwłaszcza z wykorzystaniem bibliotek HAL-a oraz programu Cube Mx.
Bardzo ważne jest także dobranie taktowania. Zegar dla USB powinien wynosić 48MHz.
Opis biblioteki:
Jednym z czołowych plików w bibliotece jest usbd_desc.c. W nim zawarte są deskryptory za pomocą których opisano konfigurację urządzenia poprzez USB.
Pierwszym z nich jest deskryptor urządzenia w którym zawarto takie informacje jak wersję standardu USB, numery pozwalające na identyfikację producenta oraz urządzenia.
- __ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] __ALIGN_END =
- {
- 0x12, /*bLength */
- USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
- 0x00, /*bcdUSB */
- 0x02,
- 0x00, /*bDeviceClass*/
- 0x00, /*bDeviceSubClass*/
- 0x00, /*bDeviceProtocol*/
- USB_OTG_MAX_EP0_SIZE, /*bMaxPacketSize*/
- LOBYTE(USBD_VID), /*idVendor*/
- HIBYTE(USBD_VID), /*idVendor*/
- LOBYTE(USBD_PID), /*idVendor*/
- HIBYTE(USBD_PID), /*idVendor*/
- 0x00, /*bcdDevice rel. 2.00*/
- 0x02,
- USBD_IDX_MFC_STR, /*Index of manufacturer string*/
- USBD_IDX_PRODUCT_STR, /*Index of product string*/
- USBD_IDX_SERIAL_STR, /*Index of serial number string*/
- USBD_CFG_MAX_NUM /*bNumConfigurations*/
- } ; /* USB_DeviceDescriptor */
Kolejnym elementem jest deklaracja adresu urządzenia oraz pozostałych parametrów:
- #define USBD_VID 0x0483
- #define USBD_PID 0x5710
- #define USBD_LANGID_STRING 0x409
- #define USBD_MANUFACTURER_STRING "STMicroelectronics"
- #define USBD_PRODUCT_HS_STRING "USB HID device HS"
- #define USBD_SERIALNUMBER_HS_STRING "000000000111"
- #define USBD_PRODUCT_FS_STRING "USB HID device FS"
- #define USBD_SERIALNUMBER_FS_STRING "000000000112"
- #define USBD_CONFIGURATION_HS_STRING "HID Config"
- #define USBD_INTERFACE_HS_STRING "HID Interface"
- #define USBD_CONFIGURATION_FS_STRING "HID Config"
- #define USBD_INTERFACE_FS_STRING "HID Interface"
Biblioteki API klawiatura
Drugi przykład będzie zawierał wykonanie klawiatury za pomocą biblioteka HID oraz API. W pierwszej kolejności należy wgrać bibliotekę USBLib.
Następnie idąc od początku należy zdeklarować zmienną zewnętrzną na początku funkcji głównej, któa będzie informowała o definicji struktury:
- extern USB_OTG_CORE_HANDLE USB_OTG_dev;
Kolejnym elementem jest funkcja przesyłająca dane z STM-a na komputer:
Do przesyłania danych musi zostać przygotowany buffor zawierający 9 bajtów. Podczas przesyłania danych na linii może się pojawić jeden klawisz specjalny oraz 6 klawiszy znakowych.
Włączenie biblioteki dla USB_HID:
- void USB_HID_Init(void)
- {
- USBD_Init(&USB_OTG_dev,
- #ifdef USE_USB_OTG_HS
- USB_OTG_HS_CORE_ID,
- #else
- USB_OTG_FS_CORE_ID,
- #endif
- &USR_desc,
- &USBD_HID_cb,
- &USR_cb);
- }
Poniżej lista wskaźników i ich numer jaki należy wprowadzić do bufora na pozycji 1. Poniżej znajduje się informacja, który z bitów ma być ustawiony aby była możliwość zaznaczenia klawisza jako włączonego:
- Lewy Control = 0x01;
- Lewy Shift = 0x02;
- Lewy Alt = 0x04;
- Klawisz Systemowy = 0x08;
- Prawy Control = 0x10;
- Prawy Shift = 0x20;
- Prawy Alt = 0x40;
- Prawy Systemowy = 0x80;
Kody wszystkich klawiszy jakie są dostępne z tą biblioteką zostały przedstawione pod tym linkiem linkiem lub tutaj. Dodatkowo na internecie można jeszcze znaleźć taką książkę:
- a - 0x04
- b - 0x05
- c - 0x06
- d - 0x07
- e - 0x08
- f - 0x09
- g - 0x0A
- h - 0x0B
- i - 0x0C
- j - 0x0D
- k - 0x0E
- l - 0x0F
- m - 0x10
- n - 0x11
- o - 0x12
- p - 0x13
- q - 0x14
- r - 0x15
- s - 0x16
- t - 0x17
- u - 0x18
- v - 0x19
- w - 0x1A
- x - 0x1B
- y - 0x1C
- z - 0x1D
Klawisze numeryczne wraz ze znakami specjalnymi:
- 1 ! - 0x1E
- 2 @ - 0x1F
- 3 # - 0x20
- 4 $ - 0x21
- 5 % - 0x22
- 6 ^ - 0x23
- 7 & - 0x24
- 8 * - 0x25
- 9 ( - 0x26
- 0 ) - 0x27
Dane dla poszczególnych klawiszy można zdefiniować przy użyciu #define:
- #define CONTROL_LEFT (1<<0)
- #define SHIFT_LEFT (1<<1)
- #define ALT_LEFT (1<<2)
- #define GUI_LEFT (1<<3)
- #define CONTROL_RIGHT (1<<4)
- #define SHIFT_RIGHT (1<<5)
- #define ALT_RIGHT (1<<6)
- #define GUI_RIGHT (1<<7)
- #define KEY_A 0x04
- #define KEY_B 0x05
- #define KEY_C 0x06
- #define KEY_D 0x07
- #define KEY_E 0x08
- #define KEY_F 0x09
- #define KEY_G 0x0A
- #define KEY_H 0x0B
- #define KEY_I 0x0C
- #define KEY_J 0x0D
- #define KEY_K 0x0E
- #define KEY_L 0x0F
- #define KEY_M 0x10
- #define KEY_N 0x11
- #define KEY_O 0x12
- #define KEY_P 0x13
- #define KEY_Q 0x14
- #define KEY_R 0x15
- #define KEY_S 0x16
- #define KEY_T 0x17
- #define KEY_U 0x18
- #define KEY_V 0x19
- #define KEY_W 0x1A
- #define KEY_X 0x1B
- #define KEY_Y 0x1C
- #define KEY_Z 0x1D
- #define KEY_1 0x1E
- #define KEY_2 0x1F
- #define KEY_3 0x20
- #define KEY_4 0x21
- #define KEY_5 0x22
- #define KEY_6 0x23
- #define KEY_7 0x24
- #define KEY_8 0x25
- #define KEY_9 0x26
- #define KEY_0 0x27
- #define KEY_F1 0x3A
- #define KEY_F2 0x3B
- #define KEY_F3 0x3C
- #define KEY_F4 0x3D
- #define KEY_F5 0x3E
- #define KEY_F6 0x3F
- #define KEY_F7 0x40
- #define KEY_F8 0x41
- #define KEY_F9 0x42
- #define KEY_F10 0x43
- #define KEY_F11 0x44
- #define KEY_F12 0x45
Funkcja wysyłająca oraz kasująca przyciski wygląda następująco:
- void KeyboardWrite(uint8_t Specjalny, uint8_t Pierwszy, uint8_t Drugi, uint8_t Trzeci, uint8_t Czwarty,
- uint8_t Piaty, uint8_t Szosty)
- {
- uint8_t buff[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 9 bytes long report */
- int i = 0;
- buff[0] = 0x01;
- buff[1] = Specjalny;
- buff[2] = 0x00;
- buff[3] = Pierwszy;
- buff[4] = Drugi;
- buff[5] = Trzeci;
- buff[6] = Czwarty;
- buff[7] = Piaty;
- buff[8] = Szosty;
- USBD_HID_SendReport(&USB_OTG_dev, buff, 9);
- buff[0] = 0x01;
- for(i=1;i<9;i++)
- {
- buff[i] = 0x00;
- }
- USBD_HID_SendReport(&USB_OTG_dev, buff, 9);
- }
Obie muszą być zdeklarowane, spowodowane jest to tym, że komputer musi być informowany o tym że dany klawisz został wciśnięty oraz, że został puszczony. Jeśli dane zostaną tylko wysłane bez skasowania, to będą one cały czas odczytywane jako włączone.
Biblioteki API myszka
W tym przypadku włączenie biblioteki jest takie samo jak dla klawiatury. Dane wysyła się na pięciu bajtach:
- void MouseSend(uint8_t Przycisk, uint8_t OsX, uint8_t OsY, uint8_t Scroll)
- {
- uint8_t buff[5] = {0, 0, 0, 0, 0};
- buff[0] = 0x02;
- //Lewy przycisk 0x01 | Prawy przycisk 0x02 | Srodkowy przycisk 0x04
- buff[1] = Przycisk;
- buff[2] = OsX;
- buff[3] = OsY;
- buff[4] = Scroll;
- USBD_HID_SendReport(&USB_OTG_dev, buff, 5);
- buff[0] = 0x02;
- buff[1] = 0x00;
- buff[2] = 0x00;
- buff[3] = 0x00;
- buff[4] = 0x00;
- USBD_HID_SendReport(&USB_OTG_dev, buff, 5);
- }
Jeśli chodzi o ustawianie i wywoływanie danych, to można to robić np. poprzez wciśnięcie przycisku systemowego w pętli np. tak:
Deklaracja przycisku:
Deklaracja przycisku:
- void ButtonInit(void)
- {
- GPIO_InitTypeDef GPIO_InitDef;
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
- GPIO_InitDef.GPIO_Pin = GPIO_Pin_0;
- GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
- //Ustawienie jako wejście
- GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;
- GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_InitDef.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_Init(GPIOA, &GPIO_InitDef);
- }
Teraz czas na pętlę główną programu, w pętli będzie wywoływane przesunięcie myszki oraz wpisanie danych poprzez klawiaturę:
Ja wykorzystałem wyzwalanie przyciskiem, ale równie dobrze można wykonać w pętli, przez co dane będą cyklicznie wysyłane na komputer od razu po podłączeniu do USB.
- int main(void)
- {
- uint8_t i = 0;
- SystemInit();
- ButtonInit();
- USB_HID_Init();
- while(1)
- {
- if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0))
- {
- delayms(70);
- if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0))
- {
- delayms(100);
- if(i==0)
- {
- KeyboardWrite(0x00, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15);
- KeyboardWrite(0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17);
- i++;
- }
- else if(i==1)
- {
- MouseSend(0x01, 0x05, 0x10, 0x10);
- MouseSend(0x02, 0x05, 0x08, 0x06);
- i=0;
- }
- }
- }
- }
- }
Ja wykorzystałem wyzwalanie przyciskiem, ale równie dobrze można wykonać w pętli, przez co dane będą cyklicznie wysyłane na komputer od razu po podłączeniu do USB.