Saturday, September 3, 2016

This blog code repository available as ZIP file in Code archive since service closed by Google

I get occasional code request since Google stopped providing Code service. I didn't found time to move the repository to other provider. Links in posts would also need updating which I didn't give much thought yet.

Until migration the code is still available in Code archives as ZIP file:

https://code.google.com/archive/p/found-bits/source/default/source

P.S.: This post will be updated with new location after migration.

Saturday, October 10, 2015

DHT22 humidity and temperature published to dweet io with CC3200MOD from eBay

I see great potential in CC3200 for low power WiFi device. I mean battery powered and hibernating most of the time. TI promises years long operation this way. I wanted to test hibernation consumption on CC3200-LAUNCHXL eval board I played with before. Unfortunately LEDs and some other ICs stay powered on the board even if configured to be supplied with two AA batteries. TI did own low power tests without them. That meant unsoldering which I didn't want to do.
CC3200MOD seemed like a good alternative. I got one from eBay (even paid extra 2 bucks for faster shipping in hope that it will arrive sooner).

CC3200-LAUNCHXL

While waiting for CC3200MOD I wrote an application to read humidity and temperature data from DHT22 sensor wired to LAUNCHPAD with 3 wires.


It went smoothly. Results were instant. I decided to post data from sensor to dweet.io. dweet.io is a service to store small amounts of data (up to 2000 characters at the time of writing this) with http GET or POST requests. Data is then available (with up to 500 history entries at the time of writing this) to retrieve by other applications or just visualize on their page. JSON format is used for data.


CC3200MOD

Flat cable with 1.27mm spacing needed to be soldered to received CC3200MOD (it was a process I would rather not repeat - I like software more and more :) ). Additionally I crimped connector on the cable. I was worried that module schematics will be a mystery since I found no official document but it seems that schematics is like suggested by TI. I couldn't figure out the SPI FLASH device as marking seem custom.
[UDATE] According to SPI flash ID read out with command 0x9F returning EF 40 14 it should be W25Q80 8Mbit device from Winbond.


Photo below shows CC3200MOD with flat cable breakout, MM232R module from FTDI with FT232RQ USB to UART bridge for UNIFLASH software from TI to load firmware, iTAG50 debugger from iSYSTEM connected to debug pins, DHT22 and 2xAA battery pack for supply.



Code

I don't have nicely packed project to show the code (yet). Posting to dweet.io is shown below. Other than that it's DHT22 reading (you can get code for this everywhere on the web) and hibernation call in a loop.

#define SERVER_NAME "dweet.io"
void POST2dweet_io(float fTemp, float fHum)
{
  signed char g_Host[] = SERVER_NAME;
  unsigned int uiIP;
  long lRetVal = sl_NetAppDnsGetHostByName(g_Host, strlen((const char *)g_Host), (unsigned long*)&uiIP, SL_AF_INET);

  int iSockID = 0;
  iSockID = sl_Socket(SL_AF_INET,SL_SOCK_STREAM, 0);

  SlSockAddrIn_t    Addr;
  int    iAddrSize;
  Addr.sin_family = SL_AF_INET;
  Addr.sin_port = sl_Htons(80); // default port for http
  Addr.sin_addr.s_addr = sl_Htonl(uiIP);
  iAddrSize = sizeof(SlSockAddrIn_t);

  lRetVal = sl_Connect(iSockID, ( SlSockAddr_t *)&Addr, iAddrSize);

  const char *pPOSTHeaderFormat =
    "POST http://dweet.io/dweet/for/CC3200MODDHT22 HTTP/1.1\r\n"
    "Content-Type: application/json\r\n"
    "Content-Length: %d\r\n"
    "\r\n";
  const char *pPOSTBodyFormat =
    "{\"Hum\":%3.1f,\"Tmp\":%3.1f}";

  char acSendBuff[512] = { 0 };
  long lBodyLen = sprintf(acSendBuff, pPOSTBodyFormat, fHum, fTemp);
  lBodyLen = sprintf(acSendBuff, pPOSTHeaderFormat, lBodyLen);
  lBodyLen += sprintf(acSendBuff + lBodyLen, pPOSTBodyFormat, fHum, fTemp);
  acSendBuff[lBodyLen] = 0;
  int iTXStatus;
  iTXStatus = sl_Send(iSockID, acSendBuff, lBodyLen, 0);
}

Current usage

I couldn't measure hibernation consumption. It seems that my ampere meter has too much influence on supply rail. It looks like CC3200 is restarted on supply voltage drop in the moment of consumption change. Without ampere meter CC3200 works on lower voltages (tested with two rechargeable batteries at 2.4V).
Using active supply instead of AA batteries or proper shunt resistor for current measurement helped. I got cca. 5uA hibernation consumption and 25mA active mode consumption. I would agree that It takes around 5s for CC3200 to wake up, read DHT22 connect to WLAN and send data. From this one can evaluate how long a set of average AA with 2000mAh capacity will last. It's one year by waking up every 10 minutes.
Simplified calculation without taking into account all variables (e.g. battery self discharge) is as follows:
Charge used during active period:
Qap = Ia * Ta = 25mA * 5s = 125mAs
Charge used during idle period:
Qip = Ii * (Tp - Ta) = 5uA * (10min - 5s) = 2.975mAs
Sum of two makes up total charge used in one period:
Qpp = Qap + Qip = 127.975mAs (= 128mAs)
So the count of all periods per available charge in batteries is:
N = Qb / Qpp = 2000mAh / 128mAs = 56250
And total time:
T = N * Tp = 56250 * 10min = 390days

You can use function below in Google Sheets to test other combinations (provide values in SI units - s, A, As).

function BattDuration(BattCapacity, Iidle, Iactive, Tactive, Tperiod)
{
  var QperPeriod = Iidle * (Tperiod - Tactive) + Iactive * Tactive;
  var numCycles = BattCapacity / QperPeriod;
  var duration = numCycles * Tperiod;
  return duration;
}


Note: current in hibernation mode was measured with DHT22 disconnected!

CC3200 is awesome

With integrated FLASH it would be even better. But from what I understand it is difficult to mix silicon process for analog and FLASH. Maybe in next generation?
I don't get why ESP8266 is generating so much noise while CC3200 is getting so little coverage. I would agree that it depends on the usage scenario where for low power CC3200 wins as it was designed for it. But it also works without hibernation. And it offers more than twice as much with less than half hassle to get it to work for twice the price of a module compared to ESP8266. Is the $5 absolute price all we are going for? From what I read on the internet (and I could be wrong) ESP8266 doesn't have popular CPU with compilers and debuggers available - young open source solutions don't count. I didn't see HTTP server ready to use on ESP8266 nor out-of-the-box SSL support like CC3200 has. Not to mention peripherals, more RAM and bigger SPI FLASH support. I'm not saying CC3200 is better than ESP8266, except that it is :). Again, I might be biased because I never used ESP8266 and I don't want to start a war. However I did decide on CC3200 based on information I got from internet.

Wednesday, June 3, 2015

Before main()

I had a short presentation on build process and C program startup for embedded projects. Below are the slides from presentation with some additional info added for the purpose of this post.

In general I wanted to point out that most C compilers work in same fashion. Showcase examples below are based on GCC for Cortex M. Others compilers have equal functionality that only differs in syntax or naming and you can achieve same things with them.

Name "Compiler" for a toolset is a bit misleading. A toolset is a set of tools used for building a project and one of the steps is called compilation. It is done with compiler sub part. Image presents most vital parts of toolset (light green), again, badly marked as Compiler in lower left corner.

Overall the complete development environment is shown with emphasis that Editor, Compiler and Debugger are separate entities. They are usually sold as one IDE. But you can use each from different vendor if you wish. Write code in Visual Studio, compile with make and GCC and debug in Eclipse over GDB on OpenOCD. Executable file has the debug info (if not intentionally stripped in build process) so that debugger IDE can pick up source files specified in it. Such setups are complex to set and it's best to stick to simple all-in-one solutions. 

Compiler marks where address of external symbol is needed. Linker populates the address after all pieces are put together and absolute locations can be calculated.

Build process in example is made with two calls to compiler converting the source files to object files and one call to linker gluing them together. Well known 'make' also does this. It's just that it uses more complex/flexible syntax to describe with what parameters which utils are called and in what order. IDEs also do this internally. But the parameters are defined with clickable options in dialogs.

Few GCC info points as reference to sample:

Short comment to -ffunction-sections and --gc-sections option pair: size optimization with unused code deletion is not straight forward. Source files are not thrown at compiler executable all at once and processed together. They are processed one by one. Functions can't be just removed if not used inside single source file - they can be called from some other source files. So compiler can't do it. And linker knows nothing about functions. Trick here is to instruct compiler to tag each function and prepare it as separate chunk as input to linker. Linker can then figure out which chunks are not accessed and omit them from final image.

MEMORY layout tells the (GCC) linker where the memories are on target platform. This differs from device to device.

SECTIONS entries instruct linker where to put chunks of "program" generated by compiler. By default (GCC) compiler tags code (functions) with ".text", constants with ".rodata", initialized global variables with ".data" and uninitialized global variables with ".bss". Own tags can be used as shown. Tagged chunks are called 'compiler output sections' or 'linker input sections' and are not to be confused with 'linker output sections'.

There is a problem with not-loadable programs. Loadable programs like on PC where OS loads executable in memory and executes it have global variables initialized with loading, i.e. initial global variable values are written to variable locations when loading - the program is built that way. But nothing loads embedded programs. They are usually stored in nonvolatile (read only) memory and start executing at power on. So what about the initial values for global variables? They can't just appear in (RAM) memory out of thin air. RAM by definition holds random values at power on. But RAM is where variables are! The trick is to store initial values somewhere in nonvolatile memory and copy them to locations of global variables immediately after power on. GCC enables this with 'AT>' command in linker file. Global variables are linked to addresses in RAM but their initial values stored where 'AT>' points. Other compilers also do this, you just don't realize that they add this so called 'startup code' to your project and insert call to it somewhere soon after reset.

This is most minimal C project. It would be a single source file project if Some.h, SSomeStruct and SomeCall wouldn't be used.

Usually build process generates some summary of what was built. How it looks differs from compiler to compiler but in general information is what is present in final image, where it is and what is the size of it. It is commonly referred to as 'map' file.

Friday, July 25, 2014

CC3200 LaunchPad with external debugger and ARM GCC in Eclipse

Hot stuff! I got the board and read most of the the documentation, examples and APIs. It's time for debugging now. Since I don't like bulky SDKs that come for each eval board I tend to extract what I need from them (installing to virtual machine) and use external debugger. Modifications to connect external debugger are depicted below. No HW changes are needed on this one. TI made it all configurable with jumpers. There are 2 changes made:
  • SOP pins are configured to enable SWD (Single Wire Debug).
  • Debug lines are disconnected from on board FTDI debugger. External debugger is connected to freed debug pins via 3 lines SWDIO(TMS), SWDTCK(TCK) and GND. Let's not get into why 3 lines are needed for Single Wire Debug :).
I stopped boot in endless loop after connecting to it. I made simple while(1) empty loop project. Downloaded it to beginning of the RAM (0x20000000) and it worked.
I'm still not sure about clock settings. It doesn't seem that any PLL needs initializing...
[UPDATE]
I did sample project without any PLL code and it worked.

Also I didn't get SWO (Single Wire Output) to generate any output from ITM (Instrumentation Trace Macrocell). I know ITM was configured properly because it worked on some other devices. That only leaves IO configuration which I did with enabling GPIO3CLKEN clock, configuring corresponding GPIO_PAD_CONFIG_24 and set pin as output by setting GPIOA3[0]. Scope didn't show anything on TDO pin when writing ITM stimulus registers. Did anyone manage to get trace output ?
[UPDATE]
I added following code to initialize SWO pin without success:
PRCMPeripheralClkEnable(PRCM_GPIOA3, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK | PRCM_DSLP_MODE_CLK);
PinModeSet(PIN_17, PIN_MODE_1);
PinConfigSet(PIN_17, PIN_STRENGTH_6MA, PIN_TYPE_STD);
PinDirModeSet(PIN_17, PIN_DIR_MODE_HW);
It works! Apparently TDO is not the pin to get SWO out. Pins 45, 53 and 50 are according to CC3200 forum. Anyway, connecting iTag to 53 and adding code below did the trick.
PinModeSet(PIN_53, PIN_MODE_13);


CC3200-LAUNCHXL + iTAG

[UPDATE]
Created Eclipse C projects for CC3200 libs are available in repository. Short note for building: download CC3200 SDK separately (or extract from CCS installation). Library folders contain RefreshFiles.bat script invoked with CC3200 path to copy needed files to project folder.

While playing with integrated HTTP server I got frustrated with constant terminal to Uniflash tool UART switching. I decided to use UART Tx pin multiplexed with JTAG, which was not used since I'm using SWD.
To utilize it I added FTDI MM232R USB to serial module. I only connected CC3200 Tx to MM232R Rx for printf() tracing. Unconnected pin on LAUNCHPAD debug pinhead (J11) could be used for sending serial data to CC3200.
Code for UART initialization:
#define PRCM_UART_TERM PRCM_UARTA1
#define PIN_TERM_TX PIN_16
#define PIN_TERM_TX_MODE PIN_MODE_2
#define PIN_TERM_RX PIN_17
#define PIN_TERM_RX_MODE PIN_MODE_2
#define UART_TERM UARTA1_BASE
#define PRCM_UART_TERM PRCM_UARTA1

PRCMPeripheralClkEnable(PRCM_UART_TERM, PRCM_RUN_MODE_CLK);
PinTypeUART(PIN_TERM_TX, PIN_TERM_TX_MODE);
UARTConfigSetExpClk(UART_TERM,
                    MAP_PRCMPeripheralClockGet(PRCM_UART_TERM),
                    115200,
                    (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));

and code for sending single character:
UARTCharPut(UART_TERM, ch);

CC3200-LAUNCHXL + iTAG + MM232R

CC3200-LAUNCHXL to MM232R closeup

Soon after I realized I can capture serial data with iTAG debugger since SWD leaves pins for capturing SWO and iTAG can be configured to dump UART data from that pin to terminal window.
CC3200-LAUNCHXL + iTAG with UART capturing

CC3200-LAUNCHXL + iTAG with UART capturing (iTAG closeup)

CC3200-LAUNCHXL + iTAG with UART capturing (LAUNCHPAD closeup)

Programming SPI flash without Uniflash through debug port

With setup above updating the SPI flash content still requires disconnecting the debug session, switching SOP jumper to 'FLASH' position and resetting the board to program files with TI Uniflash tool.

For development purposes I wrote small monitor application to run on CC3200 and Python script controlling the execution of the monitor through debugger to write files to SPI flash, e.g. write file data to CC3200 RAM and trigger function calling SimpleLink lib to write the data to file. Scripting is supported by winIDEA, IDE used with iTAG debugger and iSYSTEM.Connect API. My intention was to first format the flash and then program all files in specified folder. It turned out that formatting is not supported by library (only Uniflash tool). As workaround first SPI flash must be erased on low (SPI) level, then formatted with Uniflash tool and all data from flash read to be later used as "empty" image. This is done once. SPI access is provided in the script. Later on every update of the files the script erases the flash, programs "empty" image to it and then calls SimpleLink lib to create and write files. See script for more details.

Recording ITM printf() messages on SWO

Tested on pin 53 as mentioned in update above. Don't get confused by the photo below. I'm using white wire for SWO. SWO is configured by debugger to output at 4Mbits/second. CPU speed and SWO prescaler settings in winIDEA must be configured for this. Following equation applies: BAUD = CPUclk/(SWOprcs + 1) -> 80MHz/(19 + 1) = 4MHz.

White wire connected from pin 53 configured as SWO to TDO input on iTag

4M BAUD configured in winIDEA



Monday, July 21, 2014

Communication with embedded devices

It is always a hassle to communicate with embedded device. I do manage one time solution e.g. through USB to UART converter but it is always too much interleaved with application code to later switch to integrated USB peripheral.

Sometimes in the past I got requests to provide new or additional way of communication for device. Another use case I wrote this library for is when I found myself repeating writing same code. I had point to point serial communication implemented on some project. On another project I used serial bus approach (with CAN physical drivers - not important) and the communication was identical with addition of the byte header for destination address at the beginning of each packet. Repetition also happened on client and host side.

In short, Comm library offers a way to make communication entities modular thus easily replaceable. It also makes them stackable to make the whole through communication extending easy. First library implementation is in C [Google Code repository].

Communication is meant to be of  query - response from master (client) to slave (host). The use of library enforces a way of transmission and reception but the same design can be used on both sides of communication channel. This means that layer can have same implementation on client and host  side if both running on the same hardware platform.


From library point of view the communication entity is self sufficient communication layer implementing a set of predefined functions i.e. implementing an interface. As it turned out layers are of two types. In one stack there are more data formatting layers and single physical layer.

Physical layers are in charge of data storage and thus having communication buffer. Example of a layer on top of physical would be an encryption layer. Application interacts with Comm library functions when transferring data. Library then calls Comm layer implementation as needed. All functions have C++ style “this” pointer parameter. In the application accessible library functions this is a predefined Comm stack type holding a pointer to array of another predefined type instances each describing single Comm layer implementation. Comm layer instance is used as the "this" pointer in layer implementation functions and in library functions used for data transfer between layers. Comm layer instance has 5 pointers:
  • vague void pointer member to store reference to a custom type object for specific implementation
  • pointer to predefined type object holding function pointers of layer implementation
  • pointers to upper and lower layer if any
  • pointer to Comm stack to which layer belongs.

Instance custom pointer and pointer to functions are initialized in extra initialization function call which is custom for each specific layer implementation and additionally exposed to the application together with a custom instance type. This initialization function is not to be mistaken with layer implementation initialization function.
Pointers to surrounding layers and pointer to parent stack are initialized in library initialization call which should result in established connection after return.
Or everything again in objective language words: main communication stack object has methods for communication and holds references to layer objects each implementing the defined interface and custom init method which is only method called by application directly.
Below is a general sequence and internal states of the objects after each initialization step:

Initial state.

After first layer custom initialization call.

After last layer custom initialization call.

After Comm library initialization call.
Next to custom and implementation initialization method a Comm layer implements data transfer, event notification, and process methods.

Data transfer methods:

  • Send
    To send data in direction from application towards physical layer. Layer can optionally implement this method to transform data or add data to sent data. Additional data must be then in same fashion removed on receiving side in OnData call.

  • Store
    To save data in comm buffer. Called from OnData call back from upper layer. Usually only implemented in lowest, physical layer.

  • OnData
    To optionally unpack received data. Called from lower layer ReceiveProcess when new data received. Here is a chance to undo the changes to data made in Send and in PacketStart and in PacketEnd on transmitting side. If implemented it must forward remaining data with chained OnData call to upper layer or call Store if there is no upper layer like library does.

Event notifications:

  • PacketStart
    First call from application when sending a packet. Lower layers get this call first, upper last. Each layer can already put own header to transmitted packet with call to Send. This call gives layers a chance to clean up from previous application packet transfer. Send call(s) will follow.

  • PacketEnd
    Last call from application when sending a packet. Upper layers get this call first to append custom data to the packet with call to Send if needed. Physical layer can start actual transmission in this call.

  • OnNewPacket
    Call originates in physical layer ReceiveProcess method when start of new packet is detected. Here is a chance for layers to prepare for new packet. OnData call(s) will follow.

  • OnPacketEnd
    Called from physical layer ReceiveProcess  after complete packet was received.

Process methods:

  • TransmitProcess
    Called repeatedly from application after PacketEnd. Usually only implemented in physical layer. Returns Active status while data is being sent to medium and returns Idle status after all data is sent.

  • ReceiveProcess
    Called repeatedly from application to receive a packet. Usually only implemented in physical layer. Returns Idle status while there is no incoming data, optionally returns Active status while data is being received and returns NewPacket status after whole packet is received. Together with NewPacket status it returns the size of the packet and pointer to received data.

Flow of calls to send and receive the data with Comm library is as depicted below.

First call in client to send a packet to client; first call on host to send response.
PacketEnd behaves identically. Physical layer can start sending data in PacketEnd call.

Multiple calls to send data on client and host.

Multiple calls while transmitting data on client and host.

Repeatedly called on client while expecting a response from host; repeatedly called on host while waiting for new packet. OnPacketEnd behaves identically.

Repeatedly called on client while expecting a response from host; repeatedly called on host while waiting for new packet.
For test implementation I did a pair of embedded STM32F4 project and Windows client application communicating over USB. Windows side uses WinUSB library for communication and firmware reports a OS USB descriptor associating device with WinUSB driver so drivers or .inf files are not needed. Firmware code is in C and Windows client application uses C++ in Windows only code.

WinUSB communication with STM32 round 2 (Part 2: Client)


Previous client application is linked against WinUSB and SetupAPI libraries. This was a bit of a bother with WDK and project settings. They are loaded dynamically in new client test application. Two thin wrappers load the libraries and provide needed function calls with same prototypes as original library functions. SetupAPI is needed at low level for enumeration and when creating connection. WinUSB is further wrapped in an object representing USB device. And later wrapped in Comm interface explained in another post.
USB device object uses two USB pipes to transfer data which takes two USB endpoints in bulk mode at device side. One for sending data to device and other to receive it. Messaging control is done through USB control endpoint. Transfer sequence is optimized from previous implementation. Sending a complete application packet consists of sending a control message with the 32 bits length of the packet and then writing the packet data to bulk pipe. Receiving a packet is done with reading the return size through the control endpoint util nonzero length is returned and then reading the packet data from second endpoint. Application packet is meant to be a complete data set needed for single operation on device and can be larger than endpoint buffer.
Comm wrapper mentioned earlier is an implementation of the layer interface as specified by Comm library. Comm library core utilizes this interface to make transferring of data easier to application. WinUSB Comm layer is written to be the last layer in stack, also referred to as "physical" layer as is the closest to the physical medium on the master (client) side - Windows PC.
Application is written to test the connection and speed. Device is expected to return same data as sent by application. Speed is calculated from time it takes to send and receive back the packet.

Application uses Comm library methods to transfer the data. It is just that the Comm library object is initialized with comm layer using USB. It could instead be some other communication type, e.g. serial port or TCP/IP.

[Source files]

This is the second post in this series (of total 2 posts). See 1.

Thursday, May 8, 2014

ARMv7 coprocessor access

Macros below generate little endian ARM (not THUMB) instructions for access to coprocessor. This for when instructions can't be compiled but rather stored in table as data.

// MCR<c> <coproc>, <opc1>, <rt>, <crn>, <crm>{, <opc2>}
// Read coprocessor register (no condition)
#define A_MRC(coproc, opc1, Rt, CRn, CRm, op2)  (0xEE100010 | (((opc1) & 0x7) << 21 ) | (((CRn) & 0xF) << 16 ) | (((Rt) & 0xF) << 12 ) | (((coproc) & 0xF) << 8) | (((op2) & 0x7) << 5)) | ((CRm) & 0xF)
// Write coprocessor register (no condition)
#define A_MCR(coproc, opc1, Rt, CRn, CRm, op2)  (0xEE000010 | (((opc1) & 0x7) << 21 ) | (((CRn) & 0xF) << 16 ) | (((Rt) & 0xF) << 12 ) | (((coproc) & 0xF) << 8) | (((op2) & 0x7) << 5)) | ((CRm) & 0xF)


Saturday, May 3, 2014

WinUSB communication with STM32 round 2 (Part 1: Firmware)

Introduction


I did another WinUsb device. This time I've used STM32F4. Project in Google Code repository is for STM32F4DISCOVERY board featuring STM32F407VGT6.
I wanted to make it so that it wouldn't need Windows side driver information in a form of .inf file. I did it with Windows OS USB descriptors. They are described in subsection on MS "Dev Center - Hardware" Drivers page (links at the end of post). 



Result of device providing these descriptors is Windows loading one of MS provided drivers and associating the device with it as instructed in descriptor gotten from device when plugged in USB port.
"MS provided drivers" are not to be mistaken with "USB device class drivers included in Windows" which are drivers for external HDDs, webcams, HIDs...
WinUSB is one of MS provided drivers providing raw communication with device, e.g. WriteBuffer/ReadBuffer. This means that PC side application and firmware in device must follow some defined communication protocol. These are well defined for standard class USB devices like mouse and keyboard or external HDD but with WinUSB custom protocol can/must be defined. It can be quite simple like adding 4 byte header to transferred buffer holding the length of buffer.

[Update] Windows enumeration

While preparing PC side demo application to demonstrate communication I found out that categorization differs for single interface devices and composite multiple interface devices. Image above shows 2 devices in  device manager each plugged in own USB port and each with single USB interface associated with WinUSB driver. So does the image below with additional Properties window.
Single device with single WinUSB driver associated interface. 

But notice how composite device (having multiple USB interfaces) is shown on next image. Two WinUSB  associated interfaces were reported in descriptors by device:
Single device with two WinUSB driver associated interfaces.
Three devices are shown in Device Manager. One Composite device and two WinUSB devices - one for each interface. WinUSB devices have Composite device set as parent. Note how "Bus reported device description" differs for single interface device from dual interface device.
To test both configuration un/comment WINUSBCOMM_DUAL_INTERFACE_TEST macro in usbd_WinUSBComm.h file.

Windows OS USB descriptors


Text below refers to 'interfaces'. Term is used for a set of endpoints/pipes used in a group. Interfaces have own descriptors.

MS OS String Descriptor


When device is plugged in USB port for the first time on a Windows machine it also receives a standard USB string descriptor request requesting the string descriptor with index 0xEE among other requests during enumeration. For first few indices device responds with standard string descriptors which are Manufacturer, Product and Serial strings (also Language IDs for index 0). Device can ignore 0xEE string descriptor request or respond with data predefined by MS. If so Windows will then query for additional MS specific descriptors:
  • Extended Compat ID OS Descriptor
  • Extended Properties OS Descriptor
  • others not relevant here
The content of MS OS String Descriptor must be:

Value Field Size in Bytes Description
0x12,
bLength
1
Length of the descriptor
0x03,
bDescriptorType
1
Descriptor type - string
0x4D, 0x00,
0x53, 0x00,
0x46, 0x00,
0x54, 0x00,
0x31, 0x00,
0x30, 0x00,
0x30, 0x00,
qwSignature
14
MSFT100” Signature field
MS_VendorCode,
bMS_VendorCode
1
Vendor-specific Vendor code
0x00
bPad
1
Pad field

MS_VendorCode is a value (defined as custom macro in project) which Windows will use to form requests for other descriptors. This gives device developer some leeway to add OS descriptors functionality without clashing with other custom USB requests which device might already support. For new and simple USB devices supporting only single WinUSB interface the vendor code is not that much relevant. Just make it non zero so that it won't cause problems on USB 3 hosts (see specification for more).
Note that Windows queries this descriptor only once. It can be a hassle during development. Information that OS descriptors have been queried for some device is stored in registry under
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags\VVVVPPPPRRRR
(VVVV - vendor ID; PPPP - product ID; RRRR - revision).
Delete VVVVPPPPRRRR key and also uninstall the device with utility like USDDeview to always get fresh device plug in behavior.

Extended Compat ID OS Descriptor


After device responds with proper 'MS OS String Descriptor' Windows sends a setup packet with request for 'Compat ID OS Descriptor'. Format of request is as follows:

Setup request field Value
bmRequestType b11000000 (device to host transfer direction; vendor type request; recipient is device)
bRequest
MS_VendorCode
wValue 0x0000
wIndex
0x0004 (request for an extended compat ID OS descriptor)
wLength
0x0010 or full length of descriptor

This a chance for device to inform Windows which driver should be loaded. Content below shows WinUSB driver association:

Value Field Size in Bytes Description
0x28, 0x00, 0x00, 0x00,
dwLength
4
The length, in bytes, of the complete extended compat ID descriptor
0x00, 0x01,
bcdVersion
2
The descriptor’s version number, in binary coded decimal (BCD) format
0x04, 0x00,
wIndex
2
An index that identifies the particular OS feature descriptor
0x01,
bCount
1
The number of custom property sections
0, 0, 0, 0, 0, 0, 0,
RESERVED
7
Reserved
0,
bFirstInterfaceNumber
1
The interface or function number
0,
RESERVED
1
Reserved
0x57, 0x49, 0x4E, 0x55, 0x53, 0x42, 0x00, 0x00,
compatibleID
8
The function’s compatible ID ("WINUSB")
0, 0, 0, 0, 0, 0, 0, 0,
subCompatibleID
8
The function’s subcompatible ID (none)
0, 0, 0, 0, 0, 0,
RESERVED
6
Reserved

Descriptor is queried twice. First with only header length (0x10) in wLength field. Then, after full length is known to Windows (from dwLength field) with full length in wLength field.
Each interface in device can have own association. Multiple sections follow the header (in yellow) in case of more interfaces. Descriptor is queried on device level and all associations must be listed in single descriptor. bFirstInterfaceNumber denotes the index of associated interface in each section.

Extended Properties OS Descriptor


After associating the driver with device Windows queries for interface properties. These are meant to provide information which is usually provided in .inf files of drivers, e.g.:

[USB_Install.HW]
AddReg=Dev_AddReg

[Dev_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,"{EA0BD5C3-50F3-4888-84B4-74E50E1649DB}"

Properties Descriptor request is also issued twice (or more for descriptors larger than 64kByte). First with wLength set to 0x0A to only retrieve the header and later with wLength set to full length gotten from dwLength.
Following is the layout of USB Setup request:

Setup request field Value
bmRequestType b11000001 (device to host transfer direction; vendor type request; recipient is interface)
bRequest
MS_VendorCode
wValue 0xppii (pp – PageNumber for descriptors larger than 64kB; ii – interface index)
wIndex
0x0005 (request is for an extended properties OS descriptor)
wLength
0x000A or full length of descriptor

Note that properties are queried per interface. So if your device has multiple interfaces the request is send at least twice for each interface (BTW the number of interfaces is known from standard USB configuration descriptor). Following descriptors defines two properties:

  • DeviceInterfaceGUID
    This identifies interface with GUID which is used later in Windows application when querying the OS for devices with SetupDiGetClassDevs
  • Label
    This should be a friendly name displayed for device in Device manager. But apparently it only works form Windows 8 on

Value Field Size in Bytes Description
0xCC, 0x00, 0x00, 0x00,
dwLength
4
The length, in bytes, of the complete extended properties descriptor
0x00, 0x01,
bcdVersion
2
The descriptor’s version number, in binary coded decimal (BCD) format
0x05, 0x00,
wIndex
2
The index for extended properties OS descriptors
0x02, 0x00,
wCount
2
The number of custom property sections that follow the header section
0x84, 0x00, 0x00, 0x00,
dwSize
4
The size of this custom properties section
0x01, 0x00, 0x00, 0x00,
dwPropertyDataType
4
Property data format
0x28, 0x00,
wPropertyNameLength
2
Property name length (PNL)
'D',0, 'e',0, 'v',0, 'i',0, 'c',0, 'e',0, 'I',0, 'n',0, 't',0, 'e',0, 'r',0, 'f',0, 'a',0, 'c',0, 'e',0, 'G',0, 'U',0, 'I',0, 'D',0, 0, 0,
bPropertyName
PNL
The property name (DeviceInterfaceGUID)
0x4E, 0x00, 0x00, 0x00,
dwPropertyDataLength
4
Length of the buffer holding the property data (PDL)
'{',0, 'E',0, 'A',0, '0',0, 'B',0, 'D',0, '5',0, 'C',0, '3',0, '-',0, '5',0, '0',0, 'F',0, '3',0, '-',0, '4',0, '8',0, '8',0, '8',0, '-',0, '8',0, '4',0, 'B',0, '4',0,
'-',0, '7',0, '4',0, 'E',0, '5',0, '0',0, 'E',0, '1',0, '6',0, '4',0, '9',0, 'D',0, 'B',0, '}',0, 0, 0,
bPropertyData
PDL
PDL Format-dependent Property data ({EA0BD5C3-50F3-4888-84B4-74E50E1649DB})
0x3E, 0x00, 0x00, 0x00,
dwSize
4
The size of this custom properties section
0x01, 0x00, 0x00, 0x00,
dwPropertyDataType
4
Property data format
0x0C, 0x00,
wPropertyNameLength
2
Property name length (PNL)
'L',0, 'a',0, 'b',0, 'e',0, 'l',0, 0, 0,
bPropertyName
PNL
The property name (Label)
0x24, 0x00, 0x00, 0x00,
dwPropertyDataLength
4
Length of the buffer holding the property data (PDL)
'W',0, 'i',0, 'n',0, 'U',0, 'S',0, 'B',0, 'C',0, 'o',0, 'm',0, 'm',0, ' ',0, 'd',0, 'e',0, 'v',0, 'i',0, 'c',0, 'e',0, 0, 0

bPropertyData
PDL
PDL Format-dependent Property data (WinUSBComm Device)
These properties are stored in Windows registry at:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_xxxx&PID_xxxx\sssssssss\Device Parameters
Use USDDeview or similar utility to uninstall device and delete this entry while developing.

HW Info


To supply the board from OTG USB micro AB connector you need to connect PA9 pin to 5V pin. This is possible because VBUS is (strangely) connected directly to PA9 VBUS sensing input.

[Update] Comm library

Communication is implemented by implementing Comm layer so that it can be used with Comm library explained separately.

This is first post in this series (of total 2 posts). See 2.

Reference


Links tree below is for my future reference. You probably want to see descriptor specification (in bold).