PDA

View Full Version : I2C and MCP23016



keymuu
- 12th January 2009, 11:49
Hi!

Searched around the forum but did not found a solution to my problem.:(

This is what I have:
One MCP23016 connected to PIC18F4550 via I2C.
Pins A0,A1,A2,Vss1,Vss2 and Vss3 grounded, TP flouting...
CLK connected to +5V with 3k9 resistor and 33pF to gnd.
I2C pulled up with two 2k2 resistors.

Can read and write a I2C EEPROM with I2CREAD and I2CWRITE without problems, so I think the problem is solely on the MPC23016 side.

I'm first trying only to use half of it, that is only P1 now.
The pins on P1 are pulled up with 10k each.

The code is like this:
1)
DEFINE i2c_hold 1
deviceIO con $40 ' MCP23016 address
GP0 con $00 ' port GP0 all in
GP1 con $01 ' port GP1 all in
OLAT0 con $02 ' Output latch register
OLAT1 con $03 '
IPOL0 con $04 ' input polarity normal, not inverted
IPOL1 con $05 '
IODIR0 con $06 ' all in = 1
IODIR1 con $07 ' 00111011 down=0,up=1,valoT=3,valoE=4,On/Off=5
INTCAP1 con $09 ' read-only, Interrupt Captured Value
INTCAPbyte var byte ' interrupt capture reg for buttons
IOCON1 con $0b '
I2Cbyte var byte
IOEbyte var byte

2)
I2Creg = OLAT1 : I2Cbyte = $ff : gosub writeIOExtender: pause 20
I2Creg = IODIR1 : I2Cbyte = $7f : gosub writeIOExtender: pause 20
I2Creg = GP1 : I2Cbyte = $7f : gosub writeIOExtender: pause 20

3)
I2Creg = GP1 : gosub readIOExtender' to IOEbyte
LCDOUT HEX2 IOEbyte

4)
writeIOExtender:
i2cdevice = deviceIO
I2CWrite SDA,SCL,i2cdevice,I2Creg,[i2cbyte],error
return'from writeIOExtender
'--------------------------------------------------
readIOExtender: ' to IOEbyte
i2cdevice = deviceIO
I2Cread SDA,SCL, i2cdevice,I2Creg,[IOEbyte],error
return'from readIOExtender

You can read back every register except one (INTCAP). I do not know what fails, read or write or perhaps both, but I do not get back what I wrote, so I do not know which one fails, read or write?

And as you can guess, buttons on GP1 does not com through:mad:

It seems not to have any effect which one is first (2), OLAT1, IODIR1 or GP1?
I have also the error trap if no ack received (NAK) but it jumps not there!

Can anyone see what is wrong?... Thank you in advance ...:)

eggman
- 12th January 2009, 13:46
I could get the MCP23016 to work reliably only by removing the timing capacitor (33pF) all together.

keymuu
- 12th January 2009, 20:11
I could get the MCP23016 to work reliably only by removing the timing capacitor (33pF) all together.

Thanks Eggman!

When I removed that capacitor the program goes to the error-label, so that does not solve my problem :(
I soldered a new 33pF on the PCB and got rid of the error label jump but the original problem remains...

I'm puzzled, I do not know what I should try next!

Any other suggestions?

Darrel Taylor
- 12th January 2009, 21:09
Since you are using an 18F4550, it means you are using MPASM as the assembler.
So, DEFINE i2c_hold 1 needs to be Uppercase.


You can read back every register except one (INTCAP). I do not know what fails, read or write
Not sure what you mean here. The INTCAP? registers are read-only.

Also not sure what you are trying to do with the port.
You write all 1's to the output latch OLAT1, then set all but 1 pin to input IODIR1, then write to GP1 which actually writes to OLAT1 again. Doesn't make sense.

What is it you're trying to do, and what's not working.

keymuu
- 12th January 2009, 22:43
Since you are using an 18F4550, it means you are using MPASM as the assembler.
So, DEFINE i2c_hold 1 needs to be Uppercase.

Not sure what you mean here. The INTCAP? registers are read-only.

Also not sure what you are trying to do with the port.
You write all 1's to the output latch OLAT1, then set all but 1 pin to input IODIR1, then write to GP1 which actually writes to OLAT1 again. Doesn't make sense.

What is it you're trying to do, and what's not working.

Thank you Darrel!

I changed it, so now I have DEFINE I2C_HOLD 1...
but that did not change anything.

With INTCAP I just wanted to say that it is the only register that is read-only, besides it was kind of irrelevant from me to mention that in the first place :o

The final goal is to use the device as an input extender, that is only inputs. However, one should also be able to read and write all registers, except one. Nothing seems to work, as earlier told, I can't tell if it is the write or read that fails. I have tried all kind of values and also changing the sequence of those three initializing lines but without any sign of life. Well, of course maybe some kind of quiet life when not the error label is activated.

What values would you suggest to use in the initialization section and what sequence would you use for those three init lines?

Darrel Taylor
- 12th January 2009, 23:07
I think it's probably working, but you don't realize it.

In your code, the only thing you're reading is GP1, and prior to that it writes to GP1, so you're thinking you should read the same value back.

But when you write to GP1, the value is actually written to OLAT1.
Reading GP1 reads the state of the pins, not what was written to GP1.
<br>

keymuu
- 12th January 2009, 23:49
I think it's probably working, but you don't realize it.

In your code, the only thing you're reading is GP1, and prior to that it writes to GP1, so you're thinking you should read the same value back.

But when you write to GP1, the value is actually written to OLAT1.
Reading GP1 reads the state of the pins, not what was written to GP1.
<br>

Thanks, I really hoped you were right, but sorry... :(
I tested it with the following code:
Loop:
I2Creg = OLAT1 : I2Cbyte = $ff : gosub writeioextender: pause 20
I2Creg = IODIR1 : I2Cbyte = $ff : gosub writeioextender: pause 20

I2Creg = olat1 : gosub readioextender: pause 20
LCDOUT hex2 ioebyte, " "
I2Creg = gp1 : gosub readioextender: pause 20
LCDOUT hex2 ioebyte, cmd,home1
if onoffpressed = 1 then powerout = 0
goto Loop

I have a button on GP1.0 (pin 2) that is pressed or not, on the LCD one can only see 00 00, nothing else. I think this demonstrates that you can not write OLAT1 or read it, it also shows that a button press does not come through, or perhaps the GP1 is not initialized correctly. Tested also to write IODIR1 first but with the same result...:mad:

How should one initialize the chip with all pins as inputs?:confused:

Darrel Taylor
- 13th January 2009, 00:37
When you get all 0's, it usually means one of two things.

1) The SDA,SCL pins still have the analog mode enabled.

2) There aren't any pull-ups, or they pull-down.

If you have a voltmeter, check the voltage on the SDA,SCL pins.
They should be above 4V when no data is being transfered.

But I'm betting on analog mode.
<br>

keymuu
- 13th January 2009, 11:34
When you get all 0's, it usually means one of two things.

1) The SDA,SCL pins still have the analog mode enabled.

2) There aren't any pull-ups, or they pull-down.

If you have a voltmeter, check the voltage on the SDA,SCL pins.
They should be above 4V when no data is being transfered.

But I'm betting on analog mode.
<br>

As said in post 1#, both (SCL and SDA) are pulled up with 2 2k2 resistors, one for each. So when the lines are inactive you can measure 4,98V there, so that suggest also that the problem has to be somewhere else. I have also looked with an oscilloscope on the active signals, but being not so familiar with the inner live of the I2C protocol, can't tell if there would be something wrong, to me they look "normal". From the clock line I could calculate a clock frequency of something around 65kHz +/-20%. Is that OK?

Darrel Taylor
- 13th January 2009, 12:03
Then it must be 1).

Put this line at the top of your program.

ADCON1 = $F ; set all pins to digital
<br>

keymuu
- 13th January 2009, 13:43
Then it must be 1).

Put this line at the top of your program.

ADCON1 = $F ; set all pins to digital
<br>

Copy-Pasted that ADCON1... and that could have helped but unfortunately it did not help, nothing changed :(
The problem is somewhere else, but where?

Does that 65KHz sound correct? On what frequency should the clock run under those two I2C routines (I2CREAD I2CWRITE), where could one find any info on that. Tried also DEFINE I2C_SLOW, but again without success... What can I do? The expander is kind of alive because it does not throw you to the error label....:o

Darrel Taylor
- 13th January 2009, 13:58
No 65khz does not sound right, and ...
It doesn't throw you to the error label because it always reads 0's on the pin.

An ack from the device is a low pulse. As long as it sees a low when it expects an ack, it will never jump to the label.

OK, it's time for you to pony up some of the info you've been leaving out.

What are your CONFIGs?
What cyrstal are you using?
What DEFINE OSC are you using?
What pins are the SCL and SDA on?

And the rest of the code.
<br>

keymuu
- 13th January 2009, 17:31
No 65khz does not sound right, and ...
It doesn't throw you to the error label because it always reads 0's on the pin.

An ack from the device is a low pulse. As long as it sees a low when it expects an ack, it will never jump to the label.

OK, it's time for you to pony up some of the info you've been leaving out.

What are your CONFIGs?
What cyrstal are you using?
What DEFINE OSC are you using?
What pins are the SCL and SDA on?

And the rest of the code.
<br>

So, back to the basics. ;)
If not 65kHz is correct, then what should it be?

If I change the device address from $40 to something else for ex. $45 it will throw the program to the error label. That behavior sounds "healthy", doesn't it?

By CONFIGs you mean? See post #1
The crystal is 12MHz
DEFINE OSC 48
The SDA,SCL pins are connected correctly to pins 33,34 + 2x 2k2 pull-ups
Using also Microchip USB HID bootloader v2.2

I2Creg = OLAT1 : I2Cbyte = $ff : gosub writeioextender: pause 20
I2Creg = IODIR1 : I2Cbyte = $ff : gosub writeioextender: pause 20
The current test loop is here:
Loop:
gosub getbattery
if onoffpressed = 1 then powerout = 0

I2Creg = GP1 : gosub readioextender' to IOEbyte
pause 20

lcdout cmd,home3, dec battery/10, ".",dec battery//10,"V ", dec w/100," ", _
dec onoffpressed,cmd,home4, hex2 IOEbyte," "
goto Loop

Archangel
- 13th January 2009, 18:38
By CONFIGs you mean? See post #1


No look at this thread.
http://www.picbasic.co.uk/forum/showthread.php?t=543

Darrel Taylor
- 13th January 2009, 19:00
A quick test here on a 4550 at 48mhz, shows ~250khz clk.

For the configs, I didn't know you were using the USB bootloader. The configs will be whatever the bootloader was programmed with. You'd probably need to read back the chip to find out what configs are set.

I was hoping for more of the program, there's a lot of stuff missing, and you never know, 1 statement could change everything.

Since you are getting all 0's. Maybe you should just try to read the RB0 pin without I2C, as a test. There's a pull-up on it, if you only read a 0, then something's still not right.
<br>

keymuu
- 13th January 2009, 19:16
No look at this thread.
http://www.picbasic.co.uk/forum/showthread.php?t=543

Oops, sorry... the question was about all this here below :o

Here are the configurations settings:
DEFINE RESET_ORG 1000h ' Room for Microchip USB Bootloader
define ADC_BITS 10 ' 10 bit A/D
define ADC_CLOCK 3 ' 3=rc
define ADC_SAMPLEUS 50 ' sampling time in us
define I2C_HOLD 1 ' Can't read or write MCP23016 without i2c_hold = 1

'**************** Define LCD registers and bits *********************
DEFINE LCD_DREG PORTD ' Set LCD Data port
DEFINE LCD_DBIT 4 ' Set starting Data bit (0 or 4 if 4-bit bus)
DEFINE LCD_RSREG PORTC ' Set LCD Register Select port
DEFINE LCD_RSBIT 2 ' Set LCD Register Select bit
DEFINE LCD_EREG PORTC ' Set LCD Enable port
DEFINE LCD_EBIT 1 ' Set LCD Enable bit
DEFINE LCD_BITS 4 ' Set LCD bus size (4 or 8 bits)
DEFINE LCD_LINES 4 ' Set number of lines on LCD
DEFINE LCD_COMMANDUS 2000 ' Set command delay time in us, 2000 us
DEFINE LCD_DATAUS 50 ' Set data delay time in us, 50 us

sda var PORTB.0 ' i2c data pin 33
scl var PORTB.1 ' i2c clock pin 34

deviceIO con $40 ' MCP23016
GP0 con $00 ' port GP0 all in
GP1 con $01 ' port GP1 all in
OLAT0 con $02 ' Output latch register 0
OLAT1 con $03 ' 1
IPOL0 con $04 ' input polarity normal, not inverted
IPOL1 con $05 '
IODIR0 con $06 ' all out = 0
IODIR1 con $07 '
INTCAP1 con $09
INTCAPbyte var byte
IOCON1 con $0b '

cmd con $fe ' LCD command
clr con 1 ' LCD clear display
blink con $0f ' LCD blinkinking cursor on
home1 con $80 ' LCD move cursor to beginning of first line
home2 con $c0 ' LCD move cursor to beginning of second line
home3 con $94 ' debug lines
home4 con $d4 ' debug lines

keymuu
- 13th January 2009, 22:51
A quick test here on a 4550 at 48mhz, shows ~250khz clk.

For the configs, I didn't know you were using the USB bootloader. The configs will be whatever the bootloader was programmed with. You'd probably need to read back the chip to find out what configs are set.

I was hoping for more of the program, there's a lot of stuff missing, and you never know, 1 statement could change everything.

Since you are getting all 0's. Maybe you should just try to read the RB0 pin without I2C, as a test. There's a pull-up on it, if you only read a 0, then something's still not right.
<br>

I'm note anymore sure about anything...:o

When I use the following code to show things:

LCDOUT cmd,home3, dec battery/10, ".",dec battery//10,"V ",dec w/100," ", dec onoffpressed, _
cmd,home4, hex2 portb.0, " ", hex2 IOEbyte," ",cmd,home1

I can see on row 4 = 01 00, RB0=1 !! IOEbyte should show the button press on P1.0 but NO it does not come through :mad:
Took PIC of the PCB and read it with meProg and attached it here.

By the way, the ~250kHz that you got, are you absolutely sure that you communicated with the MPC23016 device? I'm just wondering because I have an impression from the datasheet of MCP23016 that it couldn't run that fast...:confused: Is my impression wrong?

Darrel Taylor
- 14th January 2009, 01:15
The configs look OK.

The PORTB.0 read looks OK.

In my test, I did not have an MCP23016 connected, I was just testing the clk frequency with I2CWRITE at 48mhz. The MCP23016 is a 400khz device, so 250khz should not be a problem.

The i2cdevice and I2Creg variables weren't shown. Are they both BYTEs?
I assume they are, just asking.

The only other possibility I can see is that the registers are configured as "Pairs", and all examples in the datasheet show reading or writing 2-bytes of data at a time. I don't see anything that says you can't read only 1-byte at a time, but then there's nothing that says you can either.

So this is my last best guess...
Try doing everything with 2 data bytes.

I2CWrite SDA,SCL,i2cdevice,I2Creg,[i2cbyte1, i2cbyte2],error
If the address is for GP0 ($0), then i2cbyte1 should have GP0 and i2cbyte2 should have GP1.

If the address is for GP1 ($1), then they will be reversed. GP1 in i2cbyte1 and GP0 in i2cbyte2.

Both write and reads would need 2-bytes.
And be sure to cycle power after changing it, in case it's still locked up.

hth

keymuu
- 14th January 2009, 09:21
The configs look OK.

The PORTB.0 read looks OK.

In my test, I did not have an MCP23016 connected, I was just testing the clk frequency with I2CWRITE at 48mhz. The MCP23016 is a 400khz device, so 250khz should not be a problem.

The i2cdevice and I2Creg variables weren't shown. Are they both BYTEs?
I assume they are, just asking.

The only other possibility I can see is that the registers are configured as "Pairs", and all examples in the datasheet show reading or writing 2-bytes of data at a time. I don't see anything that says you can't read only 1-byte at a time, but then there's nothing that says you can either.

So this is my last best guess...
Try doing everything with 2 data bytes.

I2CWrite SDA,SCL,i2cdevice,I2Creg,[i2cbyte1, i2cbyte2],error
If the address is for GP0 ($0), then i2cbyte1 should have GP0 and i2cbyte2 should have GP1.

If the address is for GP1 ($1), then they will be reversed. GP1 in i2cbyte1 and GP0 in i2cbyte2.

Both write and reads would need 2-bytes.
And be sure to cycle power after changing it, in case it's still locked up.

hth

Thank you Darrel, thank you indeed :)
You are a real and great PIC wizard! :cool:

You have made me extremely happy...

...have been struggling with this problem too long, I'm very happy that it is solved "so easily", meaning that the error here was almost close to a syntax error.

Who could have guessed in the first place that you need to read/write both data registers to get it working. Is this said somewhere in the data sheet? If so, it has not caught my eye.

This does slightly complicate the code for me, it would have been very handy if one could read the ports separately, but no big deal, not a real problem...

THANK YOU again for helping me out of my desperate situation with this "strange" MCP23016 behavior...:)

Darrel Taylor
- 14th January 2009, 11:27
WooHoo!

Pulled one out of my asparagus that time. :D

Thanks for sticking with it keymuu!

CYA round,