Author Topic: USB HID Communication  (Read 40320 times)

vikram_t80

  • Member
  • ***
  • Posts: 13
USB HID Communication
« on: November 03, 2011, 01:25:13 am »
Hi Jan,

I am using STM3220G - EVAL board from ST microelectronics and am working on USB communication method to send and receive data via USB to and from host (in my case I am using USB demonstrator a PC application as a host).
Now the STM3220G - EVAL module has only come along with joystick application hence I am only able to design a code that can receive data from device (i.e., STM32F207 microcontroller) to host (USB demonstrator).

I have earlier used STM3210C-EVAL module that had come along with generic HID example which had STM32F107 microcontroller. On this board I  managed to send and receive data.
But since my project requires to use STM32F207 microcontroller series I started to work on this new eval module.

In the example code for joystick I understood that when the joystick on the board is moved in either direction, the execution jumps to SysTick handler and from there on calls USBD_HID_SendReport() function which is in turn calls DCD_EP_Tx() function which is the function used to send report from device to host.(As per the USB theory that I have read). This function checks whether the EP is 0(control endpoint) or any other endpoint(In my case I am using Interrupt endpoint for data transfer) and then calls function USB_OTG_EPStartXfer() which intiates the transfer of data bytes.

Now the problem is that since joystick application only deals with input report this demo example code is not useful. And after checking through the entire code I found that as the function USBD_HID_SendReport() is called to send the report there is no function called to received report. Hence I wrote a small piece of code stating USBD_HID_ReceiveReport() which calls the function DCD_EP_PrepareRx() which is used to receive report(as per the demo application theory).The theory i am mentioning is actually a user manual provided by ST (UM1021 User manual).

CAn you please let me know where can I call this receive report function or in which handler so that it starts receiving the report data and what buffer will it show the data in?

Currently I have placed this USBD_HID_ReceiveReport() function in DCD_HandleOutEP_ISR() handler.

Thankyou in advance and sorry for such a long post.
I am stuck at this stage since last 2 days.

Regards,
Vikram

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: USB HID Communication
« Reply #1 on: November 03, 2011, 11:29:48 am »
I'm not familiar with the ST microelectronics example code. However, at the device, bulk and interrupt transfers are identical (except for allowed max packet sizes), so if ST provides any example code that uses interrrupt or bulk OUT (host-to-device) transfers, you might look at that for clues.

Jan

Tsuneo

  • Frequent Contributor
  • ****
  • Posts: 145
Re: USB HID Communication
« Reply #2 on: November 03, 2011, 11:45:51 am »
Hi Vikram and Jan,

As Jan suggested, CDC and MSC examples are worth to look in.

Vikram,
Which transfer type do you apply for the output report, interrupt OUT pipe or Set_Report request?

Tsuneo

vikram_t80

  • Member
  • ***
  • Posts: 13
Re: USB HID Communication
« Reply #3 on: November 03, 2011, 11:51:58 pm »
Thankyou Jan and Tsuneo for your quick response.

I am using Interrupt OUT pipe in my code which seems to be default transfer type used for HID class.
One more thing that I got to know from the demo code that there is also VCP (Virtual communication Protocol) code provided and that uses BULK transfer type but only on Control End point 0.
Whereas for HID class the Control endpoint 0 is used only while enumeration.
In the VCP code which also shows how the data is transfered from host to device I also got to the handler which handles this routine but the problem is that I am not able to understand where in the entire code the value of ep's transfer buffer is sent to Receive_buffer( as this is the one which gets updated with the data that I send on VCP utility).

Regards and thanks in advance.

Tsuneo

  • Frequent Contributor
  • ****
  • Posts: 145
Re: USB HID Communication
« Reply #4 on: November 04, 2011, 03:03:43 am »
Quote
Currently I have placed this USBD_HID_ReceiveReport() function in DCD_HandleOutEP_ISR() handler.

Umm..
In the device implementation of "STM32_F105-07_F2xx_USB-Host-Device_Lib_V2.0.0",
when a transfer (transaction) completes on an OUT endpoint,
DCD_HandleOutEP_ISR() calls USBD_DataOutStage() (usbd_core.c) callback, and then this callback calls DataOut() callback of a device class driver. I recommend you to apply this original code flow of the library, instead of touching to DCD_HandleOutEP_ISR() directly.

On usbd_hid_core.c,
- Add an interrupt OUT endpoint to the configuration descriptor set.
- Make a DataOut routine, and register it to the callback table (structure).
- In USBD_HID_Init(), prepare OUT endpoint for reception

Interrupt OUT endpoint
Code: [Select]
STM32_F105-07_F2xx_USB-Host-Device_Lib_V2.0.0\Libraries\STM32_USB_Device_Library\Class\hid\inc\usbd_hid_core.h

#define USB_HID_CONFIG_DESC_SIZ       ( 34 + 7 )         // <----- increase config size by the OUT EP descriptor


STM32_F105-07_F2xx_USB-Host-Device_Lib_V2.0.0\Libraries\STM32_USB_Device_Library\Class\hid\src\usbd_hid_core.c

/* USB HID device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
  ... 
  ... 
  /************** Descriptor of Joystick Mouse interface ****************/
  /* 09 */
  0x09,         /*bLength: Interface Descriptor size*/
  USB_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/
  0x00,         /*bInterfaceNumber: Number of Interface*/
  0x00,         /*bAlternateSetting: Alternate setting*/
  0x02, /* 0x01, */        /*bNumEndpoints*/               // <--------- touch to number of endpoints
  0x03,         /*bInterfaceClass: HID*/
  0x00, /* 0x01, */         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
  0x00, /* 0x02, */         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
  0,            /*iInterface: Index of string descriptor*/
  ... 
  ... 
                                    // <------  append OUT endpoint descriptor

  /******************** Descriptor of Mouse endpoint ********************/
  /* 34 */
  0x07,          /*bLength: Endpoint Descriptor size*/
  USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
 
  HID_OUT_EP,    /*bEndpointAddress: Endpoint Address (OUT)*/
  0x03,          /*bmAttributes: Interrupt endpoint*/
  HID_OUT_PACKET, /*wMaxPacketSize */
  0x00,
  0x0A,          /*bInterval: Polling Interval (10 ms)*/
  /* 41 */
} ;

Registration of DataOut routine and its implementation
Code: [Select]
static uint8_t  USBD_HID_DataOut( void *pdev, uint8_t epnum );  // prototype of DataOut callback

USBD_Class_cb_TypeDef  USBD_HID_cb =
{
  USBD_HID_Init,
  USBD_HID_DeInit,
  USBD_HID_Setup,
  NULL, /*EP0_TxSent*/ 
  NULL, /*EP0_RxReady*/
  USBD_HID_DataIn, /*DataIn*/
  USBD_HID_DataOut,  /* NULL, */ /*DataOut*/  // <----------- register DataOut callback
  NULL, /*SOF */
  NULL,
  NULL,     
  USBD_HID_GetCfgDesc,
#ifdef USB_OTG_HS_CORE 
  USBD_HID_GetCfgDesc, /* use same config as per FS */
#endif 
};


#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
  #if defined ( __ICCARM__ ) /*!< IAR Compiler */
    #pragma data_alignment=4   
  #endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t USB_Rx_Buffer[ HID_OUT_PACKET ] __ALIGN_END ;


static uint8_t  USBD_HID_Init (void  *pdev, uint8_t cfgidx)
{
 
  /* Open EP IN */
  DCD_EP_Open(pdev,
              HID_IN_EP,
              HID_IN_PACKET,
              USB_OTG_EP_INT);
 
  /* Open EP OUT */
  DCD_EP_Open(pdev,
              HID_OUT_EP,
              HID_OUT_PACKET,
              USB_OTG_EP_INT);
                                     // <------  add this block, from here
  /* Prepare Out endpoint to receive next packet */
  DCD_EP_PrepareRx(pdev,
                   HID_OUT_EP,
                   (uint8_t*)(USB_Rx_Buffer),
                   HID_OUT_PACKET);
                                     // <------  to here
  return USBD_OK;
}


static uint8_t  USBD_HID_DataOut( void *pdev, uint8_t epnum )
{
  uint16_t USB_Rx_Cnt;
 
  if ( epnum != (HID_OUT_EP & 0x0F) )
    return USBD_FAIL;

  /* Get the received data buffer and update the counter */
  USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count;
 
  //
  // process output report on USB_Rx_Buffer, here
  //
 
  /* Prepare Out endpoint to receive next packet */
  DCD_EP_PrepareRx(pdev,
                   HID_OUT_EP,
                   (uint8_t*)(USB_Rx_Buffer),
                   HID_OUT_PACKET);

  return USBD_OK;
}

Tsuneo

vikram_t80

  • Member
  • ***
  • Posts: 13
Re: USB HID Communication
« Reply #5 on: November 04, 2011, 06:12:16 am »
Thanks Tsuneo for the quick response.

It works.

Just have one query!

Is it possible to have send and receive interrupt acting together at the same time?
I mean to say when I click the run button in the Keil compiler to check the working of code is it possible that within the while loop both data send and receive is executed together?

Thanks & Regards

Tsuneo

  • Frequent Contributor
  • ****
  • Posts: 145
Re: USB HID Communication
« Reply #6 on: November 04, 2011, 08:56:14 am »
Quote
Is it possible to have send and receive interrupt acting together at the same time?

Yes.
IN and OUT endpoints work independently, even if they share the same endpoint number.

Quote
I mean to say when I click the run button in the Keil compiler to check the working of code is it possible that within the while loop both data send and receive is executed together?

The timing of transaction complete is determined by host, when the device endpoints are ready for transactions. The completion of IN and OUT may occur in a same cycle of your while loop, or either one may occur after a couple of cycles.

If you want to control the timing (at least, the order) on the device side, keep the endpoint in not-ready (not-armed). IN endpoint is kept in not-ready, until DCD_EP_Tx() is called. OUT endpoint is in not-ready, until DCD_EP_PrepareRx() is called (see above USBD_HID_DataOut() implementation). While in not-ready, both endpoints return NAK to host, which is intrinsic flow control of USB.

Above implementation processes OUT transaction in hardware interrupt. If you like to process it in a polling loop,
- USBD_HID_DataOut() just raises a flag, and returns without doing further.
- In your polling loop, watch this flag. When it is up, drop it and do the contents of above USBD_HID_DataOut().

Tsuneo

vikram_t80

  • Member
  • ***
  • Posts: 13
Re: USB HID Communication
« Reply #7 on: November 07, 2011, 12:18:53 am »
Thanks a lot for your inputs.

I am able to get two way communication on USB HID class.

I now want to design a PC utility that will have few options like selecting the binary file that needs to be transferred and show the status of transfer.

Can you give me some idea how to write PC utility as this will be the first time I will be writing PC utility?
I am aware of C programming language.

Thanks and regards,
Vikram

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: USB HID Communication
« Reply #8 on: November 07, 2011, 09:41:40 am »
The .NET Framework has classes for working with files:

http://msdn.microsoft.com/en-us/netframework/aa496123

Jan

vikram_t80

  • Member
  • ***
  • Posts: 13
Re: USB HID Communication
« Reply #9 on: November 07, 2011, 10:44:32 pm »
Thankyou Jan,

I will look into the files.

I am also trying to write the code for error detection and correction using CRC.
In the development demo module of the STM3220G-EVAL they have provided a crc.c files which handles the functions related to getting the CRC code for given data considering the generator polynomial of degree CRC-32 as defined in their user reference manual for STM32F207 microcontroller.

I tried to use the function after my all data gets transferred from host to device where I gather the data in a buffer defined internally.

I use the function CRC_CalcBlockCRC() and then the CRC_GetCRC function.

But when I try to take the return value of this function in a dummy variable defined as uint32 type it says that the variable is defined but never used (which seems to be confusing!) as I am already assigning the return value of the functions.

Morever the function just runs the loop but never increments the index value and directly jumps out of the loop when value of index reaches the BufferLength. Below is the function:

uint32_t CRC_CalcBlockCRC(uint32_t pBuffer[], uint32_t BufferLength)
{
  uint32_t index = 0;
 
  for(index = 0; index < BufferLength; index++)
  {
    CRC->DR = pBuffer[index];
  }
  return (CRC->DR);
}

and i am calling this function in the function as defined below:

unsigned int abc;
uint16_t USB_Rx_Cnt;
static uint8_t  USBD_HID_DataOut (void  *pdev,
                              uint8_t epnum)
{
 
  unsigned int i=0;
 

  STM_EVAL_LEDOn(LED1);
  /* Get the received data buffer and update the counter */
  USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count;

 
 /* Prepare Out endpoint to receive next packet */
  DCD_EP_PrepareRx(pdev,
                   HID_OUT_EP,
                  (uint8_t*)(USB_HID_RX_BUF),
                   HID_OUT_PACKET);
 
  for(i=0; i<0x0F; i++)
  {
     Buf[j] = USB_HID_RX_BUF;
   j++;
  }
 
  if(j==MAX_DATA_XFER)
  {
     BufferFull = 1;
      CRC_ResetDR();
    abc = CRC_CalcBlockCRC((uint32_t*)Buf,0x0F);
    abc = CRC_GetCRC();
  }

  DCD_EP_Flush(pdev, HID_OUT_EP);
 
  return USBD_OK;
}
/**

Can you please help me to get an idea why this would be happening.

Thanks and regards,
Vikram

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: USB HID Communication
« Reply #10 on: November 08, 2011, 11:37:33 am »

vikram_t80

  • Member
  • ***
  • Posts: 13
Re: USB HID Communication
« Reply #11 on: November 25, 2011, 04:59:52 am »
Hi Jan,

Thanks for your support all the way in improving my application development.

I am confused and bit stuck at one point now.

I have designed a PC utility based on the USB HID demonstrator available from ST Microelectronics.

Here instead of writing the bytes that are needed to be send to device in the Output Report box, I have written a code and designed a select binary button. Once this button is clicked it allows to select the binary files located on the local drive to be uploaded.

This works fine.
But the problem is once the write button is clicked which actually goes through the routine defined in demonstrator.cpp file it only writes to device the last 256 bytes of the total bytes within the selected file send.
Here
I have mentioned 256 bytes because that is the report count I have set up in the device side descriptor members.
Below is attached the view of demonstrator and the code that it is writting to device.




fp = fopen(PathName,"rb");//opens the file after the select binary button is clicked
for(i=0;i<even;i++)// even is the number total file size divided by 256
         {
            
            fread(pData,sizeof(char),256,fp);
            memcpy(&OutputReport, pData, OutSize);  //send to device
            OutputReport[0] = 0x04;
            UpdateData(FALSE);
            WRITE_ROPRT = TRUE;
                        }
fclose(fp);

Once when WRITE_RPORT == TRUE
then
it calls the write handle
and
DWORD WINAPI CUsbHidDemonstratorDlg::WriteReport(void*)
{
   DWORD   BytesWritten = 0;
   INT      Index =0;
   //OutputReport[0] = 0x04;
   BOOL Result = TRUE;
    while(TRUE)
   {
      if(WRITE_ROPRT)
      {
         if (HidDeviceObject != INVALID_HANDLE_VALUE)
         {
            CancelIo(HidDeviceObject);
            
            if(Use_Setxxx == 1 )
            {
               //OutputReport[0] = 0x04;
               Result = HidD_SetFeature
               (HidDeviceObject,
                  OutputReport,
                  Capabilities.FeatureReportByteLength);
            }
            else
            {
               
               
               Result = WriteFile
                  (HidDeviceObject,
                  &OutputReport,
                  Capabilities.OutputReportByteLength,
                  &NumberOfBytesWriten,
                  (LPOVERLAPPED) &HIDOverlapped);
            }
            /*if(InputReport[2] == 0x01)
               WaitForSingleObject(ReadThread, INFINITE);*/

            WRITE_ROPRT = FALSE;
         }
      }
      _sleep(25);
   }

What I want to do is that when first 256 bytes are read in the Output report and the WRITE_RPORT is made true the host should write this bytes to device and wait for acknowledge which is what I have writtin in my device application code.

I am checking whether the Buffer array count is reached 256 or not.
If yes it writes BufferFull = 1
and this value I send as Input report's second byte to host.

I want the host to wait untill it receives this signal and only then it should send the next 256 bytes and so on.

I tried to search on net but was not successful.

Sorry for such long post.

Please need help.

Thanks in advance
Vikram

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: USB HID Communication
« Reply #12 on: November 25, 2011, 09:53:43 am »
I don't see any code that reads from the device.

Use a protocol analyzer or breakpoints and watch variables to find out exactly what is happening. 

Jan

spark

  • Member
  • ***
  • Posts: 2
Re: USB HID Communication
« Reply #13 on: March 06, 2012, 06:54:20 pm »
Quote
Is it possible to have send and receive interrupt acting together at the same time?

Yes.
IN and OUT endpoints work independently, even if they share the same endpoint number.

Quote
I mean to say when I click the run button in the Keil compiler to check the working of code is it possible that within the while loop both data send and receive is executed together?

The timing of transaction complete is determined by host, when the device endpoints are ready for transactions. The completion of IN and OUT may occur in a same cycle of your while loop, or either one may occur after a couple of cycles.

If you want to control the timing (at least, the order) on the device side, keep the endpoint in not-ready (not-armed). IN endpoint is kept in not-ready, until DCD_EP_Tx() is called. OUT endpoint is in not-ready, until DCD_EP_PrepareRx() is called (see above USBD_HID_DataOut() implementation). While in not-ready, both endpoints return NAK to host, which is intrinsic flow control of USB.

Above implementation processes OUT transaction in hardware interrupt. If you like to process it in a polling loop,
- USBD_HID_DataOut() just raises a flag, and returns without doing further.
- In your polling loop, watch this flag. When it is up, drop it and do the contents of above USBD_HID_DataOut().

Tsuneo

Hello guys,
this forum is great. It helped me with my diploma. I'm writing a TMC class for USB. I used HID example as base. I managed to get receive (rx) of data working. I have DCD_EP_PrepareRx() in init and at the end of DataOut stage. But i don't understand how to transmit (tx) data.
  If i understand the teory right, when the device receives IN token, it sets IN interrupt and i get to my callback function throught USBD_OTG_ISR_Handler -> DCD_HandleInEP_ISR -> class_cb-> DataIn. There i should call DCD_EP_Tx() to send data ?
  With my current code, i have one DCD_EP_Tx() in DataOut stage and one in DataIn stage. If i have Tx function either only in DataOut or only in DataIn, it won't send data.
Here is my code:    http://pastebin.com/v5B8E94F
And here is a screenshot:   https://picasaweb.google.com/117645259943730105591/Miscellaneous#5716934778930016946
It's my diploma and i'm not C/ARM guru so it's a bit messy , sorry for that.

I have another question. Is there any easy way to check, if rx/tx fifos are full/empty ?
I saw in processor manual that there are some interrupt registers, which mean, when they are set, it sould call some interrupt routine which i could use to set/reset some flag indicating if fifo is full/empty ( something simmilar as mentioned before ) ... but, is there any easier way how to manage it ?

Thanks for any of your answers.

spark

  • Member
  • ***
  • Posts: 2
Re: USB HID Communication
« Reply #14 on: March 20, 2012, 03:28:08 am »
Quote
Is it possible to have send and receive interrupt acting together at the same time?

Yes.
IN and OUT endpoints work independently, even if they share the same endpoint number.

Quote
I mean to say when I click the run button in the Keil compiler to check the working of code is it possible that within the while loop both data send and receive is executed together?

The timing of transaction complete is determined by host, when the device endpoints are ready for transactions. The completion of IN and OUT may occur in a same cycle of your while loop, or either one may occur after a couple of cycles.

If you want to control the timing (at least, the order) on the device side, keep the endpoint in not-ready (not-armed). IN endpoint is kept in not-ready, until DCD_EP_Tx() is called. OUT endpoint is in not-ready, until DCD_EP_PrepareRx() is called (see above USBD_HID_DataOut() implementation). While in not-ready, both endpoints return NAK to host, which is intrinsic flow control of USB.

Above implementation processes OUT transaction in hardware interrupt. If you like to process it in a polling loop,
- USBD_HID_DataOut() just raises a flag, and returns without doing further.
- In your polling loop, watch this flag. When it is up, drop it and do the contents of above USBD_HID_DataOut().

Tsuneo

Hello guys,
this forum is great. It helped me with my diploma. I'm writing a TMC class for USB. I used HID example as base. I managed to get receive (rx) of data working. I have DCD_EP_PrepareRx() in init and at the end of DataOut stage. But i don't understand how to transmit (tx) data.
  If i understand the teory right, when the device receives IN token, it sets IN interrupt and i get to my callback function throught USBD_OTG_ISR_Handler -> DCD_HandleInEP_ISR -> class_cb-> DataIn. There i should call DCD_EP_Tx() to send data ?
  With my current code, i have one DCD_EP_Tx() in DataOut stage and one in DataIn stage. If i have Tx function either only in DataOut or only in DataIn, it won't send data.
Here is my code:    http://pastebin.com/v5B8E94F
And here is a screenshot:   https://picasaweb.google.com/117645259943730105591/Miscellaneous#5716934778930016946
It's my diploma and i'm not C/ARM guru so it's a bit messy , sorry for that.

I have another question. Is there any easy way to check, if rx/tx fifos are full/empty ?
I saw in processor manual that there are some interrupt registers, which mean, when they are set, it sould call some interrupt routine which i could use to set/reset some flag indicating if fifo is full/empty ( something simmilar as mentioned before ) ... but, is there any easier way how to manage it ?

Thanks for any of your answers.


Hello guys, i solved the problem,
it was because i was developing it in virtual windows xp on virtual box. When i switched to read HW, it worked beautifly. The same happened when i connected my device to the usb hub.