USB CDC with SAMD21
I started working on this because I had another project that needed USB communication accross all platforms while being relatively affordable. This ruled out trusty FTDI and most of the other USB-Serial cconverters. As for the software, using the Arduino M0 stack was an option but I wanted to learn the inner workings of USB while keeping the code lean. I found this great USB stack by kevinmehall and wrote the USB-CDC interfacing functions and tried on a board at hand. After ironing out the various little bugs in the descriptors I was able to get my device to enumerate as a COM port on windows. USBTreeView is a great tool for debugging USB enumeration issues. It updates faster than device manager and you can view all the descriptors in one page.
Even though the enumeration was successful, opening the port in a terminal program was a hit or miss. Someitmes the port would open and communicate for a while and hang up the terminal program, sometimes it wouldn’t open at all. Microsoft page for the driver doesn’t mention any hardware requirements other than having both Class and Subclass codes set to 0x02 in the device descriptor. After digging around the internet for a while I found out that as soon as you open a COM port in windows, it tries to set the port properties (Baud rate, parity etc) and immediately reads it back to make sure the values were changed. Obviously I wasn’t interested in baurd rates as I wasn’t implementing a USB->Serial converter, mine was only a device that was pretending to be one. However to satisfy this requirement I had to change one of the CDC desriptors.
The bmCapabilities
field indicates that this device supports the requests SET_LINECODING
and GET_LINECODING
. Then I had to update the EndPoint 0 handler to handle these Class-Specific CDC requests.
bmRequestType | Request Code | Value (bRequest) | Data |
---|---|---|---|
00100001B | SET_LINE_CODING | 0x20 | Line Coding |
10100001B | GET_LINE_CODING | 0x21 | Line Coding |
bitmap | CDC_SET_CONTROL_LINE_STATE | n | meaning |
bitmap | CDC_SEND_BREAK | n | meaning |
Line Coding is a 7 byte structre that encode the properties of the Serial port we are supposed to be emulating. The structure is described below, however it’s not really that important since I wasn’t working with a real serial port.
Offset | Field | Size | Description |
0 | dwDTERate | 4 | Baud rate, in bits/s |
4 | bCharFormat | 1 | Stop bits 0 - 1 Stop bit 1 - 1.5 Stop bits 2 - 2 Stop bits |
5 | bParityType | 1 | Parity 0 - None 1 - Odd 2 - Even 3 - Mark 4 - Space |
The handler functions just load and store the data from/to host in a variable, which is sufficient for the driver to function properly with the device.
After this little modification I was able to get the device working quite smoothly on Windows (10), macOS( Mojave 10.14) and Chrome OS.