USB Communication With PIC Microcontroller CDC – XC8

USB PIC18F45K50 Circuit Diagram

USB Communication With PIC Microcontroller CDC – XC8

Watch the video tutorial

The RS-232 serial interface port (COM Port) is now rarely found on a personal computer (PC), this once common interface has been virtually replaced by the Universal Serial Bus or USB for short.

Today USB has grown beyond PCs to become the common interface for many embedded industrial and consumer products like in cameras, GPS, printers, etc.

This has created a problem because many PC Software were designed to communicate with the embedded applications using the RS-232 interface as the USART is still one of the simplest serial protocol to use with any microcontroller, many devices are still using it as their main communication protocol like GSM/GPRS modules, GPS, etc.

If you need to communicate to a PC, then some sort of serial to USB converter is required.

Figure 1: Emulating the Serial Port Interface

There are many types of USB communications. The functionality of USB devices is defined by class codes, this is used to identify a device’s functionality and to nominally load a device driver based on that functionality, like Communication class, HID, image, mass storage, etc.

The Communication Device Class (CDC) supports a wide range of devices that can perform telecommunications and networking functions. Examples for communications equipment are:

  • Telecommunications devices, such as analog phones and modems, ISDN terminal adapters, digital phones, as well as COM-port devices
  • Networking devices, such as ADSL and cable modems, as well as Ethernet adapters and hub

A detailed description about CDC is provided by the USB Implementers Forum (USB-IF)

In this article we will discuss the USB Communication Device Class (base class 0x02) that can be used to emulate the serial port and thus no software modification on the computer, or a simpler software development because as serial port interface can be made easily with many PC software design tools like Visual Studio for example.

You can learn more about creating a PC software from this tutorial:

Creating a Serial Port Interface with C#

USB is very different to other simpler peripherals like USART or SPI for example where you could simply add the peripheral interface to your project with some few library codes or accessing the registers by reading the datasheet.
With USB it’s different, you really need to write your code off the USB framework like the TCP/IP framework.
USB is an order of magnitude more complicated than the other peripherals. It must be constantly serviced to maintain a connection to a PC.

Fortunately Microchip provides Application Libraries that one can simplify the job for us. You can use Microchip Libraries for Applications (MLA) which have sample projects for different USB classes. Under the folder:

apps –> usb –> device  you can choose the sample projects with the USB class you are developing, in this case you could use the cdc_basic or cdc_serial_emulator.

To learn more about Microchip Libraries for Applications and how to copy the sample projects, please read:

Microchip Libraries for Applications

Using prepared libraries make your job much easier, but you must use them carefully, not just try to bolt them onto an existing application, read the supplied documentations in the doc folder to understand the structure and different files used in the libraries.

In this tutorial we are not going to use Microchip Libraries for Applications, we will use the USB Framework Lite with MPLAB Code Configurator (MCC).

The USB Framework Lite is an MCC Application Library of the Microchip Libraries for Applications. The current version of writing this tutorial is v1.26.1 and only supports the USB CDC and Vendor classes. We are going to use it with the PIC18F45K50 which has Active Clock Tuning (ACT), feature on some  newer 8-bit PIC microcontrollers like the PIC18F45K50 or the PIC16F1459 and many others that can allow us to use the USB full speed without the need of an external crystal oscillator, this can save money in BOM and extra less source of problem from the oscillator circuit components. The PIC18F45K50 is a newer model of the popular PIC18F4550.

Circuit Diagram

USB Communication Circuit Diagram

Figure 2: USB Communication Circuit Diagram

The above circuit diagram shows the minimum components needed, as we are using a PIC with Active Clock Tuning (ACT), no high-speed, high-accuracy external crystal and its circuity is required, we are going to use the internal oscillator.

The PIC can get power from the host PC. Please keep in mind the limits for devices drawing power from the USB. According to USB specification 2.0, this cannot exceed 100mA per low-power device or 500mA per high-power device.

A decoupling capacitor should be connected VDD and ground ( as close as possible to the PIC), a value of 0.1uF  could be fine.

The PIC will utilize the on-chip 3.3V USB voltage regulator to provide power to the internal transceiver and to provide a source for the internal pull-up resistors. By using the internal components, this helps reduce the number of external components. The USB connection can be electrically detached by disabling the USB module in the firmware. By disabling the USB module in firmware (setting the USBEN bit in the UCON register to ‘0’), the on-chip USB voltage regulator will also be disabled. This simulates the physical detachment of the USB cable.

An external capacitor like a 0.47uF is required to be connected to the PIC VUSB pin for the internal voltage regulator (please read the datasheet for more info). If an external regulator is connected to VUSB, you can disable this internal regulator.

Figure 3: USB Interface On-chip components

The figure 4 below shows the USB peripheral and options

Figure 4: USB peripheral and options

Active Clock Tuning

Active Clock Tuning (ACT) is a feature on some  newer 8-bit PIC microcontrollers like the PIC18F45K50, PIC16F1459, etc.

The Active Clock Tuning (ACT) module continuously adjusts the 16 MHz Internal Oscillator, using an available external reference (from the USB Host), to achieve ± 0.20% accuracy required by the USB standard. This eliminates the need for a high-speed, high-accuracy external crystal which is required for the USB full speed operation when the system has an available high-accuracy clock source such as the High Frequency Internal Oscillator (HFINTOSC).

Figure 5: Active Clock Tuning (ACT) Block Diagram

To learn more about USB communication system, please read the article:

USB Communication with PIC Microcontroller

Buy a USB PIC Microcontroller from Our Online Shop

USB Clock Settings

Start a new MPLAB X project and select the PIC18F45K50. Start the MCC to configure our peripherals.

Click on the System Module in the Project Resources. When the the PIC18F45k50 is used for USB connectivity, a 6MHz or 48MHz must be provided to the USB module for operation in either Low-Speed (1.5 Mbit/s) or Full-Speed modes (12 Mbit/s). To achieve this we will need a USB frequency of 48MHz which we can achieved by using the internal 16MHz clock with PLL (Phase-locked loop). This PIC has 3x and 4xPLL Clock multipliers as shown on the figure below. By using the internal 16MHz with 3x PLL we can raise the USB Clock frequency to 48MHz.

Figure 6: PIC18F45K50 Clock settings for 48MHz USB Full speed using MCC

The HFINTOSC is tuned using Full-speed USB events. The ACT is enabled by setting the ACTEN bit of the ACTCON register. When enabled, the ACT takes control of the OSCTUNE register. The ACT uses the
selected ACT reference clock to tune the 16 MHz Internal Oscillator to an accuracy of 16 MHz ± 0.2%.
The tuning automatically adjusts the OSCTUNE register every reference clock cycle

Figure 7: PIC18F45K50 ACTON Register

MLA USB Device Lite

Under the Device Resources, Double click the MLA USB Device Lite to add it to the Properties Resources under the Peripherals. Select it to set its Properties. This is where we’re gonna configure all the USB settings, with MCC the setup is super easy, most of the default settings can be left unchanged:

  • USB Class: The first thing we are going to select CDC
  • The rest of the settings can be left at their default values like: Endpoint 0 size: 8, Endpoint Buffer Mode: Full Ping Pong, Enable USB Auto Attach
  • USB Tasks: The USB stack can be operated in either USB POLLING mode or USB INTERRUPT mode. In Polling mode the USBDeviceTasks() function should be called periodically to receive and transmit packets through the stack. In this example we will select the Interrupt mode.
  • The Internal Pull Up Resistors are enabled.
  • Vendor ID (VID) and Product ID (PID): Every USB product line must have a unique combination of VID and PID. All firmware examples use Microchip’s VID (0x04d8) and a unique PID. Prior to manufacturing and marketing a new USB product, the VID and PID need to be changed. New VID and PID numbers can be obtained by purchasing a VID from the USB Implementers Forum:
    • Alternatively, Microchip has a free VID sublicensing program. An application form for obtaining a PID (for use with Microchip’s VID: 0x04d8) from Microchip can be obtained through the following link:
    • Once a new VID/PID combination is obtained, both the firmware and the .INF file (when applicable) will need to be updated. To modify the VID/PID in the .INF file, open the relevant INF file and search for the “[DeviceList]” sections. There are two sections, one for 32-bit and one for 64-bit, both sections should be identical. In these sections, some text will appear with the form “USB\VID_xxxx&PID_yyyy”. Update the “xxxx” and “yyyy” sections with the new hexadecimal format VID/PID values
    • The .inf file is a plain text (ex: editable with Notepad) installation instruction/information file that tells the OS what driver needs to be used for the hardware, and anything else that may need to happen during the driver installation process.
    • Manufacturing String and Product String: This is the Product and Manufacturer name as it will appear in your device manager. If you change these details, modify the .inf file as well.

Figure 8: MLA USB Device Lite configuration in MCC

CDC Settings:

We will leave the default Abstract Control Management Capabilities settings.

Figure 9: USB CDC Settings

USB Library Functions/Macros

In MCC Project Resources, click on Generate to generate all the USB files and the Main.c file.

A typical USB CDC bare code could look like this:

You can get the descriptions of these functions and more form the MLA USB Library Help document form the Microchip Libraries for Applications installation folder, or read the descriptions in functions prototypes in MCC USB header files (like the usb_device.h, usb_device_cdc.h)


This function initializes the device stack it in the default state. The USB module will be completely reset including all of the internal variables, registers, and interrupt flags. This function must be called before any of the other USB Device functions can be called, including USBDeviceTasks()

If using MCC, the USBDeviceInit() is called from the SYSTEM_Initialize() function which initializes also the interrupt, oscillator, pin manager also. So the first code which MCC generate by default is:


If using MCC, this function is called from the SYSTEM_Initialize() function, no need to do anything . This function indicates to the USB host that the USB device has been attached to the bus. This function needs to be called in order for the device to start to enumerate on the bus.

Please note this function should only be called when USB_INTERRUPT is defined. Also, should only be called from the main() loop context. With MCC, the below code will be generated for you automatically by MCC.


This function is the main state machine/transaction handler of the USB device side stack. When the USB stack is operated in “USB_POLLING” mode, (usb_config.h user option) the USBDeviceTasks() function should be called periodically to receive and transmit packets through the stack. This function also takes care of control transfers associated with the USB enumeration process, and detecting various USB events (such as suspend).
This function should be called at least once every 1.8ms during the USB enumeration process. After the enumeration process is complete (which can be determined when USBGetDeviceState() returns CONFIGURED_STATE), the
USBDeviceTasks() handler may be called the faster of: either once every 9.8ms, or as often as needed to make sure that the hardware USTAT FIFO never gets full. A good rule of thumb is to call USBDeviceTasks() at a minimum rate of either the frequency that USBTransferOnePacket() gets called, or, once/1.8ms, whichever is faster. See the inline code comments near the top of usb_device.c for more details about minimum timing
requirements when calling USBDeviceTasks().
When the USB stack is operated in “USB_INTERRUPT” mode, it is not necessary to call USBDeviceTasks() from the main loop context. In the USB_INTERRUPT mode, the USBDeviceTasks() handler only needs to execute when a USB interrupt occurs, and therefore only needs to be called from the interrupt context.


This function returns the current state of the device on the USB. This function is used to determine when the device is ready to communicate on the bus. Applications should not try to send or receive data until this function returns CONFIGURED_STATE.

For more information about the various device states, please refer to the USB specification section 9.1 available from The table 1 below shows the USB Visible Device States:

Table 1: USB Visible Device States

USB Device States as returned by USBGetDeviceState():


This macro is used to check if the CDC class handler firmware is ready to send more data to the host over the CDC bulk IN endpoint.

A return value of true indicates that the CDC handler firmware is ready to receive new data, and it is therefore safe to
call other APIs like putrsUSBUSART() and putsUSBUSART(). A return value of false implies that the firmware is still busy sending the last data, or is otherwise not ready to process any new data at this time.

The return value of this function is only valid if the device is in a configured state (i.e. – USBDeviceGetState() returns CONFIGURED_STATE)

Remarks: Make sure the application periodically calls the CDCTxService() handler, or pending USB IN transfers will not be able to advance and complete.




This handles device-to-host transaction(s). This function should be called once per Main Program loop after the device reaches the configured state. This function is needed, in order to advance the internal software state machine that takes care of sending multiple transactions worth of IN USB data to the host, associated with CDC serial data. Failure to call CDCTxService() periodically will prevent data from being sent to the USB host, over the CDC serial data interface


This copies a string of BYTEs received through USB CDC Bulk OUT endpoint to a user’s specified location. It is a non-blocking function. It does not wait for data if there is no data available. Instead it returns ‘0’ to notify the caller that there is no data available.

uint8_t * buffer: Pointer to where received BYTEs are to be stored. Input argument ‘buffer’ should point to a buffer area that is bigger or equal to the size specified by ‘len’.

uint8_t len : The number of BYTEs expected. Value of input argument ‘len’ should be smaller than the maximum endpoint size responsible for receiving bulk data from USB host for CDC class


This writes a string of data to the USB including the null character. Use this version, ‘putrs’, to transfer data literals and data located in program memory.


This writes a string of data to the USB including the null character. Use this version, ‘puts’, to transfer data from a RAM buffer.


This writes an array of data to the USB. Use this version, is capable of transferring 0x00 (what is typically a NULL character in any of the string transfer functions).


In this project example (circuit on figure 2), we are using the PIC18F45K50, clock configuration (figure  6) to run in Full speed mode. The USB Device Lite configuration in MCC is on figure 8, the USB Task Interrupt mode is selected.

Please make sure to enable the Global Interrupts and the Peripheral Interrupts;

Any received character will be echoed back plus 1. If we receive ‘a‘, we echo ‘b‘, ‘1‘ we echo ‘2‘ and so on. This so that the user knows that it isn’t the echo enabled on their terminal program.

To find out which COM Port has been assigned to your USB device, go to Device Manager and expand the Ports (COM & LPT) to see all the COM ports on your PC. In this example the USB is on COM3

Figure 10: USB in Device Manager

Figure 11 below shows characters “a,1,2,3,4,b,c,d” echoed back to the terminal + 1.

Figure 11: Characters echoed back on Tera Term Terminal

With USB CDC, you can switch seamlessly your RS232 project to USB without ganging any code in your computer software, in the project in the link below, we will use the same PC software in Controlling a PIC Microcontroller from a PC Graphical User Interface (GUI) with USB:

Controlling a PIC Microcontroller from a PC Graphical User Interface (GUI) with USB

If you liked this article, then please subscribe to our YouTube Channel for video tutorials. You can also find us on Twitter and Facebook (please like our page or follow us for more updates).
If you have a question, please ask in the forum or as a comment on YouTube.

You can download the full project files (MPLAB X project) below here. All the files are zipped, you will need to unzip them (Download a free version of the Winzip utility to unzip files).

Download MPLAB X Project: PIC-USB-CDC

Share this post

has been added to your cart:
Select your currency