- 
	
	
	
		
PIC 18F4550 and MCP23017
	
	
		I have seen some questions about using a port expanders, here is a version that works for the MCP23017, I am using MicroChip's USB boot loader.  This program simply sets the 23017's PortA as inputs and PortB as outputs (driving LEDs).  It also send the PortA value to the PC via USB.
	Code:
	
define LOADER_USED 1        ' USB Boot loader
define RESET_ORG 800h
define INTERRUPT_ORG 808h
DEFINE OSC 48
DEFINE I2C_SCL PORTA,1
DEFINE I2C_SDA PORTA,0
Define I2C_SLOW 1     'At this clock speed this needed to be added to make it work.
'define SHIFT_PAUSEUS 200
ADCON1 = $F       'port A digital
CMCON = 7         'PortA Digital
USBBufferSizeMax   con 16  ' maximum buffer size
USBBufferSizeTX    con 16  ' input 
USBBufferSizeRX    con 16  ' output
' the USB buffer...
USBBuffer        Var Byte[USBBufferSizeMax] 
USBBufferOut     Var Byte[USBBufferSizeMax]
USBBufferCount   Var Byte 
Addr             var byte
j                var byte
i                var byte
'======================================================================================
' Port A Registers (8 Bit Mode)
IODIRA             con   $00          ' Direction Bit Control Reg
IOBITSA            con        %11111111        ' Direction Bit Settings Reg (0 = output, 1 = input)
IPOLA              con        $01                        ' Interrupt Polarity Control Reg
GPINTENA           con        $02                        'Interrupt Enable Reg
DEFVALA            con        $03
INTCONA                   con        $04
IOCONA                   con        $05                        ' I/O Configuration Reg
IOCONSETA          con        %10111000        ' IOCON register setting
GPPUA                   con        $06                        ' Pull-Up Control Reg
INTFA                   con        $07                        ' Interrupt Flag Register
INTCAPA                   con        $08
GPIOA                   con        $09                        ' General Purpose I/O
IOLATA                   con        $0A                        ' Output Latch Reg
'Port B Registers (8 Bit Mode)
IODIRB                  con        $10                        ' Port B Direction Bit Control Address
IOBITSB                  con        %00000000                ' Port B Direction Bit Settings (0 = output, 1 = input)
IPOLB                  con        $11                        ' Interrupt Polarity Control
GPINTENB          con        $12                        ' Interrupt Enable Reg
DEFVALB                  con        $13
INTCONB                  con        $14
IOCONB                  con        $15                        ' I/O Configuration Reg
IOCONSETB         con        %10111000                ' IOCON register setting
GPPUB                  con        $16                        ' Pull-Up Control Reg
INTFB                  con        $17                        ' Interrupt Flag Register
INTPOLB                  con        $18
GPIOB                  con        $19                        ' General Purpose I/O Reg
IOLATB                  con        $1A                        ' Output Latch Reg
'========================================================================================
' Port A Reguisters (16 Bit Mode)
IODIRA16            con            $00
IODIRB16            con            $01
IOCONA16            con            $0A
IOCONB16            con            $0B
GPIOA16             con                $12
GPIOB16             con                $13
OLATA16             con                $14
OLATB16             con                $15
IO_EXP0WR           con            $4E                ' MCP23017  Write
IO_EXP0RD           con            $4F                ' MCP23017  Read
IO_DAT              var     byte    ' MCP23017  Data read
IO_DAT_OLD          var     byte
Out_Dat             var     byte
    '   Interrupt definition   
    '   ====================
        '   USB interrupt used to keep USB connection alive
INCLUDE "DT_INTS-18.bas"    ' Base Interrupt System
INCLUDE "ReEnterPBP-18.bas"     ' Include if using PBP interrupts
ASM
INT_LIST  macro    ; IntSource,         Label,  Type, ResetFlag?
        INT_Handler    USB_INT,  _DoUSBService,   ASM,  yes
    endm
    INT_CREATE               ; Creates the interrupt processor
ENDASM
usbinit ' initialise USB...
USBService                                    ' keep connection alive
UIE = $7F
UEIE = $9F
@   INT_ENABLE  USB_INT
gosub InitMPC23017
gosub ReInitPortA
goto Programstart
' ************************************************************
' * Sub-routines                                             *
' ************************************************************
DoUSBService:    'Keeps USB alive
    usbservice
@   INT_RETURN
' ************************************************************
' * receive data from the USB bus                            *
' ************************************************************
DoUSBIn:
   USBBufferCount = USBBufferSizeRX              ' RX buffer size
   'USBService                                    ' keep connection alive
   USBIn 1, USBBuffer, USBBufferCount, DoUSBIn   ' read data, if available
   return
    
' ************************************************************
' * wait for USB interface to attach                         *
' ************************************************************
DoUSBOut:
   USBBufferCount = USBBufferSizeTX              ' TX buffer size
   'USBService                                    ' keep connection alive
   USBOut 1, USBBufferout, USBBufferCount, DoUSBOut ' if bus available, transmit data
   return
   
InitMPC23017:
Pause 100
'#############  PORTA
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOCONA,IOCONSETA]
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IODIRA,$0]          'PortA inputs
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOLATA,$FF]            'PortA = 0
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IPOLA,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[DEFVALA,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[GPPUA,$FF]           'pull-ups enabled
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[INTCONA,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[GPINTENA,$FF]        'interupt on change
'##############  PORTB
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOCONB,IOCONSETB]
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IODIRB,0]
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOLATB,$FF]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IPOLB,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[DEFVALB,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[INTCONB,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[GPINTENB,0]
return
ReInitPortA:
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOCONA,IOCONSETA]
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IODIRA,$FF] 
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[GPPUA,$FF]           'pull-ups enabled
return
' ************************************************************
' * main program loop - remember, you must keep the USB      *
' * connection alive with a call to USBService every couple  *
' * of milliseconds or so...                                 *
ProgramStart: 
   'gosub DoUSBIn
   'gosub DoUSBOut
i2cwrite PortA.0,PortA.1,IO_EXP0wr,[gpioa]
i2cread PortA.0,PortA.1,IO_EXP0rd,[IO_dat] 
if IO_DAT_OLD != io_dat then
''''   usbbufferout(0) = io_dat
   for j = 0 to 15
  usbbufferout(j) = io_dat
   next j
   gosub  dousbout
 out_dat = io_dat
   io_dat_old = io_dat
endif
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[gpioB,out_dat]
PAUSE 250
''i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[gpioB,0]
''PAUSE 250   
   
goto ProgramStart  
   end
 Thanks
Dave
	 
 - 
	
	
	
	
		I have not played with USB much.
This is something I will study from.
Thanks
	 
 - 
	
	
	
	
		Hey Dave,
Im trying to use 16f877a with macp23017, but I wasn't successful.
I define the mcp23017 register, but when I want to compile my program it gives me error.
I tried to use your program and learn from it but I had no success, I noticed you are using some include file that I dont have them.
I have 16 led on each port would you please help me?
	Code:
	
'*  Notes   :  XT 4Mhz                                          *
'*          :                                                   *
'****************************************************************
'Includes 
include "modedefs.bas" 
'****************************************************************
'Definitions
DEFINE I2C_SDA PORTc,4 
DEFINE I2C_SCL PORTc,3   
'****************************************************************
'Variables
a var byte
chip1 var byte
chip2 var byte
chip3 var byte
chip4 var byte         
'****************************************************************
'Alias
CP VAR PORTc.3
DP VAR PORTc.4
                         
'****************************************************************
'Initialization
    'Pic 16f877A
option_reg.7 = 0 'portb pullups enabled         
SSPSTAT=%00111101
'SSPCON1=%00101111
SSPCON2=%00000001
    'MCP 23017
IODIRA Con $00
IODIRB con $00
IPOLA con $00
IPOLB con $00
GPINTENA con &00
GPINTENB con $00
DEFVALA con $00
DEFVALB con $00
INTCONA con &00
INTCONB con &FF
IOCON con %00001000
GPPUA con $00
GPPUB con $00
INTFA con $00
INTFB con $00
INTCAPA con &00
INTCAPB con &00
GPIOA con $00
GPIOB Con $FF
OLATA con $00
OLATB con &FF  
'****************************************************************
'set ports   
     trisa=%00000000             
     trisc=%00000000     
     pause 500                 
     goto start          
start:
    porta.5 = 1
    portc.7=0
    pause 500
    porta.5 = 1
    portc.7=1
    pause 500
    I2CWRITE dp,cp,$00,chip1,[%00000000] 'one-shot sample
    pause 200
    I2CWRITE dp,cp,$00,chip1,[%11111111] 'one-shot sample 
    pause 200
    I2CWRITE dp,cp,$00,chip1,[%00000000] 'one-shot sample
    pause 200
    I2CWRITE dp,cp,$00,chip1,[%11111111] 'one-shot sample 
    pause 200
     goto start   
end
 
	 
 - 
	
	
	
	
		What errors are you getting?
The include files are for USB
Dave
	 
 - 
	
	
	
	
		Thanks Dave,
before i used to define 16f877a register like ( adcon = $82), so i thought it should be same to define mcp registers like (IODIRA = $00 to make my mcp port a as output), I got my first error there (  syntax error, red color on my whole line) then I saw that you define by constant. so i changed my method and followed you and defined the by using "con". but something happens that I dont understand it gives me error when i define GPINTENA con &00, but it accepts GPINTENB con &00 after seeing this I told myself ok im going to use assembly language and i will insert it to my Picbasic as a sub routin. I know that i have to Equate all register and variable like ( eecon1 equ h'08'  ) but I think those are for my pic and i dont know how i shoud have access to MCP register to for example turn the pull ups on or off
I think I'm totally lost. I thought that i define my chip one time (which pin is input and which one output, interrupts, pull ups,..., and then I can use I2CWRITE and read command to send my bytes to the outputs and then read from it.
how would you do that?
Thanks for your time, I really appreciate it
Artha
	 
 - 
	
	
	
	
		I think you have a typo error
GPINTENA con &00 should be GPINTENA con $00
Try that
Dave
You also need to initialize the MCP23017
	Code:
	
InitMPC23017:
Pause 100
'#############  PORTA
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOCONA,IOCONSETA]
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IODIRA,$0]          'PortA inputs
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOLATA,$FF]            'PortA = 0
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IPOLA,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[DEFVALA,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[GPPUA,$FF]           'pull-ups enabled
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[INTCONA,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[GPINTENA,$FF]        'interupt on change
'##############  PORTB
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOCONB,IOCONSETB]
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IODIRB,0]
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOLATB,$FF]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IPOLB,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[DEFVALB,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[INTCONB,0]
'i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[GPINTENB,0]
return
ReInitPortA:
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IOCONA,IOCONSETA]
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[IODIRA,$FF] 
i2cwrite  PortA.0,PortA.1,IO_EXP0WR,[GPPUA,$FF]           'pull-ups enabled
return
 
	 
 - 
	
	
	
		
you were right
	
	
		Hey Dave, thanks man
you were right i made that mistake and i looked over and i couldn't catch it, you are having good eyes  (i fixed it now).
but still there is no success. even i copy you code and deleted you usb codes line and compile it but i could not get it to work.
I connect all three address pin for my MCP to ground
I pulled up mcp reset pin with a 15K R
I pulled up SDA and SCL pin with two 1k R
portc.3 is my clock and portc.4 is my data
portc.7 is  connected to an led just for test, so i know where i am in the program
and this is my codes
again thanks for your time
'*  Notes   :                                                   *
'*          :                                                   *
'*************************************************  ***************
'Includes 
include "modedefs.bas"                       
'*************************************************  ***************
'Definitions
DEFINE I2C_SDA PORTc,4 
DEFINE I2C_SCL PORTc,3
define ADC_BITS 10
define ADC_CLOCK 3
define ADC_SAMPLEUS 50
'*************************************************  ***************
'Variables
A var byte
Chip1 var byte
POT1 var word
POT2 var word                  
'*************************************************  ***************
'Alias
CP VAR PORTc.3
DP VAR PORTc.4
          
'*************************************************  ***************
'Initialization
'Pic 16f877A 
adcon1=$02       'left justify A0 and A1
CCP1CON=0       'disable capture/compare
CCP2CON=0
CMCON=7         'disable comparater
option_reg.7=0  'portb pullups enabled         
    'set ports
trisa=%11111111
trisb=%00000000
trisc=%00000000
trisd=%00000000
'MCP 23017
chip1 = $40
IODIRA      con $FF         ' port A output
IODIRB      con $FF	     ' port B output
IOCON       con $3C        ' Defining the MCP Bank as 0 and sequential disable
MCPA        con $12
MCPB        con $13
MCPAL       con $14
MCPBL       con $15
      
       'Initialing MCP 
       
i2cwrite  dp,cp,$0A,chip1,[iocon]
i2cwrite  dp,cp,$0B,chip1,[iocon]       
i2cwrite  dp,cp,$00,chip1,[iodira]
i2cwrite  dp,cp,$01,chip1,[iodirb]
'IODIRA Con $00
'IODIRB con $00
'IPOLA con $00
'IPOLB con $00
'GPINTENA con $00
'GPINTENB con $00
'DEFVALA con $00
'DEFVALB con $00
'INTCONA con $00
'INTCONB con $FF
'IOCON con %00001000
'GPPUA con $00
'GPPUB con $00
'INTFA con $00
'INTFB con $00
'INTCAPA con $00
'INTCAPB con $00
'GPIOA con $00
'GPIOB Con $FF
'OLATA con $00
'OLATB con $FF)  
'*************************************************  **************     
'Main Program
     
start:
    portd =$00
    portc.7=0
    pause 500
    portd = $ff
    portc.7=1
    pause 500
    portc.7=0
    pause 500
    'I2CWRITE DataPin,ClockPin,Control,{Address,}[Value{,Value...}]{,Label}
    I2CWRITE dp,cp,mcpa,chip1,[$FF] 'porta on
    portc.7=1
    pause 200
    I2CWRITE dp,cp,mcpa,chip1,[$00] 'porta off 
    portc.7=0
    pause 200
    I2CWRITE dp,cp,mcpal,chip1,[$FF] 'porta on
    portc.7=1
    pause 200
    I2CWRITE dp,cp,mcpal,chip1,[$00] 'porta off
    portc.7=0
    pause 200
    I2CWRITE dp,cp,mcpb,chip1,[$FF] 'portb on
    portc.7=1
    pause 200
    I2CWRITE dp,cp,mcpb,chip1,[$00] 'portb off
    portc.7=0
    pause 200
    I2CWRITE dp,cp,mcpbl,chip1,[$FF] 'portb on
    portc.7=1
    pause 200
    I2CWRITE dp,cp,mcpbl,chip1,[$00] 'portb off
    portc.7=0
    pause 200       
  
    goto start   
end
	 
 - 
	
	
	
	
		The smallest resister I have used for I2C is 4.7K, also you need to initialize the MCP23017 correctly.
Also you have set your port direction to input
IODIRA con $FF ' port A output
IODIRB con $FF ' port B output
to make the port output you need to change the $FF to $00, just like setting a port direction in a PIC.
Good luck
Dave
	 
 - 
	
	
	
	
		Ok I changed it to 4.7k R and correct the port direction.
could you please tell me what im doing wrong for initializing the chip? I know the default mode chip's port are all input and all the register's bit are %000000000 according to table 1-6 page 11 pf MCP23017 data sheet it means there is no pull up, inverter. am i right?
i just need to have access to those register address which they are all again at page 11 table 1-6. 
and the way to do that with I2CWRITE command is following the help page of PBP which it says: I2CWRITE DataPin,ClockPin,Control,{Address,}[Value{,Value...}]{,Label}
indicate the data pin, indicate clock pin, address of specific register that i want to change (IODIRA &00) table 1-6, address of my chip, [ and value in the bracket ] 
is this the correct way?
	 
 - 
	
	
	
	
		Table 1.6 pg 11 shows POR/RST value of IODIRA and IODIRB is %11111111. NOT %00000000. Setting the dir to 1 makes the pin an input.
Maybe you were looking at the next 2 registers? IPOLA and IPOLB?
	 
 - 
	
	
	
	
		Take a look at your I2C data format
The proper way to send data  for this chip is
i2cwrite I2C_SDA, I2C_CLK,control,[Value{..value}]
In your case it should be:
I2C_SDA is PORTC.4 or as you have defined DP
I2C_CLK is PORTC.3 or as you have defined CP
The control needs to be: (assuming you have the address lines connected to zero volts)
%01000000 or $40 for write
%01000001 or $41 for read
{PicBasic actually changes the control bit.0 for you in the statement I2CWRITE and I2CREAD}
So to set a register you need two values, the registers address and the value you want it to be.
To set the IODIRA to be outputs you would send
Value 1  control code for IODIRA is $00  see table 1-3 in the data sheet
value 2  you want to make them outputs so  would be $00 (just like setting TRIS in a PIC)
I2CWRITE DP, CP, $40[$00, $00]  'IODIRA is set to output
You also need to set the IOCON register
I2CWRITE DP, CP, $40[$05, %10111000]  "see data sheet register 1-6 for details
So to initialize Porta as output you would program this.
I2CWRITE DP, CP, $40[$05, %10111000]   "Set configuration
I2CWRITE DP, CP, $40[$00, $00]  'Make porta all outputs
Then to write to the port you would use this:
I2CWRITE DP, CP, $40[$09,$FF]  '$09 is the address for GPIOA, $FF would make the porta pins high, to set them low, send values [$09, $00]
You only need to set the weak pull-ups if you are making a port(pin) an input.
Hope this helps
Dave
	 
 - 
	
	
	
		
Thanks
	
	
		Thanks Dave,
I really appreciate your time and patient. in I2c command where it asks about the address I thought it asks about chip's physical address, but now thank to you I can fully control this chip and use it. again thanks
Artha
	 
 - 
	
	
	
	
		You are welcome, glad I could help.
Take care
Dave