Open source USB echo demo
From DP
Contents |
Overview
Here is a the open source USB stack we use on our PIC24F and PIC18F projects. We've compiled a simple echo demo to help you get started. Download the package below.
Downloads
- Open source USB stack folder in our SVN
- Zipped open source USB stack
- Echo demo hex file for the Bus Pirate v4
Stack overview
The Open source stack was authored by Honken and JTR over several years. Many others have contributed bug reports and improvements. It has these features:
- USB CDC (virtual serial) support
- USB HID support
- Double buffered
- Interrupt or polling driven
Directory structure
These common USB files are located in \dp_usb\ and can be shared by many projects:
- \dp_usb\cdc.c - USB CDC (virtual serial) driver functions
- \dp_usb\cdc.h - USB CDC (virtuel serial) driver function declarations, and CDC codes definitions.
- \dp_usb\picusb.h - common definitions used by the stack for PCI18F and PIC24F families
- \dp_usb\usb_stack.c - USB stack functions
- \dp_usb\usb_stack.h - USB stack function declarations
- \dp_usb\usb_stack_globals.h - includes files needed for the stack
These project specific USB files should be located in the project directory. That's \echo\ for the echo demo.
- \echo\descriptors.h - USB descriptors, including the manufacturer and device name strings.
- \echo\prj_usb_config.h - Setting for the USB related hardware, and registers. This is where you can change the USB PID, VID, DEV etc.. Also implemented in this file are hardware setups for the board you'll use.
- \echo\descriptors.h - USB descriptor definitions, and declarations
- \echo\configwords.h - Board, or chip specific config words.
USB setup
USB VID/PID
#define USB_VID (0x4d8) #define USB_PID (0x000a) // Microchip CDC #define USB_DEV 0x0002
At the top of prj_usb_config.h file are basic USB configurations. This is where you can change the USB PID, VID, DEV etc...
Manufacturer and device names
Polling
The stack supports both polling and interrupt driven USB communication.
#define USB_INTERRUPTS //use interrupts instead of pollingLocated in the prj_usb_config.h are renaming definitions which are used by various functions through out the code. Enable this for Interrupts, comment it out for polling.
do { usb_handler(); //insert project specific code here )while(1);
With polling you have to regularly call the usb_handler function in your code. See the echo demo for examples. All your code has to be written in a loop, and it has to be serviced repeatedly, another option is to do it on timer.
Interrupt
Interrupts are more difficult to get going, but they work a lot smoother, and don't impact chip performance as much.
PIC24F
#pragma interrupt _USB1Interrupt void __attribute__((interrupt, auto_psv)) _USB1Interrupt() { //USB interrupt //IRQ enable IEC5bits.USB1IE //IRQ flag IFS5bits.USB1IF //IRQ priority IPC21<10:8> usb_handler(); ClearGlobalUsbInterruptFlag(); }
This is the interrupt service routine. It calls the same usb_handler function the polling method does, and it clears the USB interrupt flags. While the two seam identical, interrupt driven communication is much more efficient, since the usb_handler function is called only when there is an USB event to handle, instead of being called all the time.
PIC18F
- Insert explanation here
Sending and receiving data
Here are 3 examples of sending and receiving data from the USB. All of them use a double buffer mode, and all the functions are located in the cdc.c file.
Method 1
if (poll_getc_cdc(&RecvdByte)) putc_cdc(RecvdByte+1);
The function poll_getc_cdc will return the number of characters in the buffer, and move the first in line to the RecvdByte variable.
The putc_cdc function will move the RecvdByte variable, incremented by 1, to be next in line on the out buffer.
Method 2
if (peek_getc_cdc(&RecvdByte)) { RecvdByte = getc_cdc(); putc_cdc(RecvdByte+1); }
This method first checks if there is a character waiting in Que with the peek_getc_cdc function, and if there is, it will retrieve it to the RecivdByte variable via the getc_cdc function. Same as with the first method the received character will be sent via the putc_cdc function incremented by one.
Method 3
if (poll_getc_cdc(&RecvdByte)) { putc_cdc(RecvdByte+1); // CDC_Flush_In_Now(); }
This method is similar to the first, with the exception that the CDC_Flush_IN_Now function is called at the end. This sends the output buffer immediately, while the other two methods wait for the usb_handler function to be called.
Bus Pirate v4
If you have Bus Pirate v4 there is a working example in our SVN.
Adding a new board
How to add new hardware to the echo demo.
CONFIG words
Setup the oscillator for USB
- PIC 18FxxJxx osc settings (16MHZ, PLL/4)
- PIC 18F osc settings (20 mhz pll/5)
_CONFIG2( IESO_OFF & FCKSM_CSDCMD & OSCIOFNC_ON & POSCMOD_HS & FNOSC_PRIPLL & PLLDIV_DIV3 & IOL1WAY_ON & PLL_96MHZ_ON & DISUVREG_OFF)
- PIC 24F osc settings (?)
As with each PIC there are CONFIG words that need to be setup to let the uC know in which stat it should operate. Examples for some of our project config words are contained in the confingwords.h file. Below is the example for PIC24F256GB106 located on the Bus Pirate v4.
Hardware setup function
- add setup to 'SetupBoard() function
prj_usb_config.h
#define CDC_BUFFER_SIZE 64u #define CLOCK_FREQ 32000000 #define BAUDCLOCK_FREQ 16000000 // required for baud rate calculations #define UART_BAUD_setup(x) U1BRG = x #define CDC_FLUSH_MS 4 // how many ms timeout before cdc in to host is sent
Within the prj_usb_config.h file are hardware definitions for some of our boards. The bare minimum required for the SB stack to function are the definitions listed above. The vaules are set for the Bus Pirate v4 hardware, so for other PIC they need to be changed.
