PDA

View Full Version : PIC 18F4550 and MCP23017



DaveC3
- 9th May 2008, 16:40
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.



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

mackrackit
- 10th May 2008, 12:48
I have not played with USB much.
This is something I will study from.
Thanks

artha2
- 26th November 2010, 09:50
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?



'* 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

DaveC3
- 26th November 2010, 14:44
What errors are you getting?

The include files are for USB

Dave

artha2
- 27th November 2010, 09:12
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

DaveC3
- 27th November 2010, 15:43
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


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

artha2
- 29th November 2010, 08:33
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

DaveC3
- 29th November 2010, 19:20
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

artha2
- 1st December 2010, 10:04
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?

cncmachineguy
- 1st December 2010, 15:30
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?

DaveC3
- 1st December 2010, 17:03
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

artha2
- 4th December 2010, 09:09
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

DaveC3
- 4th December 2010, 15:01
You are welcome, glad I could help.

Take care

Dave