USB HID generic example questions

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

USB HID generic example questions

1,640 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by emanuelj on Fri Oct 03 02:00:23 MST 2014
Hello everyone,

I'm a beginner in USB-programming and have some questions about USB-communication.
I'm using the usbd_lib_hid_generic-example (lpcopen v2.10) with the LPC1769 microcontroller and would like to make it communicating with a host-pc.
It already works fine, but what I don't understand is that I get an error when I want to send less (and more) than 8 bytes (which I configured in hid_desc.c) from PC to the device.

hid_desc.c is configured as it follows:

#define HID_INPUT_REPORT_BYTES       8/* size of report in Bytes */
#define HID_OUTPUT_REPORT_BYTES      8/* size of report in Bytes */
#define HID_FEATURE_REPORT_BYTES     8/* size of report in Bytes */


Means this that I have to transmit 8 bytes, no more, no less, to the device?
Would it be possible to change the code that this is only the MAXIMUM number of bytes, so that I can transmit less than 8 bytes?

Furthermore I don't know what FEATURE_REPORT means. Whats the purpose of a feature-report?

Thank you for your answers!

Emanuel
Labels (1)
0 Kudos
5 Replies

1,085 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mch0 on Wed Oct 08 10:50:49 MST 2014
Glad to help you, but at this point it might work only by sheer luck.

You have declared two pointers, but they are not initialized, i.e. can point to any location.
This might be currently unused RAM (lucky for the moment), but also could be ROM, nothing at all (unused address space) or in the worst case some *used* RAM.
The last case is the most dangerous as you will later get unexplained errors or program crashes.

You could instead simply write this:

USBD_API->hw->ReadEP(hUsb, pHidCtrl->epout_adr, (uint8_t*)buffer);


Then your pointer is generated "on the fly", it points to a defined location in RAM and you don't have to copy the data in the loop.
It's already stored there by the USB stack.

The second part is OK since you initialize the pointer.
You could save the additional pointer as well:

USBD_API->hw->WriteEP(hUsb, pHidCtrl->epin_adr, (uint8_t*)message, REPORT_SIZE);

But that's only for elegance :), your solution is quite as well.
With your definitions (unsigned char) of the buffers the two typecasts to uint8_t  I introduced are probably superfluous, depends on the defintion of the type. But they don't add code either.

Mike


0 Kudos

1,085 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by emanuelj on Wed Oct 08 10:17:42 MST 2014
Thank you for your great answer, now I got it!
I've implemented all my ideas and it's all working.

On the device-side I've converted the incoming report to a char[] via typecast:

static uint8_t *incoming_report;
static uint8_t *outgoing_report;
#define REPORT_SIZE 16
unsigned char buffer[REPORT_SIZE];
unsigned char message[REPORT_SIZE];

...

USBD_API->hw->ReadEP(hUsb, pHidCtrl->epout_adr, incoming_report);

for (i=0; i<= REPORT_SIZE; i++)
{
buffer = (unsigned char)(*(incoming_report+i));
}

//Do something ...

sprintf((char *)message, "value: %d", value);
outgoing_report = &message[0];


USBD_API->hw->WriteEP(hUsb, pHidCtrl->epin_adr, outgoing_report, REPORT_SIZE);

...


maybe not the most elegant method but it's perfectly working

best regards
Emanuel
0 Kudos

1,085 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mch0 on Sun Oct 05 03:33:48 MST 2014
Hi,

I can't give a tutorial on HID or C, but I'll try to give you a starter.

uint8_t *:
This is actually a perfect fit for the generic reports.
These are almost always defined as an array of n bytes (see report descriptor) and the uint8_t-pointer just points to the start of the report you want to receive (OUT) or send (IN).
Within the device you might then allocate two static arrays like uint8_t outreport[8] and uint8_t inreport[4].
The host sends an OUT-report with 8 bytes (uint8_t), 7 bytes as defined by your format and a terminating 0 for convenience.
On the host side this could look like this:

char buffer[8];
sprintf(buffer,"%03d:%03d",num1,num2);
hid_write(buffer);


Then these 8 bytes arrive at the device and are (see later) available in the buffer outreport.

On the device side you retrieve them like this:

if (2== sscanf((char*)outbuffer,"%d:%d",&num1,&num2))
{
   // got them
  ...
}


Likewise for the answer you sprintf() on the device side into the inbuffer and sscanf() on the host side.
If your host program is compiled on a windows system make sure that you use ASCII-chars (bytes), not the native WCHAR type (16 bit).

You see, nothing arkward at all, just typecast the uint8_t-pointer to let it point to the type you need.


USB stack usage:
The NXP ROM stack could have been documented a little bit better, but for a simple HID device it's very easy.
The most important thing is to get used to callback functions. The device can NEVER EVER initiate any data transfer, it just can react to events.
So when something happens to an endpoint because THE HOST has done something your program is interrupted, the stack decodes the event and calls the endpoint handler.
Here this is HID_Ep_Hdlr().
You can then look at the event (3rd param) and take actions as appropriate.

Commonly you would react to these events:
USB_EVT_IN: The prepared IN-report has been sent (your ACK).

USB_EVT_OUT_NAK: The host tried to send a report, but the stack told the host to try later again. This is a good place for you to place a read request. Then the next try of the host will be accepted and you will get notified.
Example code:
USBD_API->hw->ReadReqEP(hUsb, pHidCtrl->epout_adr, outbuffer, 8);

USB_EVT_OUT: A new report from the host is availabe and you can retrieve it.
Example code:
n=USBD_API->hw->ReadEP(hUsb, pHidCtrl->epout_adr, outbuffer);

Now assume you want to PREPARE a report to the host:
This could like this (anywhere in the code) for your format:
n=USBD_API->hw->WriteEP(g_hUsb, pHidCtrl->epin_adr, inbuffer, 4);

When the host has retrieved this report you get the USB_EVT_IN event in the callback function.

For a starter or very short actions you can do all required actions within the callback handler.
For actions that take more time better set a flag (volatile variable) and handle the action in the background.

Mike






1,085 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by emanuelj on Sat Oct 04 12:56:39 MST 2014
Ok, thank you for your reply! I understand.

Now I want transmit a string, but the type of the report (which is *uint8) is therefore inappropriate.

An example string (from PC->device) consists out of 3 letters, a colon and a 3-digit number
Example-format: "XXX:000"
The device should interpret the message and answer for instance with an acknowlegde, which could have the form "ACK"

What's the easiest way doing this? With an *uint8 format this is very awkward.

Another problem in my case is the WriteEP() function. I can only call the function in HID_Ep_Hdlr() because of the set of parameters. How can I achieve a WriteEP()-function which may be called from everywhere in my program? Best would be a WriteEP(char message[len]) function.

Sry for my (probably stupid) questions, but I'ts the first time I've ever programmed with USB and with such a big library.

Thank you for your replies!
0 Kudos

1,085 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mch0 on Fri Oct 03 09:23:36 MST 2014
Hi,

yes, 8 means 8 in this case.
You may define more than one report of each type and these can then have different length, say three OUT reports with the first having 4 bytes, the second 7 and the third 17.
Then you can choose which report and hence the number of bytes yo want to send.
However, according to the standard, you are still restricted to the set of discrete numbers you have specified.
Unless you know what you are doing (more than one report of a type involves an additional ID byte) I recommend you simply define a MAX number and pad reports on the host side and discard those bytes on the device side and vice versa.

A feature report is transferred via EP0 and uses control transfers, whereas the other two are transferred via the specified EPs and use interrupt transfers. Again I'd say for a starter use IN/OUT.
The only exception is, when you request some specific information from a device out of several choices. Then you must use feature reports, because with IN reports the device decides what to send.

Anyway, I recommend reading some small tutorial on HID devices, there are lots of them.

Best regards,

Mike
0 Kudos