USB communication with C18 and MPLAB

From Mech
Jump to: navigation, search

Microchip publishes a USB Framework that consists of a number of files that contain code to handle the tedious parts of USB communication, with a few spots where user code can be included to build USB programs.

This code uses Microchip's CDC library to create a virtual COM port on the computer. Opening this COM port in Hyperterminal or some other terminal program should show numbers appearing once every second or so. The PIC is reading analog voltages from AN0 and writing them out to the terminal.

CDC communication requires a driver on the PC end. You'll need to download these two files and put them in a folder together somewhere: Media:Mchpcdc.inf and Media:Mchpcdc.cat. The first time you run this program on the PIC and plug in into the PC, Windows will need to set up a driver to be able to talk to the PIC. It will probably flounder around a bit and fail to find the driver. When you can, point it to the folder containing these two files and it should find them and set up the driver.

CDC programs can operate in two modes: polling or interrupt. This choice comes from the fact that USB devices must continually monitor the connection to the host, looking for incoming data and handling the sending of data in the output queue. There are two ways to accomplish this: the program can either explicitly call a function periodically to handle USB tasks, or this can be done by a regularly scheduled interrupt.

The framework provides two slots in which user code can be placed: the functions UserInit and ProcessIO. UserInit is called once at the beginning of the program to allow you to do some initialization. ProcessIO is called in a loop; each iteration should be very short and must be non-blocking, if you are using the polling structure.

Below is an example program using the polling structure, showing just the UserInit and ProcessIO functions.

You can download the full MPLAB project here. In the example project the UserInit and ProcessIO functions are jumbled in with a ton of other code in main.c, but you could move them off to their own .c file. Note: this MPLAB project is configured to work with the USB HID bootloader. Using it without a bootloader or with another bootloader will require changes.

The MPLAB project includes code for lots of other setups; Microchip sells demo boards with a variety of chips in them and this code is designed to be able to run on all of them by flipping a few compiler switches. So you'll see stuff like "#if defined(PIC18F87J50_PIM)" or whatever; the only relevant one is PIC18F4553_SCOPE, and the include file "HardwareProfile - PIC18F4553 Scope.h", which assume a PIC18F4550 or 4553


UserInit

void UserInit(void)
{
    mInitAllLEDs();

    //USER CODE!
    //ADC initialization:
    TRISAbits.TRISA0 = 1; //A0 is input
    ADCON0 = 1;           //enable ADC
    ADCON1 = 0b00001110;  //only AN0
    ADCON2 = 0b00010111;  //4 Tad, RC clock
}//end UserInit


ProcessIO

char output_buffer[100];

int count = 0;
void ProcessIO(void)
{   
    BYTE numBytesRead;

    //Blink the LEDs according to the USB device status
    BlinkUSBStatus();
    // User Application USB tasks
    if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) return;


    //Check if we're ready to send more data. If not, we can't wait until we are,
    //because blocking would screw up the rest of the USB tasks that are going on
    //in the background. If we're not ready, we just return from this function and 
    //we'll loop back around in a bit.
    if(mUSBUSARTIsTxTrfReady()) {
        count++;
        if(count == 10000) {            //this happens every second or so
            ADCON0bits.GO_DONE = 1;     //initiate ADC conversion
            while(ADCON0bits.GO_DONE);  //wait for ADC to finish

            //put the result of the ADC conversion in a string:
            sprintf(output_buffer, (const rom far char *)"%u\r\n", ADRESH);

            //queues our message up to be sent
            putsUSBUSART(output_buffer);
        }
    }
 
    //this needs to be called periodically in order for stuff to get sent out:
    CDCTxService();

}		//end ProcessIO
Personal tools