i2c struggle

edmunds

Senior Member
Dear all,

I have an I/O expander with 8 inputs connected to ground through switches. So, I believe the I/O expander is receiving logic-low, when the switch is on.
I'm using this code to read the state of the switches connected to I2C I/O expander PCF8574A for testing (never mind the PCA5554A in the variable name):

Symbol I2CSpeed = i2cfast_8
Symbol PCA9554A_1 = %01110000

hi2csetup i2cmaster, PCA9554A_1, I2CSpeed, i2cbyte
hi2cin (Lane1State)

debug Lane1State


I have been struggling to figure out what is going on for a while and now I seem to at least understand something. For some reason, Lane1State returns whatever is the value of PCA9554A_1 i.e. address of the I/O expander. Any ideas?


Regards,

Edmunds
 
Last edited:

edmunds

Senior Member
Sorry for not mentioning it - 40X2. Oki, well spotted. So my test device is slow and my real device is fast. Thank you very much, back to some more testing then.


Regards,

Edmunds
 

edmunds

Senior Member
So, now I have 255 in value for Lane1State, which means I2C error as I understand. Is there a way to debug the cause for the error further?


Thank you all for your time,

Edmunds
 

Technical

Technical Support
Staff member
When you read back the slave address it generally means the byte has never actually left the PICAXE to start with (ie you are reading the buffer which still contains the start address that should have been sent out already).

That, with 255, implies your i2c device is not responding at all. Check all A0, A1, A2 are tied to 0V and all other connections.

Have you tried writing to start with, that is generally easier to do as you can put an LED on one of the pins and see if that changes?
 

edmunds

Senior Member
hi2cout works with this piece of code with no problems:

Code:
Symbol I2CSpeed = i2cslow_8
Symbol PCA9554A_1 = %01110000    'Lane 1 DIP switch inputs
Symbol AllInputs = %11111111         'All port I/O pins set as inputs
Symbol AllOutputs = %00000000      'All port I/O set as outputs
Symbol Lane1State = b0

Main:
  hi2csetup i2cmaster, PCA9554A_1, I2CSpeed, i2cbyte 'test Lane1
'  hi2cin (Lane1State)
  for b1 = 0 to 3
    hi2cout (%00010000)
    pause 1000
    hi2cout (%00000000)
    pause 1000
  next b1
'  debug Lane1State

goto Main

Regards,

Edmunds
 

Technical

Technical Support
Staff member
Try a

hi2cin $FF,(b0)

Purely because the datasheet says all pins should be high before they are read - this command does write all pins high then read them.
 

edmunds

Senior Member
Try a

hi2cin $FF,(b0)

Purely because the datasheet says all pins should be high before they are read - this command does write all pins high then read them.
Thank you for your input. Also without this, I got it to work reliably now. I cannot say what exactly did I change when it started working, but somewhere between pull-up resistors for SDA and SCL lines and a decoupling capacitor and some software changes. I even got the interrupt to work almost reliably and I'm sure the not so reliable part comes from my code, not the hardware. Some more investigation to do there.

This is the code that worked:

Code:
Symbol I2CSpeed = i2cslow_8
Symbol PCA9554A_1 = %01110000    'Lane 1 DIP switch inputs
Symbol AllInputs = %11111111         'All port I/O pins set as inputs
Symbol AllOutputs = %00000000      'All port I/O set as outputs
Symbol Lane1State = b0
Symbol I2CInt = pinB.2    'I2C Interrupt, Active Low
Symbol ErrLED = A.6
Symbol StatLED = A.7

low StatLED
low ErrLED

main:

  if I2CInt = 0 then
    hi2csetup i2cmaster, PCA9554A_1, I2CSpeed, i2cbyte 'test Lane1
    hi2cin (Lane1State)
    high StatLED
    pause 5000
    low StatLED
  endif

#rem
  for b1 = 0 to 3
  hi2cout (%00001000)
  high A.6
  pause 1000
  hi2cout (%00000000)
  low A.6
  pause 1000
next b1

#endrem

 debug Lane1State

goto main
Now I'm trying the real stuff. I have reflow soldered my first board with surface mount PCA554AD. Of course, I'm not entirely sure I did not burn the chips while melting the solder paste. I do not have debug possibility, because somehow I did not think of it when designing the board. That is one thing I will fix for the next version :). But I have two LEDS, which is a binary display if you want. So, I know for a fact I'm back to square one with this - I'm getting the variables loaded with the addresses of the chips. Will continue the fight and will get back when I win. Hints are welcome :).


Thank you once again,

Edmunds
 

edmunds

Senior Member
I must admit, I am stuck again. I have probably spent around 8 hours on trying to figure out what part of this does not work. I will not post the schematics, since this is big and does not reflect exactly what I'm testing, but try to describe what I'm doing. The test setup is PICAXE-40X2 connected to PCA9554AD with SDA (C.4), SCL (C.3) and INT (B.2) lines all pulled high with 10k resistors. There is a red 3mm LED connected to pin #5 (IO1) through a 330Ohm resistor. The other leg of the LED goes to GND. I'm trying to get the LED to blink.

I have tested the connection of SDA, SCL and INT lines. I have tested there is power applied to both chips. I have tested there is a +4.6V on each of the IO pins of the PCA9554AD - don't really understand that, but it is there ;). Electrically, I cannot find anything wrong with the setup.

Here is the code striped to the bear minimum:
Code:
Symbol I2CSpeed = i2cfast_8
Symbol PCA9554A_5 = %01111110    'Address of the chip, A2, A1 and A0 all tied to VDD
Symbol ErrLED = A.6

'Command bytes for PCA9554A
Symbol InputReg = %10000000        'Value 0
Symbol OutputReg = %01000000     'Value 1
Symbol PolarInvReg = %00100000    'Value 2
Symbol ConfigReg = %00010000      'Value 3

Symbol AllInputs = %11111111          'All port I/O pins set as inputs
Symbol AllOutputs = %00000000        'All port I/O set as outputs

low ErrLED

main:

  hi2csetup i2cmaster, PCA9554A_5, I2CSpeed, i2cbyte
  pause 20
  hi2cout (ConfigReg,AllOutputs)
  pause 20
  for b1 = 0 to 3
    hi2cout (%01000010)
    high ErrLED
    pause 1000
    hi2cout (%00000000)
    low ErrLED
    pause 1000
  next b1

goto main
I do get my ErrLED blinking, of course, but no sign of life on the 3mm LED connected to PCA9554AD :(. Any suggestions on how to debug this further?


Thank you for your time looking into this,

Edmunds
 
Last edited:

geoff07

Senior Member
In this situation I would be putting a scope on the lines to see if there was data flowing. If you can take and print a snapshot of the trace you can work out exactly what i2c is doing. If you don't have a scope, consider the DPScope that is very inexpensive addon unit that makes your computer into a scope decent enough for picaxe work.
 

edmunds

Senior Member
In this situation I would be putting a scope on the lines to see if there was data flowing. If you can take and print a snapshot of the trace you can work out exactly what i2c is doing. If you don't have a scope, consider the DPScope that is very inexpensive addon unit that makes your computer into a scope decent enough for picaxe work.
I guess so. Ordered the one from picaxe shop to start with. This will help a lot with IR signals as well that are an important of my application.
Will be back with some graphs maybe :).


Thank you for your input,

Edmunds
 

hippy

Technical Support
Staff member
hi2cout (%01000010)

I am not familiar with the PCA9554A but the data sheet does seem to indicate that the first byte when writing should be a command followed by data and you are only sending one byte. To set and clear the IO it would seem to involve writing to the output register (register 1), a two byte command similar to when configuring, so I would have expected something like ...

HI2cOut 1, ( %00000010 ) ; Set IO1
HI2cOut 1, ( %00000000 ) ; Clear all

or

HI2cOut ( 1, %00000010 ) ; Set IO1
HI2cOut ( 1, %00000000 ) ; Clear all

Also, when debugging I2C, it's best to start with a slow speed rather than fast.
 

edmunds

Senior Member
hi2cout (%01000010)

I am not familiar with the PCA9554A but the data sheet does seem to indicate that the first byte when writing should be a command followed by data and you are only sending one byte. To set and clear the IO it would seem to involve writing to the output register (register 1), a two byte command similar to when configuring, so I would have expected something like ...

HI2cOut 1, ( %00000010 ) ; Set IO1
HI2cOut 1, ( %00000000 ) ; Clear all

or

HI2cOut ( 1, %00000010 ) ; Set IO1
HI2cOut ( 1, %00000000 ) ; Clear all

Also, when debugging I2C, it's best to start with a slow speed rather than fast.

Thank you for your input. I tried that apart from the fact I think I should be writing "%01000000", not "1" as a command byte. It did not work. I now tried the "1" as well just out of curiosity and still nothing. I do find the data sheet confusing if, for an example, compared to the data sheet of MCP23017. The spec for PCA9555 16 port expander is even worse. However, some of the advantages of PCA9554A made me to take the risk and try to figure it out. Now, redesigning everything to MCP23017 is hardly an option anymore.

As I wrote above, picaxe shop confirmed the shipment of a usb scope and I'm working on a PCA9554A breakout board to test it isolated from the rest of the circuit. Hope to be able to write a tutorial for PICAXE to NXP I2C devices after all :).

Regards,

Edmunds
 

hippy

Technical Support
Staff member
Data sheet is here -

www.nxp.com/documents/data_sheet/PCA9554_9554A.pdf

It appears writing to the "output register" won't work; that's read only. Outputs are set by writing to the configuration registers using command 3.

It's a bit of a weird chip; each pin can either be an input with a weak pull-up or, as an output, pull down to 0V.

You will need a LED+R between +V and the IO pin to make it work.
 

edmunds

Senior Member
Data sheet is here -

www.nxp.com/documents/data_sheet/PCA9554_9554A.pdf

It appears writing to the "output register" won't work; that's read only. Outputs are set by writing to the configuration registers using command 3.

It's a bit of a weird chip; each pin can either be an input with a weak pull-up or, as an output, pull down to 0V.

You will need a LED+R between +V and the IO pin to make it work.
That is the one place in the data sheet I was totally confused about. So it is kind of Active Low. That is fine for my application, even perfect, but my test setup, of course is wrong. The other place is the one about open-drain interrupt. I hope what that paragraph is saying is basically the chip has an active-low interrupt. So when the pin goes low, some inputs have changed.

I gather, I should be sending address, command byte and data (status of the pins). As I understand, address is sent for me by hi2cout command. This leaves command byte %01000000 and the actual data for me. Since I have not been able to get this to work, I have to, there is something more (or less) happening behind the scenes and thus as have been suggested, the only way to really work it out is to see what is sent and what is coming back with a scope.

Thank you for your valuable input - I will try some options now with the correct diode connections.

Regards,

Edmunds
 

hippy

Technical Support
Staff member
I gather, I should be sending address, command byte and data (status of the pins). As I understand, address is sent for me by hi2cout command. This leaves command byte %01000000 and the actual data for me.
That would be correct but I am not sure where you get %01000000 as the command byte value.

I was going to suggest looking at Figures 11 and 12 in the datasheet but Figure 11 doesn't seem right given that section 6.1.3 suggests the Output register is Read only, though the "R" there is inconsistent with "read only" shown in section 6.1.2 which would be correct for an input port.

I can only guess that "R" should show "R/W" ( as per 6.1.4 and 6.1.5 ) which would make writing to the Output register correct as per Figure 11.

Zooming in on Figure 8 shows a blob which looks like a push-pull configuration for the IO pin rather than a pull-down only I had interpreted it as before - That's a fine example as to why one should never cross wires at a blob in a circuit diagram, only have T joins at blobs.

Not sure if it is actually a push-pull but if you work through the logic of Figure 8 and its output control you might be able to figure it out.

As best I can tell you need to write to register 3 ( address %00000011 as per Fig 12 ) to set which are inputs and which are outputs. Then write to register 1 ( address %00000001 as per Fig 11 ) to set the output levels high or low.
 

hippy

Technical Support
Staff member
Texas Instruments has an equivalent datasheet which clears things up ...

http://www.ti.com/lit/ds/symlink/pca9554.pdf

It is a push-pull output - Section 8.2.2

And the command / adress bytes seem to be as I suspected - Section 8.3.2.2

Also see Section 9.1.1.1 which suggests LED+R between +V and IO for minimising current draw when the LED is off, but that doesn't seem to preclude LED+R between IO pin and 0V. It can source or sink 50mA per IO but has a chip source limit of 160mA with a higher sink limit of 250mA so sinking might be better.
 

edmunds

Senior Member
That would be correct but I am not sure where you get %01000000 as the command byte value.
My prior investigation of many hours behind the screen led me to thinking I need to supply binary number, rather than decimal. I do not remember where I got it from and I don't mind to be proven wrong. Up until now it has not made any difference.

Zooming in on Figure 8 shows a blob which looks like a push-pull configuration for the IO pin rather than a pull-down only I had interpreted it as before - That's a fine example as to why one should never cross wires at a blob in a circuit diagram, only have T joins at blobs.
Would that mean it should work as active high?

I never looked into Fig 8 as I considered it kind of rocket science. By now it might not be that bad :). Will try to make heads and tails of it.

As best I can tell you need to write to register 3 ( address %00000011 as per Fig 12 ) to set which are inputs and which are outputs. Then write to register 1 ( address %00000001 as per Fig 11 ) to set the output levels high or low.
Then I'm not that far off. That is exactly what I came up with. The only question remaining is how exactly :).


Thank you for your input,

Edmunds
 

hippy

Technical Support
Staff member
I think you can use an active high signal. Not sure if the chip inverts the output but wouldn't have thought so. I would expect this to give a short flash and a longer off for a LED+R between IO1 and 0V ...

Code:
HI2cSetup I2CMASTER, %01111110, I2CSLOW, I2CBYTE
HI2cOut   3, ( %11111101 )
Do
  HI2cOut 1, ( %00000010 ) : Pause  500
  HI2cOut 1, ( %00000000 ) : Pause 1500 
Loop
 

edmunds

Senior Member
Code:
10   hi2csetup i2cmaster, %01110000, i2cslow, i2cbyte 'address is now A2, A1, A0 to GND
20   pause 20
30   hi2cout 3, (%00000000)
40   pause 20
50   for b1 = 0 to 3
60     high A.6                             'LED connected to A.6
70     hi2cout 1, (%00000001)      'LED is now connected to IO0
80     pause 1000
90     low A.6
100    hi2cout 1, (%00000000)
110    pause 1000
120  next b1

goto main
(I admit your code is neater)

This now produces some kind of short. I get one second long blink from ErrLED and then IO7 LED goes on and stays on. A.6 never comes back and I can only reprogram picaxe through a hard reset. If I disconnect VCC from the LED, the chip goes back to "normal". I guess it goes mad on line 100. This is how far I am. Experimenting more.


Regards,

Edmunds
 
Last edited:

hippy

Technical Support
Staff member
You are setting all your pins to outputs so that could cause a problem if you have inputs wired to those outputs.

%00000001 sets IO0; you need %10000000 to set IO7.

You have also changed the Device Address in the HI2CSETUP command. Has that changed on the hardware ? Does the address actually match the setting being used ?

Is IO7 LED wired between +V and the IO or between IO and 0V ? Is the LED+R actually to the correct pin ?

It becomes extremely difficult when the goal posts keep moving so perhaps post a circuit diagram and/or some pictures of what you actually have.
 

edmunds

Senior Member
It is IO0 - typo.
The address has changed and it has been changed on the hardware as well. I took a spare chip and soldered wires directly to it and have them plugged into AXE091.
The LED is between IO0 and +V.
No other IO pins are connected to anything.
I understand it can be difficult. I will post schematics when I draw one. Will take a little time.


Thank you for your input,

Edmunds
 

edmunds

Senior Member
I am reliably reading now with this code:

Code:
  hi2csetup i2cmaster, %01110000, i2cslow, i2cbyte
  pause 10
  hi2cout 3, (%11111111)
  pause 10
  hi2cin 0, (b0)
  debug b0

Regards,

Edmunds
 

hippy

Technical Support
Staff member
Seems we are making progress. This should flash your LED ...

Code:
HI2cSetup I2CMASTER, %01110000, I2CSLOW, I2CBYTE
HI2cOut   3, ( %11111110 )
Do
  HI2cOut 1, ( %00000001 ) : Pause  500
  HI2cOut 1, ( %00000000 ) : Pause 1500 
Loop
 

edmunds

Senior Member
Nope. Same as before. "Hangs" on the second HI2COut with all zeroes. The LED remains on and hard reset or disconnecting IO0 from the LED is needed to download new program.


Thank you for your time,

Edmunds
 

hippy

Technical Support
Staff member
I am not sure what to suggest other than run it without the LED+R connected and see if that works or not.

My gut feeling is there's a hardware issue, perhaps a short somewhere, such that when the output goes low everything goes pear shaped.

It should be possible to keep the pins as input, update the output register, and read what was written to the output register. Might be worth trying this and seeing what gets sent to the Terminal ...

Code:
HI2cSetup I2CMASTER, %01110000, I2CSLOW, I2CBYTE
HI2cOut   3, ( %11111111 )
Do
  HI2cOut 1, ( b0 )
  HI2cIn  1, ( b1 )
  If b0 = b1 Then
    SerTxd( "-" )
  Else
    SerTxd( "FAIL" )
  End If
  b0 = b0 + 1 
Loop
 

edmunds

Senior Member
It is all working by now. There was a hardware error. Not to go into nitty gritty thing - just make sure you have a resistor with your LED that is connected to PCA9554.

Here is a cleaned up version of the code that works both ways - reading and writing to PCA9554 or PCA9554A. Should work for PCA9555 as well if you add which port you want to read or write to as the first byte in the parenthesis of hi2cin or hi2cout commands.

Code:
#picaxe40x2

Symbol I2CSpeed = i2cfast_8           'start testing with I2cslow_x where x is your running freq
Symbol I2CInt = pinB.2                     'I2C Interrupt, Active Low
Symbol ErrLED = A.6                         'LED 1, connected to A.6 for debug purposes
Symbol StatLED = A.7                       'LED 2, connected to A.7 for debug purposes

'I2C I/O expander addresses
Symbol PCA9554A = %01110010      'Device address, A2 and A1 to GND, A0 to VDD

'Command bytes for PCA9554A
Symbol InputReg = 0                         'Input Port Register as per datasheet section 6.1.1
Symbol OutputReg = 1                      'Output Port Register as per datasheet section 6.1.1
Symbol PolarInvReg = 2                     'Polarity Inversion Register as per datasheet section 6.1.1
Symbol ConfigReg = 3                       'Configuration Register as per datasheet section 6.1.1

'I2C I/O expander masks
Symbol AllInputs = %11111111         'All port I/O pins set as inputs
Symbol AllOutputs = %00000000      'All port I/O set as outputs

main:
low StatLED                                        'initialise to OFF
low ErrLED                                          'initialise to OFF

#rem
'Add this code to test writing to PCA9554
  hi2csetup i2cmaster, PCA9554A, I2CSpeed, i2cbyte   'set up I2C and address of the device
  pause 10                                                                      'experiment with minimum that works, 0 is possible
  hi2cout ConfigReg, (AllOutputs)                                   'set all pins to outputs in Configuration Register
  pause 10                                                                      'experiment with minimum that works, 0 is possible
 do
    high ErrLED                                                                'LED ON in paralell to know something is happening
    hi2cout OutputReg, (%00000001)                              'take IO0 pin high
    pause 1000
    low ErrLED
    hi2cout OutputReg, (%00000000)                               'take IO0 pin low
    pause 1000
 loop                                                                               'continue blinking...
#endrem
  

'Add this code to test reading PCA9554
  hi2csetup i2cmaster, PCA9554A, I2CSpeed, i2cbyte     'set up I2C and address of the device
  pause 10                                                                      'experiment with minimum that works, 0 is possible
  hi2cout ConfigReg, (AllInputs)                                      'set all pins to inputs in Configuration Register
  pause 10                                                                      'experiment with minimum that works, 0 is possible
  hi2cin InputReg, (b0)                                                    'read the state of PCA9554 pins into b0
  select case b0
  case %11111110                                                          'in case IO0 low and the rest high ...
  	high ErrLED                                                          '... take one of the LEDs high
  	pause 5000
  	low ErrLED
  	pause 1000
  case %11111101                                                          'in case IO1 low and the rest high ...
  	high StatLED                                                         '... take the other LED high
  	pause 5000
  	low StatLED
  	pause 1000
  endselect
    
goto main
 
Top