Using Velleman K8076/VM134 PIC-programmer with Microchip's Library of Applications: USB programming basics
In my previous blog I described how to install the micro usb driver on Windows 7 64-bits. This blog describes how you can use Microchip's Libraries of Application without one of Microchip's programmer boards. I use Velleman's K8076 instead. This device is also available as prebuild VM134.

As I browsed through the available applications, I saw the folder "Device - HID - Custom Demos". That immediately got my attention. So I opened the several .NET projects inside. One of the projects is called "HID PnP Demo".
In this project there's a simple 'Form' with a few buttons and a progress bar. Below I describe the analysis of this project. I decided to 'follow' the UI-items and take a dive into the code and see if I could discover what I needed besides the PIC18F4550, a breadboard and my programmer.
hidpnpdemo screen
hidpnpdemo screen

StatusBox_txtbx
  • default text: "Device Not Detected: Verify Connection/Correct Firmware"
  • upon succesfully connecting the device: "Device Found: AttachedState = TRUE"



ToggleLEDs_btn
  • sets flag ToggleLEDsPending which is handled in worker thread.
    It results in sending 0x80 to the device



  • PushbuttonState_lbl
    • default text: "Pushbutton State: Unknown"

    When connected, based on PushbuttonPressed flag it shows:
    • false: "Pushbutton State: Not Pressed"

    • true: "Pushbutton State: Pressed"

    The PushbuttonPressed flag is set in worker thread.
    First a 0x81 is send to the device. When the resulting bytes are: 0x81 0x01
    it is set to true, when resulting bytes are: 0x81 0x00 it is set to false.


    progressBar1 (show ANx voltage )
    • shows ADCValue which is determined in worker thread
      A 0x37 is send to the device and when the result starts with 0x37, the value is
      determined from the next two bytes: byte1 * 256 + byte 2

    Besides the windows application sources in different programming languages (C#, C++.NET and VB.Net) there is also the firmware part for the project which can be found in:
    [location of your microchip library of applications]\USB\Device - HID - Custom Demos\Firmware.

    In order to compile this, you need MLABX and a C18 compiler for compiling code for the PIC18F4550.

    One of the codes that should be on the firmware site is 0x80. So let't do a global search for it and see whether we can find it. One of the files that match is main.c

    void ProcessIO(void) { //Blink the LEDs according to the USB device status if(blinkStatusValid) { BlinkUSBStatus(); } // User Application USB tasks if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) return; //Check if we have received an OUT data packet from the host if(!HIDRxHandleBusy(USBOutHandle)) { //We just received a packet of data from the USB host. //Check the first byte of the packet to see what command the host //application software wants us to fulfill. switch(ReceivedDataBuffer[0]) //Look at the data the host sent, to see what kind of application specific command it sent. { case 0x80: //Toggle LEDs command blinkStatusValid = FALSE; //Stop blinking the LEDs automatically, going to manually control them now. if(mGetLED_1() == mGetLED_2()) { mLED_1_Toggle(); mLED_2_Toggle(); } else { if(mGetLED_1()) { mLED_2_On(); } else { mLED_2_Off(); } } break; case 0x81: //Get push button state //Check to make sure the endpoint/buffer is free before we modify the contents if(!HIDTxHandleBusy(USBInHandle)) { ToSendDataBuffer[0] = 0x81; //Echo back to the host PC the command we are fulfilling in the first byte. In this case, the Get Pushbutton State command. if(sw3 == 1) //pushbutton not pressed, pull up resistor on circuit board is pulling the PORT pin high { ToSendDataBuffer[1] = 0x01; } else //sw3 must be == 0, pushbutton is pressed and overpowering the pull up resistor { ToSendDataBuffer[1] = 0x00; } //Prepare the USB module to send the data packet to the host USBInHandle = HIDTxPacket(HID_EP,(BYTE*)&ToSendDataBuffer[0],64); } break; case 0x37: //Read POT command. Uses ADC to measure an analog voltage on one of the ANxx I/O pins, and returns the result to the host { WORD_VAL w; //Check to make sure the endpoint/buffer is free before we modify the contents if(!HIDTxHandleBusy(USBInHandle)) { w = ReadPOT(); //Use ADC to read the I/O pin voltage. See the relevant HardwareProfile - xxxxx.h file for the I/O pin that it will measure. //Some demo boards, like the PIC18F87J50 FS USB Plug-In Module board, do not have a potentiometer (when used stand alone). //This function call will still measure the analog voltage on the I/O pin however. To make the demo more interesting, it //is suggested that an external adjustable analog voltage should be applied to this pin. ToSendDataBuffer[0] = 0x37; //Echo back to the host the command we are fulfilling in the first byte. In this case, the Read POT (analog voltage) command. ToSendDataBuffer[1] = w.v[0]; //Measured analog voltage LSB ToSendDataBuffer[2] = w.v[1]; //Measured analog voltage MSB //Prepare the USB module to send the data packet to the host USBInHandle = HIDTxPacket(HID_EP,(BYTE*)&ToSendDataBuffer[0],64); } } break; } //Re-arm the OUT endpoint, so we can receive the next OUT data packet //that the host may try to send us. USBOutHandle = HIDRxPacket(HID_EP, (BYTE*)&ReceivedDataBuffer, 64); } }//end ProcessIO

    As you can see, all the codes used on the Windows side are also used in the switch statement: 0x80, 0x81 and 0x37.
    So what happens after receiving 0x80? The function mGetLED1 and mGetLED2 are called. Let's see where they are defined. You can use the MLAB-ide to go to the function mGetLED1() (Go to implementation):
    Searching for 0x80
    Searching for 0x80

    #define mGetLED_1() mLED_1 #define mGetLED_2() mLED_2 #define mGetLED_3() mLED_3 #define mGetLED_4() mLED_4

    Ok. So mGetLED1 returns mLED1, mGetLED2 returns mLED2. As you can see there are even 4 getLEDs defined.

    #define mLED_1 LATDbits.LATD0 #define mLED_2 LATDbits.LATD1 #define mLED_3 LATDbits.LATD2 #define mLED_4 LATDbits.LATD3

    Bingo! I recognize device ports. So there are at least 4 leds, connected to port D0 (pin 19), D1 (pin 20), D2 (pin 21) and D3 (pin 22).
    When we follow the same steps for code 0x81, we see that depending on sw3, the firmware returns either 0x81 0x00 or 0x81 0x01. So what is sw3? There's also a sw2, but it is unused in this example.


    #define sw2 PORTBbits.RB4 #define sw3 PORTBbits.RB5

    Ok, so we have two switches attached to RB4 (pin 37) and RB5 (pin 38) where RB5 is the one we need.


    Now we need to find out what happens after receiving 0x37
    A method ReadPOT() is called and the value is returned in the outgoing message.

    I removed all the ifdefs to see what is used for PIC18F4550. It becomes a lot more readable:

    WORD_VAL ReadPOT(void) { WORD_VAL w; w.Val = 0; ADCON0bits.GO = 1; // Start AD conversion while(ADCON0bits.GO); // Wait for conversion w.v[0] = ADRESL; w.v[1] = ADRESH; w.Val = ADC1BUF0; return w; }

    So it uses ADCON0. Let's see if we can find that in the "hardwareprofile picdem fsusb.h"

    #define mInitPOT() { TRISAbits.TRISA0 = 1; ADCON0 = 0x01; ADCON2 = 0x3C; ADCON2bits.ADFM = 1; }

    TRISA0 is used for the A/D conversion which means port A0 (pin 2)
    The next challenge is to build a bread board version that can be attached to the PC. Below you can see the schema I created with gEDA The schema comes with no warranty. Use at your own risk.
    USB schema PIC18F4550
    USB schema PIC18F4550

    And on breadboard:
    usb pic18f4550 breadboard
    usb pic18f4550 breadboard

    Before connecting it to the computer, always check, double check that you haven't made a mistake in wiring, otherwise you might blow up one or more of your usb ports(!)
    usb connector type B
    usb connector type B

    After I modified the source Form1.cs of the windows project, I could capture the A/D conversion and show it in a 'oscilloscope-ish' way.
    I'll will try to modify the scope a little bit to make it a little more useful. The picture below shows the result of fiddling the variable resistor with a screwdriver.
    A/D conversion: playing with screw driver
    A/D conversion: playing with screw driver

    @paiboon:

    Basically it comes down to this:
    • poll the A/D values in a fixed frequency: see private void FormUpdateTimer_Tick(object sender, EventArgs e)
    • Store the values in an array: private int[] adcValues = new int[1024];
      private int adcIndex = 0;
    • Beware of the bounds of your array: start at position 0 when you reached the end of the array
    • Draw the contents of the array in the paint event: private void paintScope(object sender, PaintEventArgs e)

    I added downloads for code at:PIC18F4550 USB Scope speed limits
    On 2013-10-10, paiboon wrote:

    comment=I+follows+your+explanation+with+pic18F2250.+I+can+get+the+A%2FD+and+show+its+value+in+computer.+However%2C+I+cannot+modified+the+code+to+display+oscilloscope+as+you+did.+Could+you+please+introduce+or+guide+me+for+modifying+the+code%3F+Just+guide+then+I+will+try+to+modify+myself.+Thank+you+in+advance.++Paiboon.

    On 2013-10-10, Eeuwe Vandyke wrote:

    comment=Updated+blog%3A+added+code+links

    On 2014-06-11, LUCAS wrote:

    comment=Proda+por+favor+subir+el+archivo+HEX+porque+no+puedo+compilar+me+sale+errores

    On 2014-06-11, Eeuwe Vandyke wrote:

    comment=I+added+the+HEX-file+(compiled+version+of+the+adjusted+version+of+Device+-+HID+-+Custom+Demos)+to+the+other+downloads.

    Back to List

    All form fields are required.
    A confirmation mail for the comments will be send to you.