For long time, UART is the only and best way to interface with PC. It is easy to adapt and handle. Though some devices (like: GSM modem etc.) have UART on themselves, but for interfacing with PC, there is only one thing now and that is USB (Universal Serial Bus).
The purpose of this article is to explain how to interface a PIC microcontroller to a PC via the USB port. Although the concepts are universal, the examples are specifically for use with MikroElektronika’s ‘MikroC Pro for PIC’.
PIC18F2550 and PIC18f4550 are famous for their USB module. To stay ahead you can start with their datasheets.
The most difficult part of this project is exactly what is required to get the PIC microcontroller to communicate with the USB port. The two most important things that absolutely have to be correct are the microcontroller configuration, and the USB device descriptor. If even the smallest thing is incorrect about either of these, communication will not occur.
The original USB 1.0 specification, defined data transfer rates of 1.5 Mbit/s "Low Speed" and 12 Mbit/s "Full Speed". The 12 Mbit/s data rate was intended for higher-speed devices such as disk drives, and the lower 1.5 Mbit/s rate for low data rate devices such as joysticks. The USB 2.0 specification has 480 Mbit/s data transfer rate, which is also known as “High Speed”. The new USB 3.0 specification has up to 5 Gbit/s data transfer rate, known as “Super Speed”.
P18F2550/4550 supports Low Speed (1.5 Mb/s) and Full Speed (12 Mb/s). So the first thing you have to know is how to set desired clock for USB.
I’ll show you how to configure ‘Full Speed’ i.e. 12 Mb/s. When you use mcu for USB connectivity, it must have either a 6 MHz or 48 MHz clock for USB operation, depending on whether Low-Speed or Full-Speed mode is being used. The first thing you can do is, use a 48MHz crystal (for full speed). But there are two drawbacks:
♦ 48MHz isn’t available that much and it is costly.
♦ The higher the crystal, the greater will be the noise.
Microchip really does a great job to solve this problem. They include a Phase Lock Loop (PLL) frequency multiplier, which allows a wide range of clock speeds from 4MHz to 48MHz. Noise cancelation is handled inside it. So we are going to use this feature to produce Full Speed for USB.
The block diagram of clock is shown below: (from datasheet, page 24).
P18F2550 have both external and internal oscillator supports. As we are discussing external oscillator that’s why I omitted the down part of the diagram (not shown). From the diagram you see that PLL module actually create 96MHz and after divide by two it is 48MHz. This PLL module must have 4MHz input. So if you use 4MHz crystal then you should set 1(one) as PLL prescaler (PLLDIV = 000). If you use 20MHz then prescaler should be 5 and so on.
That was the end of USB clock settings. Now what about mcu clock source. From the block diagram you can see that either you can use PLL’s output or you can directly use crystal’s output. And they both have some prescaler values.
In this tutorial I am going to use a 20MHz crystal. So for USB, PLL prescaler will be 5. MCU will be running at 20MHz (i.e. prescaler will be 1 [CPUDIV = 00] ).
Clock settings in MikroC
Even before programming the microcontroller, its configuration must be correct. Open a project in MikroC with P18F2550 (I have MikroC v5.2 in my hand at the time this tutorial had written). Go to Project→Edit Project. You will see a window like below:
PLL Prescaler Selection: As I use 20MHz so prescaler should be 5.
USB Clock Selection: I choose PLL.
Oscillator Selection: I choose HS (High Speed). From datasheet you’ll see 20MHz is under HS. If you want to use PLL as a mcu clock then choose HSPLL.
System Clock Postscaler Selection: As I mentioned earlier mcu scaler will be 1, I choose so.
USB voltage regulator
At the end of the figure, before ‘Watchdog Timer’, there is ‘USB voltage regulator’ and I enabled it. The thing is voltage level of USB data line (D+ and D-) is 3.3 volt. As your mcu works at 5v, so there is a 3.3v regulator for USB. You have to enable it.
Also if you study the pin diagram you’ll see there is a pin called VUSB (pin14). If you don’t want to use internal 3.3v regulator, you can supply a 3.3v source in this pin. And if you use internal regulator then add a 220nF capacitor in this pin with respect to ground to stable the internal source. I usually use two 100nF cap in parallel.
Whenever you plug-in a device in USB, what actually happens? A pop up comes which says you just attach a new device and it ask for a driver, right? Do you really notice what happen when you plug an USB mouse or keyboard? NOTHING!
Do you ever wonder why that is? Because these devices are under ‘HID (Human Interface Device) Protocol’ and Windows has driver of HID by default. So if your device communicate with OS as an HID then you don’t need any driver for your device!!
Typical examples of HID devices include :
1) Keyboards and pointing devices, for example: standard mouse devices and joysticks.
2) Front-panel controls, for example: knobs, switches, buttons.
3) Controls that might be found on devices such as telephones, VCR remote controls, games or simulation devices, for example: throttles, steering wheels.
4) Devices that may not require human interaction but provide data in a similar format to HID class devices, for example, bar-code readers, thermometers, or voltmeters.
In this tutorial I will present my device as and HID and in the subsequent tutorial I will teach you how to make Drivers.
To make your device HID you should include a descriptor source file which contains vendor id and name, product id and name, report length, and other relevant information necessary for HID class. No need to worry. MikroC also handle that too. To create a descriptor file, use the integrated USB HID terminal of MikroC (Go to Tools › USB HID Terminal). This is the same thing as ‘USART Terminal’ (in MikroC) or ‘Hyper Terminal’ (in Windows). If you ever work on serial port you know what I am talking about, otherwise ignore it.
Switch to ‘Descriptor’ tab. You can change product and vendor name. Leave rest as they are and press ‘Save Descriptor’. Save the file in your project’s folder. The default name for descriptor file is
USBdsc.c, but you can rename it.
Now that the USB descriptor is completed you must tell the compiler to use the file. This is done with the Project Manager.
On the right side of the compiler screen click on the “Project Manager” tab (red box). This opens the Project Manager. Be sure the descriptor file appears below the Sources folder. If it does not appear, right-click on the Sources folder, click Add File to Project, then select your descriptor file. Now, when your main program compiles, the descriptor file will automatically be included. Be sure to save your project by clicking on Project, Save Project in the menu.
Now it’s time for coding. MikroC compiler has an USB library. You can find it in the “Library Manager” (under the “Project Manager” tab). Check it.
For details about the functions go through,
Help→mikroC PRO for PIC Libraries→Hardware Libraries
At the end of the list you’ll find “USB Library”.
MikroC USB Library
MikroC provide two types of USB library. One for general USB communication and other for HID USB communication. I am going to discuss later one.
1) HID Enable: void HID_Enable(char *readbuff, char *writebuff);
Enables USB HID communication. It takes two parameters. ReadBuffer, the variable (more accurately an array) where you want to store the USB data that is come from the PC and WriteBuffer, where you want to store the data that’ll sent to PC.
According to datasheet these variables should be in USB RAM. MCU has 1 Kbyte USB RAM, starting from 400h to 7FFh.
From address 400h to 4FFh is for descriptor. So you actually have 500h to 7FFh for data store. So the two variables will be declared like below:
unsigned char readbuff absolute 0x500;
unsigned char writebuff absolute 0x540;
(“absolute” specifies the starting address in RAM for a variable or a starting address in ROM for a constant. If the variable or constant is multi-byte, higher bytes will be stored at the consecutive locations.)
2) HID_Read: char HID_Read(void);
Receives message from host and stores it in the Read Buffer. If the data reading has failed, the function returns 0. Otherwise, it returns number of characters received from the host.
3) HID_Write: char HID_Write(char *writebuff, char len);
Function sends data from Write Buffer to host. It has two parameters. Write buffer’s variable name and length of the data to be transmitted. If the data transmitting has failed, the function returns 0. Otherwise, it returns number of transmitted bytes. Function call needs to be repeated as long as data is not successfully sent.
4) HID_Disable: void HID_Disable(void);
Disables USB HID communication.
5) USB_Interrupt_Proc: void USB_Interrupt_Proc(void);
This routine is used for servicing various USB bus events. Should be called inside USB interrupt routine. Example:
How to Check if Your Descriptor is Working or Can the PC See My Device?
First we are going to simulate it in proteus. Draw the circuit like below.
Change the frequency (20 MHz) and load the hex file.
Play the simulation. Now if you close the USB connector in proteus you’ll see just like below:
Open the USB HID terminal (Tools › USB HID Terminal). Voila!! It detects my device. Now write something at ‘Communication’ box and press send. You will see mcu read and send back the same thing.
To see the device info, press the “Info” button (at top right corner). You’ll see a window like below:
Hardware implementation will be discuss in the next tutorial.