Showing posts with label USB. Show all posts
Showing posts with label USB. Show all posts

Tuesday, 2 January 2018

The Simplest USB HID Report Descriptor

It is often confusing for people including myself who just started to do programming for USB devices. USB protocol is complicated, a book about USB device can easily be hundreds of pages.

HID device could be the starting point for most developers, because it is relatively the simplest to do. Even though it is still difficult to handle and could cost weeks of time to get your first device working.

For me the most confusing concept is the report descriptor, I believe it is also the case for many others. Anyway once you understood it it's not so hard to handle.

HID is designed to work as an input or output device for the operating system, all the report descriptor do is to let the operating system understand what the data it send mean to the OS. For example the key that pressed, or the movement of the mouse. But for us developers we just need to use the HID interface and we use our own software to communicate with the device (unless you are developing a mouse or keyboard), we don't need the OS to understand our data. We do the report descriptor only because it's mandatory in the HID protocol.

So what we need to do is just keep the descriptor as simple as possible. According to the HID specification, the report descriptor must have the following parts:

  • Input
  • Usage
  • Usage Page
  • Logical Minimum
  • Logical Maximum
  • Report Size
  • Report Count

All the other items are optional. To generate a simplest report descriptor, we can use the official descriptor tool by the USB organization:
The order of the items does not matter, but to arrange it in a hierarchical order will make it easy to understand. I'll put the Usage Page first followed by the Usage, then the value range and report size and counts, ended with the Input.

Assuming we are going to transfer 2 bytes of data to the PC, our specific application software will know what the data means and how to use it, so what we need to tell the OS is simply there are 2 bytes of data. The final report descriptor will look like this:
It can be saved as a .h file and be used directly in C code. Use the 'Parse Descriptor' function to check if there's any error before save.

char ReportDescriptor[15] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x00,                    // USAGE (Undefined)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              // LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    // REPORT_SIZE (8)
    0x95, 0x02,                    // REPORT_COUNT (2)
    0x81, 0x02                     // INPUT (Data,Var,Abs)
};


Thursday, 13 July 2017

USB HID demo with STM32 and emWin and FreeRTOS





This is a demonstration of the HID communication. The computer program reads the LED status via the USB and shows it on screen with 3 big indicators. In the mean while the scroll bar on the computer is used to control the flashing speed of the LED on board.



The board is using a STM32F103 MCU which has a USB 2.0 device full speed port. I didn't use the ST peripheral library or the ST's USB stack, all the USB code was built from scratch and is completely interrupt driven. So the code is compact and efficient, the USB core code is less than 400 lines.



FreeRTOS is used as the operating system to provide support of the GUI and multi-tasking. 3 software timers were used to control the LED flashing speed, the GUI refreshing, and the USB idle time. The benefit of using the software timer of the RTOS is there is no additional hardware and software cost to the system.



The whole tool chain I used is free except the Keil compiler. It is possible to use a free GNU compiler, anyway I bought the Keil several years ago and I can't see any reason not using it by now. The emWin is free to use if you are using the STM32 chips thanks for a deal made between ST and Segger. FreeRTOS is free of charge, even for commercial purpose. The compiler for the Windows program is Embarcadero's C++ builder, which is free to build Win32 applications now, for both personal and commercial use.

I would like to provide the source codes for this demo, but haven't found a way to upload files in Blogger, which is this site based on, I'll update this blog when the download is available. Before that, maybe you can contact me to send you an email.

Saturday, 13 May 2017

Accessing USB buffer memory in STM32F10x

The USB device peripheral in STM32F10x seems not very well integrated in the chip like other parts. The USB related registers are even not defined in the header file stm32f10x.h , to programme with the USB, you have to define these registers by yourself.

Of course, you can use the factory provided peripheral library, and further more you can use the USB device stack which comes with the STM32CubeMX. But I like to do things in the way that writing and reading the registers directly. To me understanding other people's code is even harder than writing my own.

The job was done on a STM32F107, which has a dedicated 512 bytes buffer memory for the USB port. But this memory is not like the normal memory that you can access it with a simple read or write. In the reference manual, there's few words about it, user is only told the memory is "structured as 256 words by 16 bits", "all packet memory locations are accessed by the APB using 32-bit aligned addresses", and "the actual memory location address must be multiplied by two". It's very vague and the information is scattered in different places in the manual.

It cost me quite a while to figure out how to access the USB buffer memory. Let's get to the conclusion first:

  1. Don't use byte-width access, use 16bit, even if you just want to write 1 byte.
  2. The address in the buffer should be doubled
  3. When write a sequence of data, increase the buffer address double as the outside RAM.

If the address you are accessing is 8 in the USB buffer, or in another word you are accessing the 8th byte in the buffer, you have to write or read at BUFFER_BASE_ADDRESS+16.

In the USB device enumeration process, the micro-controller may need to send any amount of data from 1 byte to hundreds. But even if you just need to write 1 byte to the buffer, you need to use 16-bit access to write a 16bit word.

Here is my code to copy data to the USB buffer. DestAddr is the buffer address

    uint16_t *SourceAddr, *DestAddr;

    for (i=0; i<(BytesToMove+1)/2; i++)   // copy data to buffer
    {
        *DestAddr = *SourceAddr++;
        DestAddr += 2;
    }