USBPIC dualport

Because this will be quite interesting project for everyone (every PICer at least), I will try to describe it in English.

As a part of my diploma thesis I was asked to design a USB device that will replace two serial ports of a computer and a serial to optical link converter. Because there is a lot of materials on internet in this field, I thought that it will be simple to extend a CDC sample from microchip. But it wasnt. I could not find anything (anything = schematic + source code :) like that. Finally I found somewhere on forums a VCP2 example. It is a MPLAB project for PIC18F4550 microcontroller. Whole USB stack is put into 4 files (usb9.c/usb9.h, usbvcp.c/usbvcp.h). It is really cute, that you dont need to place more 20 files into your project just to enable USB functionality. Main code file (main.c) explains very cleanly how to use this project, just have a look:

TestVCP2 main.c
#include "io_cfg.h"

#include <p18cxxx.h>
#include "typedefs.h"
#include "usb9.h"
#include "usbvcp.h"

#include "p18FUSBCfg.h"      // pragma configs..

rom char USB_Manufactor_StrDsc[] = "Me";
rom char USB_Product_StrDsc[] = "Test VCP2";

void main( void)
  // USB Init


  // Forever..

    if( kbhitUSBVCPA())
      putcUSBVCPA( getcUSBVCPA());

    if( kbhitUSBVCPB())
      putcUSBVCPB( getcUSBVCPB());

You will need a MCC18 compiler to produce a .hex file. Unfortunatelly I was not able to port this code into hitech compiler (some problems with pragma static allocation)

If you are having problems understanding this piece of code, I will explain. When you compile this project and burn the hex file into PIC, it will install you two virtual serial ports (for example COM1: and COM2:). Functions kbhitUSBVCBA, putcUSBVCPA and getcUSBVCPA are related to the first virtual port (COM1) and the next three functions are related to the second port. Functions kbhitUSBVCPX tests, whether it was sent anything to this virtual port, same meaning as kbhit in ordinal C language. Function putcUSBVCPX transfers a byte to computer, and getcUSBVCPX gets a byte from virtual port. Together the four lines of code echoes everything back. If you would like to virtually connect COM1: -> COM2: and COM2: -> COM1: you should do this in this way:

virtual cross connection
    if( kbhitUSBVCPA())
      putcUSBVCPB( getcUSBVCPA());

    if( kbhitUSBVCPB())
      putcUSBVCPA( getcUSBVCPB());

The only problem is, that the device needs a driver. Funny thing about this is, that the code simulates a real device "FTDI USB Virtual Com Port". And the driver can be downloaded from FTDI webpage, or Here: FTDI USB Virtual Com Port (VCP) Driver

Ok, but now we want to make a real dual serial port USB device. For this purpose I have chosen MAX3100 integrated circuit. It is a fully configurable SPI to UART converter. In my project you will find mentioned VCP2 driver and my own simple MAX3100 driver. Both modules are connected together in main.c file. My device has two physical ports (typical serial CANON 9 pin connector and a optical converter - I dont know the exact type, it is just used in the EMC laboratory where am I working in the diploma thesis) and a bluetooth module for remote. Together the core PIC microprocessor communicates with 3 MAX3100 modules. Every module is on its own small PCB board and all of them are linked together with ribbon cable. This was for speeding up the design process.

Schematic diagram
 Full size

Core module

This module contains the PIC18f4550 microcontroller with a few passive components, 5x2 pin connector is for spi bus ribbon cable and is present on all modules. Because there was a demand to use this device remotely (without PC turned on), this module conains a 7805 stabiliser circuit.

Core board schematic
 Full size

Core board
 Full size

Serial port module

This module is dedicated to connect on the SPI BUS, the MAX3100 converts the 16 bit commands from SPI bus into UART signals (TTL levels) and MAX232 converts this into RS-232 levels. Please note, that there is available a IC from MAXIM (MAX3110) utilizing both circuits inside one package.

Serial port board schematic
 Full size

Serial port board
 Full size

Optical port module

Very similar to the serial port module, except there is no TTL to RS232 level converter, but optical diode and photoresistor (HFBR-2523) directly connected to the MAX3100 output. Note that there is a mistake in the schmatic/board on the phototransistor receiver side.

Optical port board schematic
 Full size

Optical board
 Full size

Bluetooth module

Same as optical port module, but instead of diodes, there is a OEM SPA310 bluetooth module. Next mistake - the rx/tx lines are swapped.

Bluetooth board
 Full size

MAX3100 low-level Driver

MAX3100 driver code
#define MAX_WRITECONF  0b1100000000000000
#define MAX_READCONF  0b0100000000000000
#define MAX_WRITE    0b1000000000000000
#define MAX_READ    0b0000000000000000

#define MAX_NOIRQ    0b0000000000000000
#define MAX_nTRANSMIT  0b0000010000000000
#define MAX_RTS       0b0000001000000000
#define MAX_RECEIVE   0b1000000000000000
#define MAX_TRANSMIT  0b0100000000000000


// 1.8432 MHz
#define MAX_115200    0
#define MAX_57600    1
#define MAX_28800    2
#define MAX_14400    3
#define MAX_7200    4
#define MAX_3600    5
#define MAX_1800    6
#define MAX_900      7
#define MAX_38400    8
#define MAX_19200    9
#define MAX_9600    10
#define MAX_4800    11
#define MAX_2400    12
#define MAX_1200    13
#define MAX_600      14
#define MAX_300      15

//unsigned char MAXrcvHi. MAXrcvLo;

#define MAXConfig(Addr, nCfg)  \
    MAXSend(Addr, nCfg | MAX_WRITECONF)

#define MAXSend(Addr, nData)     \
  {                \
    Addr = 0;           \
    SPIRead( (nData) >> 8 );   \
    SPIRead( (nData) & 255 );  \
    Addr = 1;          \

#define MAXSendRcv(Addr, nData)   \
  {                \
    Addr = 0;           \
    MAXrcvHi = SPIRead( (nData) >> 8 );   \
    MAXrcvLo = SPIRead( (nData) & 255 );  \
    Addr = 1;          \

#define MAXUpdate( Addr )      \
  {                \
    Addr = 0;          \
    MAXStatus = SPIRead( (MAX_READCONF) >> 8 );    \
    MAXStatus <<= 8;      \
    MAXStatus |= SPIRead( (MAX_READCONF) & 255 );  \
    Addr = 1;          \

#define MAXKbhit(Addr)        \
    (MAXStatus & MAX_RECEIVE)

#define MAXCanSend(Addr)      \
    (MAXStatus & MAX_TRANSMIT)

#define MAXgetch( Addr, nChar )    \
  {                \
    Addr = 0;           \
    MAXStatus = SPIRead( (MAX_READ) >> 8 );   \
    MAXStatus <<= 8;              \
    nChar = SPIRead( (MAX_READ) & 255 );  \
    Addr = 1;          \

#define MAXPrepareSend( Addr )      \
    MAXUpdate( Addr );        \
    while ( !MAXCanSend(Addr) )   \
    {                \
      DelayUs(200);        \
      MAXUpdate( Addr );      \

#define MAXPutch( Addr, nChar )    \
    MAXSend( Addr, (MAX_WRITE|(nChar)) );

unsigned int MAXStatus = 0;
 Download max3100.h

Final product

Photos soon...

 sorry for low qualitty, taken with my laptop\'s camera


Click here to access download directory

last change:
17.nov 2008, 12:46 - added low quality photos