Virtual COM ports are great for those projects when you need to establish communication with an embedded project, and have no UART peripheral on your board, besides the USB you programmed with. The USB Communications Device Class ( CDC ) can be used to make a USB device look like a RS-232 connection, enabling the user to communicate through USB, transmitting and receiving data through the serial port, to a terminal.
MikroElektronika has an easy-to-use library for USB devices that allows the user to make the USB device look as a Communications Device, Human Interface Device( HID ), or Mass Storage Device( MSD ). Today we will be talking about the CDC class when developing with a Virtual COM port.
USB Devices
USB can be used for many different types of devices such as audio, video, debugging, imaging or printers. The point of creating the Universal Serial Bus was to make a communication bus for all peripherals. USB is fast, reliable, supported by all operating systems, and requires no configuration or setup by the user. The idea for USB was plug n' play, that way USB could be used for any kind of peripheral on any computer and the user would not need to do any searching for drivers. Some things needed for developing a project with a USB device are a host that can support USB, a device driver on the host side along with your code on the projects' side, and you have to know what type of device class you are trying to emulate with your USB connection. On the host side, the computer must find a device driver for the type of device you are attempting to look like, as well as manage data flow, and sometimes provide power. On the device side, you must detect communications from the computer or other device, and be able to respond using standard protocols.

Virtual COM
The MikroE library uses USB protocols and disguises itself as a COM port to enable communication through the terminal using standard USART communication. The first step is disguising your USB connection using the USB_Device_CDClass. The next thing you must do is initialize the USB device library and setup an interrupt for when you have data received from the computer. Initializing the CDC class sends a device descriptor, configuration descriptor and a string descriptor. The descriptor file is used to tell the USB device what it is going to act like, and what it is going to look like, so that the computer sees it that way and treats it as such. After the computer receives this information, it recognizes the USB as a communication device and begins communication. Here is an example of the setup while attempting to use your USB as a virtual COM port.
#include// Buffer of 64 bytes char buffer[64]; // USB interrupt service routine void USB0Interrupt() iv IVT_INT_OTG_FS{ // Call library interrupt handler routine USBDev_IntHandler(); } void USBDev_CDCParamsChanged(){ } void USBDev_CDCDataReceived(uint16_t size){ // Prepare receive buffer USBDev_CDCSetReceiveBuffer(buffer); // Send back received packet USBDev_CDCSendData(buffer, size); } void main() { // Initialize CDC Class USBDev_CDCInit(); // Initialize USB device module USBDev_Init(); // Enable USB device interrupt NVIC_IntEnable(IVT_INT_OTG_FS); // Wait until device is configured (enumeration is successfully finished) while(USBDev_GetDeviceState() != _USB_DEV_STATE_CONFIGURED) ; // Set receive buffer where received data is stored USBDev_CDCSetReceiveBuffer(buffer); // Infinite loop while(1){ } }
This example simply receives any data from the computer and sends exactly what is received back to the terminal to be displayed. It uses the interrupt from the USB to receive and send data using the USB Device library. All you have to do is set a receiving buffer, and handle what you'd like to do with the received data in the USBDev_CDCDataReceived function.
To receive and transmit data on the computer side, simply use a program like PuTTY to interface with serial ports. The baud rate of the virtual COM port is auto-baud, so the baud does not need to be set and can be set to any normal baud rate, for example 9600.
Communication Device Class
The Communication Device Class (CDC) can be used in many different ways. For all the specifications on the many different ways you can use this class you can visit usb.org. CDC has many different ways of being initialized, but in the case of MikroElektronikas' library for virtual COM ports, we use 3 specific descriptors for initialization. One descriptor that is very important is called the USB_CDC_cfg_descriptor, which contains the configuration descriptor, interface descriptors, and endpoint descriptors for all of the interfaces. Here is an example of this descriptor:
const uint8_t USB_CDC_cfg_descriptor[_USB_CDC_CONFIG_DESC_SIZ] = {
// Configuration Descriptor
0x09, // bLength: Configuration Descriptor size
0x02, // bDescriptorType: Configuration
_USB_CDC_CONFIG_DESC_SIZ, // wTotalLength: number of returned bytes
_USB_CDC_CONFIG_DESC_SIZ >> 8,
0x02, // bNumInterfaces: 2 interfaces
0x01, // bConfigurationValue: Configuration value
0x00, // iConfiguration: Index of string descriptor describing the configuration
0xC0, // bmAttributes: self powered
0x32, // bMaxPower: 100 mA
// Interface Descriptor
0x09, // bLength: Interface Descriptor size
_USB_DEV_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType: Interface
0x00, // bInterfaceNumber: Number of Interface
0x00, // bAlternateSetting: Alternate setting
0x01, // bNumEndpoints: One endpoint used
0x02, // bInterfaceClass: Communication Interface Class
0x02, // bInterfaceSubClass: Abstract Control Model
0x01, // bInterfaceProtocol: AT commands
0x00, // iInterface: string descriptor index
// Header Functional Descriptor
0x05, // bLength: Descriptor size
0x24, // bDescriptorType: CS_INTERFACE
0x00, // bDescriptorSubtype: Header Functional Descriptor
0x10, // bcdCDC: specification release number
0x01,
// Call Management Functional Descriptor
0x05, // bFunctionLength: Descriptor size
0x24, // bDescriptorType: CS_INTERFACE
0x01, // bDescriptorSubtype: Call Management Functional descriptor
0x00, // bmCapabilities: Device does not handle call management itself
0x01, // bDataInterface: 1
// Abstract Control Management Functional Descriptor
0x04, // bFunctionLength: Descriptor size
0x24, // bDescriptorType: CS_INTERFACE
0x02, // bDescriptorSubtype: Abstract Control Management descriptor
0x02, // bmCapabilities: Device supports the request combination of
// Set_Line_Coding, Set_Control_Line_State,
// Get_Line_Coding, and the notification Serial_State
// Union Functional Descriptor
0x05, // bFunctionLength: Descriptor size
0x24, // bDescriptorType: CS_INTERFACE
0x06, // bDescriptorSubtype: Union functional descriptor
0x00, // bMasterInterface: Communication class interface
0x01, // bSlaveInterface0: Data Class Interface
// Interrupt IN Endpoint Descriptor
0x07, // bLength: Endpoint Descriptor size
_USB_DEV_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType: Endpoint
0x80 | _USB_CDC_INT_EP_IN, // bEndpointAddress
0x03, // bmAttributes: Interrupt
0x08, // wMaxPacketSize
0x00,
0xFF, // bInterval
// Data class interface descriptor
0x09, // bLength: Endpoint Descriptor size
_USB_DEV_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType:
0x01, // bInterfaceNumber: Number of Interface
0x00, // bAlternateSetting: Alternate setting
0x02, // bNumEndpoints: Two endpoints used
0x0A, // bInterfaceClass: CDC
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface
deo, debugging, imaging or printers. The point of creating the Universal Serial Bus was to make a communication bus for all peripherals. USB is fast, reliable, supported by all operating systems, and requires no configuration or setup by the user. The idea for USB was plug n' play, that way USB could be used for any kind of peripheral on any computer and the user would not need to do any searching for drivers. Some things needed for developing a project with a USB device are a host that can support USB, a device driver on the host side along with your code on the projects' side, and you have to know what type of device class you are trying to emulate with your USB connection. On the host side, the computer must find a device driver for the type of device you are emulating, as well as manage data flow, and sometimes provide power. Your device must detect communications from the computer or other device, and be able to respond using standard protocols.

// Bulk OUT Endpoint Descriptor
0x07, // bLength: Endpoint Descriptor size
_USB_DEV_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType: Endpoint
_USB_CDC_BULK_EP_OUT, // bEndpointAddress
0x02, // bmAttributes: Bulk
64, // wMaxPacketSize
0x00,
0x00, // bInterval: ignore for Bulk transfer
// Bulk IN Endpoint Descriptor
0x07, // bLength: Endpoint Descriptor size
_USB_DEV_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType: Endpoint
0x80 | _USB_CDC_BULK_EP_IN, // bEndpointAddress
0x02, // bmAttributes: Bulk
64, // wMaxPacketSize
0x00,
0x00 // bInterval
};
How To Use
When using the VCP library it is very easy to establish a disguise for your USB and initialize communication. There are 2 things that you must have:
- interrupt handler to call the USBDev_IntHandler()
- a function used to set the receiving buffer.
The library is then controlled by the interrupt that is waiting for the USB interrupt to be triggered that represent the data from the computer into the receive buffer for the application. After programming your board the library will start working and the PC connected will recognize the USB as a Communications Device and can begin communication. Simply open up your terminal application, like PuTTY. Find out what serial port your USB device is on by opening up the device manage in windows and looking under Ports (COM & LPT). Your device should come up as USB Serial Port followed by what COM number it is. Place that in the text box in PuTTY and set the baud rate to a normal baud rate like 9600. Click Open at the bottom right of PuTTY and begin typing. It might look like you are just typing into the terminal, but actually that is what is being sent back to the terminal from your device.
Pretty cool huh? And it was pretty painless too.
Summary
Virtual COM ports can be very useful in those rare cases where your project just doesn't have a port for UART communication, and you have to improvise and trick the computer into thinking that you do. There are many different ways to use the USB_Device library to fit your projects needs, and have been made very easy by the MikroE library. Go try this out on our Clicker 2 with the STM32, it works great!
References
USB Class Specifications http://www.usb.org/developers/docs/devclass_docs/ 2016
USB in a NutShell http://www.beyondlogic.org/usbnutshell/usb1.shtml 2010
USB Descriptors http://www.beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors 2010
MikroE USB Device Library https://libstock.mikroe.com/projects/view/568/usb-device-library 1998-2016
PuTTY Download 2016


