Você está na página 1de 8

INTERRUPT DRIVEN USB INTERFACE LIBRARY for PIC18F4550

Based on Microchips CDC Library, written by Elco Jacobs, Feb 2007. Last revision Sept 2008. Getting started The library provides 4 basic functions:
void InitializeUSB(void);

This function initializes the USB service. You should call it at the start of your main().
void RamString2USB(auto char *SendString);

This function sends a string from the RAM memory to the PC. All variable strings are in RAM memory. The string should be null terminated: the last character of the string should be \0.
void RomString2USB(auto const rom char *SendString);

This function sends a string from the ROM (instruction) memory to the PC. All constant strings are in ROM memory.
unsigned char StringFromUSB(auto ram char *ReceiveString);

This function receives a string from the PC. When used with hyperterminal, the strings usually contain just 1 character because of the slow typing speed. When one ore more characters have been received, the function returns 1, otherwise 0. How to use the libray. 1. In the project manager, add the library file InterruptUSB.lib to your project. 2. In main.c (or in any other file in which you want to use the USB functions) include InterruptUSB.h. This header file contains the prototypes for the USB functions.
#include "InterruptUSB.h"

3. In your high interrupt service routine add the following code:


void _high_ISR (void) { if( INTCONbits.TMR0IF==1){ _asm goto USBInterrupt _endasm; } } #pragma code

/* <-------- */ /* <-------- */ /* <-------- */

This code checks if an interrupt is caused by timer0, and jumps to the USBInterrupt function.

4. In the beginning of your main() function, call InitializeUSB():


void main(void){ //Declare variables //End of declare variables InitializeUSB(); //Your program starts here while(1){

} }

5. To send a constant string to the PC, use RomString2USB()


RomString2USB("Hello World!\r\n");

\r\n tells hyperterminal to start a new line. 6. To send a variable string to the PC, use RamString2USB(). Note that here the string is declared as a variable in the program. The strings should always be null terminated. (sprintf() does this automatically).
// if the size is larger (tested: 64) // communication will stall. // This is related to sprintf. Precise // cause is unknown so far. int FirstNumber, SecondNumber, Result; FirstNumber = 3; SecondNumber = 4; Result = FirstNumber*SecondNumber; sprintf(TempString, "%d X %d = %d\r\n", FirstNumber, SecondNumber, FirstNumber*SecondNumber); RamString2USB(TempString); char TempString[40];

This will display: 3 X 4 = 12

7. To receive a string from the PC to the microcontroller, use StringFromUSB():


int TSlength, i; int ISlength = 0; char TempString[3];

// User cannot type faster, so 3 // is enough for hyperterminal if(StringFromUSB(TempString)){ TSlength = strlen(TempString); for(i=0;i<TSlength;i++){ // process received characters here } }

8. Finally in the Build options, on the tab MPLINK Linker, check Suppress COD file generation.

Terminal functions When you are using this library with a PC running hyperterminal, take a look at the functions in terminal.h and terminal.c There a four functions in terminal.h/terminal.c: ClearScreenToUSB(), UserInput(), AnyKey(), String2Decimal() and WelcomeScreen(). Include terminal.h in your *.c file and add terminal.h and terminal.c to your project. See terminal.c for more instructions on how to use these functions.

Technical Stuff The Interrupt Driven USB Interface is a modification of Microchips CDC library. The CDC library connects the microcontroller to a PC through a virtual COM port. In the original CDC library from microchip the main() function looks like this:
void main(void) { InitializeSystem(); while(1) { USBTasks(); ProcessIO(); }//end while }//end main

// USB Tasks // See user\user.c & .h

Here USBTasks() services the USB functions and ProcessIO() is a function written by the user. Because USBTasks() should be called about every 1 ms to keep the USB communication alive, there are some restrictions on ProcessIO(): The time ProcessIO() can take is very limited There can be no blocking functions in ProcessIO() ProcessIO() must therefore be implemented as a finite state machine, which can be difficult and limiting. The modified USB library uses a timer0 interrupt to keep the USB communication alive. When a timer0 overflow occurs (about every 683 us, f=1.46 kHz), the high interrupt service routine (ISR) will be called. In this routine the USB tasks are serviced and a pending read from USB or write to USB is serviced. Because the USB communication is now interrupt driven, this leaves no restrictions on the main(). The main program will simply be interrupted each time to service the USB tasks.

Architecture The send and receive functions do not write or read directly to or from the USB interface, but to two circular buffers. RamString2USB() and RomString2USB() copy a string to the SendBuffer. If the SendBuffer is full, the function will wait at most 16 interrupt cycles for the buffer to empty. For RamString2USB() a maximum of 255 characters will be copied, to prevent an infinite loop if a string is not null-terminated. RomString2USB() does not have this limit, because ROM strings are usually correctly terminated. StringFromUSB(char * ReceiveString) copies a string from the ReceiveBuffer to ReceiveString. It stops at a NULL character or at timeout (just 1 interrupt cycle).

SendBuffer and ReceiveBuffer are both 128 byte ring buffers. In a ring buffer you keep track of a write position and a read position in each buffer. Your write position is ahead of your read position if there is data in the buffer. After writing to the buffer, your write position moves up 1 byte, after reading from the buffer, your read position moves up 1 byte. After writing to the last position (255), your next write position will be 0 again. Therefore the name ring buffer. The USBInterrupt() function in the high ISR has 3 tasks: It checks if data has been received from the PC and it copies it to the ReceiveBuffer. If there is data in the SendBuffer, it reads a chunk of data from the buffer and sends it to the PC. It calles USBTasks(), to keep the USB communication alive. So data will NOT be sent immediately if you use the function RamStringToUSB() or RomString2USB(), but in the next timer0 interrupt (if there is no previous data in the queue) . Also, data received from the PC will not be immediately available. It will be copied to the ReceiveBuffer in the next interrupt. Timer0 is configured as a 8 bit timer, using the internal clock with a 1:8 prescaler. With an internal oscillator frequency of 48 MHz (so a clock cycle of 48 MHz / 4 = 12 MHz), this gives an interrupt frequency of 12 MHz / (2^8 * 8) = 5859 Hz.

FAQ How can I see if the microcontroller is recognized by the PC? o Go to the Device Manager and expand ports. Here the microcontroller is listed as Microchip CDC Communications Port (COMxx). On which COM port is the CDC communications driver? o See question above. What should be the port settings for the CDC driver (in device manager)? o Baud Rate: 19200 o Data bits: 8 o Parity: none o Stop bits: 1 o Flow control: none o Advanced Use FIFO buffers: on Use highest settings, lower them when problems occur. COM port: own preference. I get a notification USB device not recognized o If you are using the microcontroller behind a USB hub, this can cause problems. Try connecting it to your PC without a hub. Try to reset the microcontroller. This might also occur if you are drawing too much current from the microcontroller/usb. Try powering your microcontroller from an external supply. The microcontroller is not listed in the Device Manager o Check if the high interrupt service routine contains the code stated in point 3 of How to use the library. o Check if InitializeUSB() is called in the beginning of your main(). I seem to receive random trash from the PC. o Make sure that you use ReceiveFromUSB(..) with a pointer to a char as argument. Using a char as argument will cause the function to write to the wrong location. See point 6 of How to use the library. I send a value to the microcontroller, but another value is received. o Try debugging with Terminal by Br@y++ instead of Windows HyperTerminal. Here you can send hex value 1 for example, by sending $01. Also there is a separate window for send and receive. You can download it here: http://bray.velenje.cx/avr/terminal/ This terminal can only use COM1-7, so you might need to change the COM port used by the CDC driver.

My program runs slower than before using the USB library. o This is normal. Remember that your program will be interrupted for the USB communication every 170 us. When no data is sent or received, the Interrupt routine takes 10 us. This is without the context save/restore that comes with an interrupt. When writing your program, keep in mind that a part of the processor time is used for the USB communication. When I simulate my program using MPLAB SIM, the interrupt does not seem to work. o The cause of this problem is the boot loader that is used in the microcontroller. The boot loader is located in the memory of the microcontroller at address 0x000-0x800. The interrupt routines are placed after the boot loader at location 0x808 (high ISR) and 0x816 (low ISR). Normally, when no boot loader is used, the interrupts are placed at 0x008 and 0x016. MPLAB SIM still expects the interrupt routines to be at this location, and therefore interrupts do not work in MPLAB SIM. There is a workaround for this problem. Add the following code to your project, under your boot loader code.
// DUMMY interrupt jumps, will not be written to PIC, because area is protected. #pragma code _DUMMY_HIGH_INTERRUPT_VECTOR = 0x000008 void dummy_high_isr (void) { _asm goto 0x000808 _endasm; } #pragma code #pragma code _DUMMY_LOW_INTERRUPT_VECTOR = 0x000018 void dummy_low_isr (void) { _asm goto 0x000818 _endasm; } // end dummy interrupt jumps

This code writes a jump to 0x808 at address 0x008 and a jump to 0x816 at address 0x016. Now when an interrupt occurs and MPLAB SIM jumps to 0x008 or 0x016, it will be forwarded to the right interrupt addresses and simulation works. When building a hex file to program the microcontroller, the dummy interrupts will not be programmed, because the area 0x000-0x800 is protected in the linker file to prevent overwriting the boot loader. Using these dummy interrupt jumps the code will work in MPLAB SIM and when running on the microcontroller.

I get an error when building starting with: MP2COD 4.02, COFF to COD File Converter Copyright (c) 2006 Microchip Technology Inc. o In Build options, tab MPLINK Linker, check Suppress COD file generation. (step 8)

Você também pode gostar