2020-08-13 03:43 PM
Could anyone tell how to add this functionality in the USB Host library for STM32CubeIDE?
I found this:
u8 c=3;
USBH_StatusTypeDef res = USBH_BUSY;
do {
res=USBH_Set_Report(&USB_OTG_Host_Hnd,&USB_Host,0x02,0x00,0x01,&c);
} while(res != USBH_OK);
Source: https://community.st.com/s/question/0D50X00009XkZ11/caps-lock-led-turn-onoff
I didn't find this function (USBH_Set_Report) in the library, I found only USBH_HID_SetReport, but I couldn't implement that.
Is there any ST document that talks about this?
I found a document written by Microchip:
A standard keyboard has three LEDs to display NumLock, Caps Lock and Scroll Lock status. The LEDs are absolute output items; the state of each LED must be included in the output reports (0 = OFF, 1 = ON)
Source: http://ww1.microchip.com/downloads/en/AppNotes/01212a.pdf
Solved! Go to Solution.
2021-11-19 09:53 PM
Okay, that worked for me, maybe there's a better way to do it, but I have other things going on:
usb_host.c:
uint32_t keyboardMain_debounce = 0;
HID_KEYBD_Info_TypeDef *keybd_info1;
uint8_t kbd_LED_status[1] = { 0B001 }; // kbd_num_lock_state = 1
uint8_t kbd_LED_status_old = 0;
uint8_t kbd_num_lock_state = 1;
uint8_t caps_lock_state = 0;
uint8_t kbd_scroll_lock_state = 0;
void USBH_HID_EventCallback(USBH_HandleTypeDef *phost) {
HID_HandleTypeDef *HID_Handle =
(HID_HandleTypeDef*) phost->pActiveClass->pData;
if (HID_Handle->Init == USBH_HID_KeybdInit) {
keybd_info1 = USBH_HID_GetKeybdInfo(phost);
USBH_DbgLog("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
keybd_info1->keys[0], keybd_info1->keys[1],
keybd_info1->keys[2], keybd_info1->keys[3],
keybd_info1->keys[4], keybd_info1->keys[5]); // USBH_DbgLog
if (HAL_GetTick() > (keyboardMain_debounce + 100)) {
keyboardMain_debounce = HAL_GetTick();
uint8_t key = keybd_info1->keys[0];
if (key == KEY_INSERT) {
keyboard_insert = !keyboard_insert;
} else if (key == KEY_KEYPAD_NUM_LOCK_AND_CLEAR) {
if ((kbd_LED_status[0] & 1) == 0) {
kbd_LED_status[0] |= 0B001;
} else {
kbd_LED_status[0] &= 0B110;
}
} else if (key == KEY_CAPS_LOCK) {
if (((kbd_LED_status[0] >> 1) & 1) == 0) {
kbd_LED_status[0] |= 0B010;
} else {
kbd_LED_status[0] &= 0B101;
}
} else if (key == KEY_SCROLL_LOCK) {
if (((kbd_LED_status[0] >> 2) & 1) == 0) {
kbd_LED_status[0] |= 0B100;
} else {
kbd_LED_status[0] &= 0B011;
}
}
}
} else if (HID_Handle->Init == USBH_HID_MouseInit) {
USBH_HID_GetMouseInfo(phost);
USBH_DbgLog(
"Mouse action: x= 0x%x, y= 0x%x, button1 = 0x%x, button2 = 0x%x, button3 = 0x%x \n",
mouse_info.x, mouse_info.y, mouse_info.buttons[0],
mouse_info.buttons[1], mouse_info.buttons[2]);
}
}
usb_host.c:
/* USER CODE BEGIN 1 */
USBH_StatusTypeDef USB_Set_Keyboard_LED_Status(uint8_t *LED_status) {
if (kbd_LED_status_old == kbd_LED_status[0]) {
return HAL_OK;
}
kbd_LED_status_old = LED_status[0];
uint8_t kbd_led[2] = { 0x00, 0x00 };
USBH_StatusTypeDef ret = HAL_BUSY;
USBH_StatusTypeDef ret2 = HAL_OK;
kbd_led[0] = LED_status[0] & 7;
for (uint8_t timeout = 0; timeout < 15; timeout++) {
ret = USBH_HID_SetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
sizeof(kbd_led));
UsrLog("USBH_HID_SetReport return: %d", ret);
IWDG_delay_ms(1);
if (ret == ret2) {
break;
}
}
// ret = HAL_BUSY;
// ret2 = HAL_OK;
for (uint8_t timeout = 0; timeout < 15; timeout++) {
ret = USBH_HID_GetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
sizeof(kbd_led));
UsrLog("USBH_HID_GetReport return: %d; kbd_led: %d", ret, kbd_led[0]);
IWDG_delay_ms(1);
if (ret == ret2) {
break;
}
}
if (kbd_led[0] == (LED_status[0] & 7)) {
ret = HAL_OK;
} else {
ret = HAL_ERROR;
}
LED_status[0] = kbd_led[0];
if ((kbd_LED_status[0] & 1) == 1) {
kbd_num_lock_state = 1;
} else {
kbd_num_lock_state = 0;
}
if (((kbd_LED_status[0] >> 1) & 1) == 1) {
caps_lock_state = 1;
} else {
caps_lock_state = 0;
}
if (((kbd_LED_status[0] >> 2) & 1) == 1) {
kbd_scroll_lock_state = 1;
} else {
kbd_scroll_lock_state = 0;
}
return ret;
}
usb_host.c:
static void USBH_UserProcess2(USBH_HandleTypeDef *phost, uint8_t id) {
/* USER CODE BEGIN CALL_BACK_21 */
switch (id) {
case HOST_USER_SELECT_CONFIGURATION:
break;
case HOST_USER_DISCONNECTION:
Appli_state = APPLICATION_DISCONNECT;
Appli_state_FS = Appli_state;
kbd_LED_status_old = 99; // <--------- to update keyboard if reconnected
usbh_core.c:
uint32_t keyboard_led_status_tick = 0;
/**
* @brief USBH_Process
* Background process of the USB Core.
* @param phost: Host Handle
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_Process(USBH_HandleTypeDef *phost) {
[...]
case HOST_CLASS:
/* process class state machine */
if (phost->pActiveClass != NULL) {
/* Update keyboard LED status */
if (HAL_GetTick() > (keyboard_led_status_tick + 10)) {
keyboard_led_status_tick = HAL_GetTick();
HID_HandleTypeDef *HID_Handle =
(HID_HandleTypeDef*) phost->pActiveClass->pData;
if (HID_Handle->Init == USBH_HID_KeybdInit) {
USB_Set_Keyboard_LED_Status();
}
}
phost->pActiveClass->BgndProcess(phost);
}
break;
case HOST_DEV_DISCONNECTED:
[...]
2021-11-17 09:11 PM
I found another reference, but I still can't make it work:
https://community.st.com/s/question/0D50X00009XkewLSAR/setreport-usb-hid-class-request-for-stm32
2021-11-17 09:30 PM
I found another reference
"In the following example the function is used to turn on the NUM LOCK, CAPS LOCK and SCROLL LOCK LEDs on a keyboard. The HID report is sent over the control endpoint. The _OnCompletion callback function is called at the end of data transfer."
/*********************************************************************
*
* _TurnOnKeyboardLEDs
*
* Function description
* Turns on NUM LOCK, CAPS LOCK and SCROLL LOCK LEDs on a keyboard.
*
* Parameters
* hInterface Handle to a HID device
*/
static void _TurnOnKeyboardLEDs(USBH_INTERFACE_HANDLE hInterface) {
USBH_URB Urb;
U8 LedState;
LedState = 0x07;
Urb.Header.pContext = NULL;
Urb.Header.Function = USBH_FUNCTION_CONTROL_REQUEST;
Urb.Header.pfOnCompletion = _OnCompletion;
Urb.Request.ControlRequest.Setup.Type = 0x21;
Urb.Request.ControlRequest.Setup.Request = 0x09;
Urb.Request.ControlRequest.Setup.Value = 0x0200;
Urb.Request.ControlRequest.Setup.Index = 0;
Urb.Request.ControlRequest.Setup.Length = 1;
Urb.Request.ControlRequest.pBuffer = &LedState;
Urb.Request.ControlRequest.Length = 1;
USBH_SubmitUrb(hInterface, &Urb);
}
2021-11-17 09:37 PM
I found another reference:
"you should be able to send 8 bytes to the keyboard using the "USBHost_Send".
Note that sends data to endpoint, It is non blocking function. After calling it, user should poll transaction status on the endpoint with USBHost_GetTransactionStatus.
Also, on the page.59 you should find how the Report should look:
(Not found) http://www.usb.org/developers/hidpage/HID1_11.pdf"
Source: https://forum.mikroe.com/viewtopic.php?t=68414
(New link) https://www.usb.org/sites/default/files/documents/hid1_11.pdf
2021-11-18 01:42 AM
I found another reference:
"Hi anyone looking at this thread, the USBH_HID_SetReport can be used to control the LED status of a keyboard from a host device. Unfortunately there must have been some issues with the compatibility between STM Cube and IAR as when I have tried the same code I was trying with IAR on True Studio the function works. I have asked ST if this is a bug."
2021-11-18 08:07 PM
I managed to make it work, but I don't know why it's working this way, I understood that I should send data in another way, based on this document, on page 79 (this information doesn't seem to have much explanation on the Internet):
https://www.usb.org/sites/default/files/documents/hid1_11.pdf
I saw a lot of people suggesting things but it seems that nobody had been able to do with STM32 and USB Host using STM32CubeIDE,
I hope it's revised and incorporated in a sample code, it's unfortunate that a company like ST can read the keys and can't give an example of how to light an LED on the keyboard.
usb_host.c:
// 0B001: NumLock
// 0B010: Caps Lock
// 0B100: Scroll Lock
// 0B111: NumLock, Caps Lock and Scroll Lock
uint8_t LED_status = 7; // 7 = 0B111
uint8_t LED_status_old = 0;
USBH_StatusTypeDef USB_Set_Keyboard_LED_Status(void) {
if (LED_status_old == LED_status) {
return HAL_OK;
}
LED_status_old = LED_status;
uint8_t kbd_led[2] = { 0x00, 0x00 };
USBH_StatusTypeDef ret = HAL_BUSY;
USBH_StatusTypeDef ret2 = HAL_OK;
kbd_led[0] = LED_status & 7;
for(uint8_t timeout = 0; timeout < 100; timeout++) {
ret = USBH_HID_SetReport(&hUsbHostFS, 0x02U, 0U, kbd_led, sizeof(kbd_led));
// 7x ret=1 ???
USBH_DbgLog("USBH_HID_SetReport return: %d", ret);
if(ret == ret2){
break;
}
}
/* This part reads the memory that is on the keyboard. */
for (uint8_t timeout = 0; timeout < 15; timeout++) {
ret = USBH_HID_GetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
sizeof(kbd_led));
UsrLog("USBH_HID_GetReport return: %d; kbd_led: %d", ret, kbd_led[0]);
HAL_Delay(1);
if (ret == ret2) {
break;
}
}
if (kbd_led[0] == (LED_status & 7)) {
return HAL_OK;
} else {
return HAL_ERROR;
}
}
usbh_core.c:
uint32_t keyboard_led_status_tick = 0;
/**
* @brief USBH_Process
* Background process of the USB Core.
* @param phost: Host Handle
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_Process(USBH_HandleTypeDef *phost) {
[...]
case HOST_CLASS:
/* process class state machine */
if (phost->pActiveClass != NULL) {
/* Update keyboard LED status */
if (HAL_GetTick() > (keyboard_led_status_tick + 10)) {
keyboard_led_status_tick = HAL_GetTick();
HID_HandleTypeDef *HID_Handle =
(HID_HandleTypeDef*) phost->pActiveClass->pData;
if (HID_Handle->Init == USBH_HID_KeybdInit) {
USB_Set_Keyboard_LED_Status();
}
}
phost->pActiveClass->BgndProcess(phost);
}
break;
case HOST_DEV_DISCONNECTED:
[...]
This video is not about USB Host, but it helped me understand a little better about Report Descriptor:
[Custom HID keyboard device | VIDEO 33]:
2021-11-19 08:39 PM
Best result:
case HOST_CLASS:
/* process class state machine */
if (phost->pActiveClass != NULL) {
/* Update keyboard LED status */
if (HAL_GetTick() > (keyboard_led_status_tick + 10)) {
keyboard_led_status_tick = HAL_GetTick();
HID_HandleTypeDef *HID_Handle =
(HID_HandleTypeDef*) phost->pActiveClass->pData;
if (HID_Handle->Init == USBH_HID_KeybdInit) {
USB_Set_Keyboard_LED_Status();
}
}
phost->pActiveClass->BgndProcess(phost);
}
break;
case HOST_DEV_DISCONNECTED:
2021-11-19 08:56 PM
This part reads the memory status that is on the keyboard:
for (uint8_t timeout = 0; timeout < 15; timeout++) {
ret = USBH_HID_GetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
sizeof(kbd_led));
USBH_DbgLog("USBH_HID_GetReport return: %d; kbd_led: %d", ret, kbd_led[0]);
HAL_Delay(1);
if (ret == ret2) {
break;
}
}
I tried this after seeing someone say it wasn't possible. Again a misinterpretation of the confusing USB protocol manual:
https://community.st.com/s/global-search/%5Busb%20host%5D%20keyboard%20num%20lock
there is no way to read out the state of LEDs from the keyboard (it even may not implement the LEDs at all). This is even explicitly stated in the standard (HID1.11) in Appendix C:
Synchronization between LED states and CAPS LOCK , NUM L OCK , SCROLL L OCK ,
COMPOSE , and KANA events is maintained by the host and NOT the keyboard
2021-11-19 09:15 PM
I think the best way to control the status of these functions, indicated via the keyboard's LED, is like this:
2021-11-19 09:53 PM
Okay, that worked for me, maybe there's a better way to do it, but I have other things going on:
usb_host.c:
uint32_t keyboardMain_debounce = 0;
HID_KEYBD_Info_TypeDef *keybd_info1;
uint8_t kbd_LED_status[1] = { 0B001 }; // kbd_num_lock_state = 1
uint8_t kbd_LED_status_old = 0;
uint8_t kbd_num_lock_state = 1;
uint8_t caps_lock_state = 0;
uint8_t kbd_scroll_lock_state = 0;
void USBH_HID_EventCallback(USBH_HandleTypeDef *phost) {
HID_HandleTypeDef *HID_Handle =
(HID_HandleTypeDef*) phost->pActiveClass->pData;
if (HID_Handle->Init == USBH_HID_KeybdInit) {
keybd_info1 = USBH_HID_GetKeybdInfo(phost);
USBH_DbgLog("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
keybd_info1->keys[0], keybd_info1->keys[1],
keybd_info1->keys[2], keybd_info1->keys[3],
keybd_info1->keys[4], keybd_info1->keys[5]); // USBH_DbgLog
if (HAL_GetTick() > (keyboardMain_debounce + 100)) {
keyboardMain_debounce = HAL_GetTick();
uint8_t key = keybd_info1->keys[0];
if (key == KEY_INSERT) {
keyboard_insert = !keyboard_insert;
} else if (key == KEY_KEYPAD_NUM_LOCK_AND_CLEAR) {
if ((kbd_LED_status[0] & 1) == 0) {
kbd_LED_status[0] |= 0B001;
} else {
kbd_LED_status[0] &= 0B110;
}
} else if (key == KEY_CAPS_LOCK) {
if (((kbd_LED_status[0] >> 1) & 1) == 0) {
kbd_LED_status[0] |= 0B010;
} else {
kbd_LED_status[0] &= 0B101;
}
} else if (key == KEY_SCROLL_LOCK) {
if (((kbd_LED_status[0] >> 2) & 1) == 0) {
kbd_LED_status[0] |= 0B100;
} else {
kbd_LED_status[0] &= 0B011;
}
}
}
} else if (HID_Handle->Init == USBH_HID_MouseInit) {
USBH_HID_GetMouseInfo(phost);
USBH_DbgLog(
"Mouse action: x= 0x%x, y= 0x%x, button1 = 0x%x, button2 = 0x%x, button3 = 0x%x \n",
mouse_info.x, mouse_info.y, mouse_info.buttons[0],
mouse_info.buttons[1], mouse_info.buttons[2]);
}
}
usb_host.c:
/* USER CODE BEGIN 1 */
USBH_StatusTypeDef USB_Set_Keyboard_LED_Status(uint8_t *LED_status) {
if (kbd_LED_status_old == kbd_LED_status[0]) {
return HAL_OK;
}
kbd_LED_status_old = LED_status[0];
uint8_t kbd_led[2] = { 0x00, 0x00 };
USBH_StatusTypeDef ret = HAL_BUSY;
USBH_StatusTypeDef ret2 = HAL_OK;
kbd_led[0] = LED_status[0] & 7;
for (uint8_t timeout = 0; timeout < 15; timeout++) {
ret = USBH_HID_SetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
sizeof(kbd_led));
UsrLog("USBH_HID_SetReport return: %d", ret);
IWDG_delay_ms(1);
if (ret == ret2) {
break;
}
}
// ret = HAL_BUSY;
// ret2 = HAL_OK;
for (uint8_t timeout = 0; timeout < 15; timeout++) {
ret = USBH_HID_GetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
sizeof(kbd_led));
UsrLog("USBH_HID_GetReport return: %d; kbd_led: %d", ret, kbd_led[0]);
IWDG_delay_ms(1);
if (ret == ret2) {
break;
}
}
if (kbd_led[0] == (LED_status[0] & 7)) {
ret = HAL_OK;
} else {
ret = HAL_ERROR;
}
LED_status[0] = kbd_led[0];
if ((kbd_LED_status[0] & 1) == 1) {
kbd_num_lock_state = 1;
} else {
kbd_num_lock_state = 0;
}
if (((kbd_LED_status[0] >> 1) & 1) == 1) {
caps_lock_state = 1;
} else {
caps_lock_state = 0;
}
if (((kbd_LED_status[0] >> 2) & 1) == 1) {
kbd_scroll_lock_state = 1;
} else {
kbd_scroll_lock_state = 0;
}
return ret;
}
usb_host.c:
static void USBH_UserProcess2(USBH_HandleTypeDef *phost, uint8_t id) {
/* USER CODE BEGIN CALL_BACK_21 */
switch (id) {
case HOST_USER_SELECT_CONFIGURATION:
break;
case HOST_USER_DISCONNECTION:
Appli_state = APPLICATION_DISCONNECT;
Appli_state_FS = Appli_state;
kbd_LED_status_old = 99; // <--------- to update keyboard if reconnected
usbh_core.c:
uint32_t keyboard_led_status_tick = 0;
/**
* @brief USBH_Process
* Background process of the USB Core.
* @param phost: Host Handle
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_Process(USBH_HandleTypeDef *phost) {
[...]
case HOST_CLASS:
/* process class state machine */
if (phost->pActiveClass != NULL) {
/* Update keyboard LED status */
if (HAL_GetTick() > (keyboard_led_status_tick + 10)) {
keyboard_led_status_tick = HAL_GetTick();
HID_HandleTypeDef *HID_Handle =
(HID_HandleTypeDef*) phost->pActiveClass->pData;
if (HID_Handle->Init == USBH_HID_KeybdInit) {
USB_Set_Keyboard_LED_Status();
}
}
phost->pActiveClass->BgndProcess(phost);
}
break;
case HOST_DEV_DISCONNECTED:
[...]