Reply by rtstofer May 2, 20062006-05-02
--- In l..., "Sutton Mehaffey" wrote:
>
> Richard,
>
> I have reviewed some of the code trying to make sure mine 'matches' as
> far as registers go. But since most of the sample code says 'From
> device to device, the I2C communication protocol may vary', unless you
> are communicating with the same device as the sample code, it probably
> won't work. Therefore, I was probing around with my scope, since I
> don't currently have a logic analyzer. Planning to get one. $$$$. I
> just noticed that my SCL line was always low once the STA command was
> issued. Trying to figure that out. I thought by issuing a STA
> command and waiting for ACK over and over I would see the SCL line move.
>
> Sutton
>

When you send the Start condition, the next thing that should happen
is the ending of the Start condition and the beginning of shifting the
slave address.

But, if you are trying to do this without an interrupt driven state
machine, good luck! Whether interrupt driven or not, a substantial
state machine is required.

It would also seem that your clock line should idle high. If it isn't
high before you send the Start condition, you can't really send a
Start. In fact, both lines should idle high. They are open collector
pins so you need to have pull-up resistors. You can get the proper
value from the I2C spec but 2.2k will work in most cases.

There is only one I2C protocol (at some level). The issue about the
device requirements comes up when talking to EEPROMs and such where
you first have to send a memory address with the first WRITE sequence
and then follow that up with a new transaction doing a data read or
write. In some cases the read/write doesn't have to be a separate
transaction but it almost always can be separate. The memory address
was latched so that subsequent transactions begin at that address.

I believe I copied the Philips code and converted it to GNU C when I
used it on my LPC2106. I was able to get it communicating with a
couple of I2C devices with darn little effort.

The setup code:

// initialize PINSEL0 & PINSEL1
PINSEL0 = 0x00;
PINSEL1 = 0x00;

// set up I2C

if ((IOPIN & 0x0C) == 0x0C) // SCL & SDA should be high, they will
be pulled
{ // down with jumpers when there is no I2C device
NoI2CDevice = 0;
VICVectCntl1 = 0x00000029; // select a priority spot for a given
interrupt
VICVectAddr1 = (unsigned long) I2CISR; // pass the address of the ISR
VICIntEnable = 0x00000200; // enable interrupt
PINSEL0 |= 0x50; // switch GPIO to I2C pins
I2C_I2CONCLR = 0x6C; // clear all I2C settings
I2C_I2CONSET = 0x40; // enable I2C interface
I2C_I2SCLH = 300; // set bit rate to 58.9824 MHz / 100000
I2C_I2SCLL = 300; // or about 600 -- 300 counts high, 300 low
}
else
NoI2CDevice = 1;

This is based on a 14+MHz crystal and running at 58 MHz and VPBDIV 1. I detect the presence of I2C devices by the simple expedient that
I will pull the SCLK and SDA lines to ground BEFORE they are set for
I2C when the I2C bus is not in use (other projects).

The I2C driver looks like this:

uint8_t NoI2CDevice = 1; // assume no device,
//revised during Initialize()

volatile uint8_t *I2CData;
volatile int I2CCounter;
volatile uint8_t I2CAddress;
volatile uint8_t lock;

void I2CMasterSend(uint8_t I2CAddr, int count, uint8_t *message)
{
if (NoI2CDevice)
return;

while (lock == 1) // wait for interrupt to signal end of I2C activity
;
lock = 1; // set I2C bus as active
I2CAddress = I2CAddr;
if (count > 0)
{
I2CData = message;
}
I2CCounter = count;
I2C_I2CONSET = 0x20; // send start condition;

while (lock == 1)
;
}

void I2CMasterReceive(uint8_t I2CAddr, uint8_t Address, int count,
volatile uint8_t *message)
{
// uint8_t packet[1];

if (NoI2CDevice)
return;

while (lock == 1) // wait for interrupt to signal end of I2C activity
; // from any previous operation
lock = 1;

I2CAddress = I2CAddr | 0x01; // set read bit
I2CCounter = count;
I2CData = message;
I2C_I2CONSET = 0x20; // set start condition

while (lock == 1) // wait for interrupt to signal end of I2C activity
; // before returning to caller
}

void I2CISR (void)
{
switch (I2C_I2STAT)
{
case (0x00): // bus error
I2C_I2CONSET = 0x14;
lock = 0;
break;
case (0x08): // start bit sent
case (0x10): // repeated start sent
I2C_I2CONCLR = 0x20; // clear start bit
I2C_I2DAT = I2CAddress; // send address and W
break;
case (0x18): // slave addr+W, ACK
case (0x28): // data transmitted ACK
if (I2CCounter > 0) // any data left?
{
I2C_I2DAT = *I2CData; // send data
I2CData++;
I2CCounter--;
}
else
{
I2C_I2CONSET = 0x10; // send stop bit
lock = 0;
}
break;
case (0x20): // slave addr NOT ACK
case (0x30): // data sent NOT ACK
I2C_I2CONSET = 0x14; // send stop bit
lock = 0;
break;
case (0x38): // arbitration lost,restart
I2C_I2CONSET = 0x24; // set STA and AA bits
break;
case (0x40): // slave addr + R, ACK
I2C_I2CONSET = 0x04; // enable ACK for byte
break;
case (0x48): // slave addr + R, NOT ACK
I2C_I2CONSET = 0x14; // set STO and AA bits
lock = 0;
break;
case (0x50): // data received ACK
*I2CData = I2C_I2DAT;
I2CData++;
if (--I2CCounter > 1)
{
I2C_I2CONSET = 0x04;
}
else // next byte is last byte
{
I2C_I2CONCLR = 0x04; // set AA condition
}
break;
case (0x58): // data received NOT ACK
*I2CData = I2C_I2DAT; // last byte
I2C_I2CONSET = 0x14; // set STO and AA bits
lock = 0;
break;
default:
break;
}
I2C_I2CONCLR = 0x08; // clear I2C int flag
VICVectAddr = 0xFF; // clear interrupt in VIC
}
I am connected to a Philips PCF8574 IO Expander and the code
for initialization and read/write is:

void InitIOPorts(void)
{
int i;
unsigned char msg = 0xFF;

for (i = 0; i < MAXINP; i++)
{
I2CMasterSend(inputports[i].address, 1, &msg);
}

for (i = 0; i < MAXOUT; i++)
{

I2CMasterSend(outputports[i].address, 1, &msg);
}
}

void ReadInputPorts(void)
{
int i;
unsigned char buf[2];

for (i = 0; i < MAXINP; i++)
{
I2CMasterReceive(inputports[i].address, 0, 1, buf);
inputports[i].mirror = buf[0];
}
}

void WriteOutputPorts(void)
{
int i;

for (i = 0; i < MAXOUT; i++)
{
I2CMasterSend(outputports[i].address, 1, &outputports[i].mirror);
}
}

I have an array of input and output ports with various addresses and
mirror values. In my application, I read all the input ports at the
beginning of a process loop and write all the output ports at the end
of the loop.

It is probably a good idea to post i2c.h so you can see the definition
of the interrupt handler and the two IO routines:

#ifndef _I2C_H_
#define _I2C_H_

#include

void I2CMasterSend(uint8_t I2CAddr, int count, uint8_t *message);
void I2CMasterReceive(uint8_t I2CAddr, uint8_t Address, int count,
volatile uint8_t *message);
void I2CISR (void) __attribute__ ((interrupt));

#endif //_I2C_H_

Now, all of this is KNOWN to work. I have it running on a simple Prop
Controller which is similar in concept to a PLC (Programmable Logic
Controller). The Prop Controller is based on PLCC, a popular
Halloween Haunt Controller:
http://www.thebells.net/Halloween/PLCC/PLCC.html

PLCC uses a PC and the parallel port, mine uses an LPC2106, a CF disk
drive and multiple IO expanders like the Philips PCF8574.

Richard

An Engineer's Guide to the LPC2100 Series

Reply by Sutton Mehaffey May 2, 20062006-05-02
Richard,

I have reviewed some of the code trying to make sure mine 'matches' as
far as registers go. But since most of the sample code says 'From
device to device, the I2C communication protocol may vary', unless you
are communicating with the same device as the sample code, it probably
won't work. Therefore, I was probing around with my scope, since I
don't currently have a logic analyzer. Planning to get one. $$$$. I
just noticed that my SCL line was always low once the STA command was
issued. Trying to figure that out. I thought by issuing a STA
command and waiting for ACK over and over I would see the SCL line move.

Sutton

--- In l..., "rtstofer" wrote:
>
> --- In l..., "Sutton Mehaffey" wrote:
> >
> > I've still having issues with getting I2C to work. I'm not even
> > seeing SCL on my scope. I'm using a Embedded Artists prototype board
> > with the 2148 board. If I have a hard loop with I2C0CONSET = 0x20 and
> > a wait for the SI flag, shouldn't I see SCL on the scope? Or can you
> > not issue a START over and over? Assuming, of course, that my initial
> > setup of the I2C is correct. Thanks.
> >
> > Sutton
> > philips_apps has posted demo code for two different development
> platforms but the code itself should work anywhere. See the Files
> section here and scan down looking for 'philips_apps' or the file
> LPC214x_LPC213x Sample code for Keil.zip - a little more than half way
> down.
>
> In my view, and it isn't universally shared, the chances of getting
> I2C to work without a logic analyzer are quite slim. It would be
> better to start with code that is known to work even if compiler
> specific code had to be changed.
>
> Richard
>
Reply by rtstofer May 2, 20062006-05-02
--- In l..., "Sutton Mehaffey" wrote:
>
> I've still having issues with getting I2C to work. I'm not even
> seeing SCL on my scope. I'm using a Embedded Artists prototype board
> with the 2148 board. If I have a hard loop with I2C0CONSET = 0x20 and
> a wait for the SI flag, shouldn't I see SCL on the scope? Or can you
> not issue a START over and over? Assuming, of course, that my initial
> setup of the I2C is correct. Thanks.
>
> Sutton
>

philips_apps has posted demo code for two different development
platforms but the code itself should work anywhere. See the Files
section here and scan down looking for 'philips_apps' or the file
LPC214x_LPC213x Sample code for Keil.zip - a little more than half way
down.

In my view, and it isn't universally shared, the chances of getting
I2C to work without a logic analyzer are quite slim. It would be
better to start with code that is known to work even if compiler
specific code had to be changed.

Richard
Reply by Sutton Mehaffey May 2, 20062006-05-02
I've still having issues with getting I2C to work. I'm not even
seeing SCL on my scope. I'm using a Embedded Artists prototype board
with the 2148 board. If I have a hard loop with I2C0CONSET = 0x20 and
a wait for the SI flag, shouldn't I see SCL on the scope? Or can you
not issue a START over and over? Assuming, of course, that my initial
setup of the I2C is correct. Thanks.

Sutton
Reply by Danish Ali April 22, 20062006-04-22
Hi Sutton,
Not wishing to appear rude, how about if you genuinely cut-and-paste
your code and then we can see if anything looks wrong.
IMHO you can get enough information to know the system is good from
a write - that's where you'll get acknowledges.
And the master should not assert acknowledge when writing.

I have never needed a logic analyser to debug I2C. You should be
able to see enough by doing
*ack*
on an oscilloscope - repeatedly if it's not a storage 'scope.

Oh and one more thing. I don't know what development toolset you're
using but be careful about using the Crossworks debugger
to look at hardware peripherals (my experience was with the VIC but the
same might apply to I2C hardware). I think the Crossworks debugger
uses memory accesses to read the state of the peripherals and so
if you have a given peripheral viewed, that peripheral will think the
processor has read all the registers, and this might change its state.
The Keil debugger allows you to view the peripheral registers without
touching them (how? perhaps they are on a scan chain?).

Help us to help you,
- Danish
--- In l..., "Sutton Mehaffey" wrote:
>
> I had a typo in my posted text. I actually was using 'A0' for the
> write and a 'A1' for a read. I went back and double checked to be
> absolutely sure. I guess the logic analyzer is the way to go, because
> I still can't spot my problem.
>
> Sutton
>
> --- In l..., "Danish Ali" wrote:
> >
> > Watch out! You've used 0xA1 as your address. That implies
> > a READ. It should be 0xA0 for a write.
> >
> > Hope this helps,
> > Danish
Reply by Sutton Mehaffey April 21, 20062006-04-21
I had a typo in my posted text. I actually was using 'A0' for the
write and a 'A1' for a read. I went back and double checked to be
absolutely sure. I guess the logic analyzer is the way to go, because
I still can't spot my problem.

Sutton

--- In l..., "Danish Ali" wrote:
>
> Watch out! You've used 0xA1 as your address. That implies
> a READ. It should be 0xA0 for a write.
>
> Hope this helps,
> Danish
>
> --- In l..., "Sutton Mehaffey" wrote:
> >
> > I'm back on my I2C problems since I was pulled off the project for a
> > month or so. I'm still having some problems. The problem I thought
> > was I was trying to address a 5V RAM instead of a 3.3V RAM. I have
> > now a couple of ATMEL 256K serial RAM - 2.7V to 5V. I am still having
> > trouble with understanding the status of the I2C bus. It appears I am
> > getting the correct status, but even when the RAM is disconnected.
> >
> > I2C0CONSET = 0x20; // Issue Start
> > (status responds with 0x08)
> > I2C0DAT = 0xA1; // Issue a write command to RAM
> ^^^^ No!
> > I2C0CONSET = 0x04; // Set AA bit for ACK
> > I2C0CONCLR = 0x28; // Clear STA and SI flags
> > (status responds with 0x18)
>
Reply by Danish Ali April 21, 20062006-04-21
Watch out! You've used 0xA1 as your address. That implies
a READ. It should be 0xA0 for a write.

Hope this helps,
Danish

--- In l..., "Sutton Mehaffey" wrote:
>
> I'm back on my I2C problems since I was pulled off the project for a
> month or so. I'm still having some problems. The problem I thought
> was I was trying to address a 5V RAM instead of a 3.3V RAM. I have
> now a couple of ATMEL 256K serial RAM - 2.7V to 5V. I am still having
> trouble with understanding the status of the I2C bus. It appears I am
> getting the correct status, but even when the RAM is disconnected.
>
> I2C0CONSET = 0x20; // Issue Start
> (status responds with 0x08)
> I2C0DAT = 0xA1; // Issue a write command to RAM
^^^^ No!
> I2C0CONSET = 0x04; // Set AA bit for ACK
> I2C0CONCLR = 0x28; // Clear STA and SI flags
> (status responds with 0x18)
Reply by Sutton Mehaffey April 20, 20062006-04-20
Richard,

Thanks for your reply. I am not using an interrupt, but am keying on
the I2C0STAT register, and branching into my state code whenever a
valid status is in the register. Maybe I do need to use a analyzer,
but I will review some of the other code as well.

For my understanding, is the ACK from any Slave chip recognized by the
changing of the status in the I2C0STAT? Maybe I'm not waiting long
enough for the status to change/refresh before assuming the previous
command is complete.

Sutton

--- In l..., "rtstofer" wrote:
>
> --- In l..., "Sutton Mehaffey" wrote:
> >
> > I'm back on my I2C problems since I was pulled off the project for a
> > month or so. I'm still having some problems. The problem I thought
> > was I was trying to address a 5V RAM instead of a 3.3V RAM. I have
> > now a couple of ATMEL 256K serial RAM - 2.7V to 5V. I am still having
> > trouble with understanding the status of the I2C bus. It appears I am
> > getting the correct status, but even when the RAM is disconnected. I
> > 'assume' from the status that those come from the I2C bus (LPC) and
> > not the RAM chip itself. How do I know that the RAM has sent back an
> > ACK? Does the new status on the bus indicate that it has? Here is
> > how my code looks based on writing a 0x77 and reading back from an
> > arbitrary address (0x01FFF):
> >
> > I2C0CONSET = 0x20; // Issue Start
> > (status responds with 0x08)
> > I2C0DAT = 0xA1; // Issue a write command to RAM
> > I2C0CONSET = 0x04; // Set AA bit for ACK
> > I2C0CONCLR = 0x28; // Clear STA and SI flags
> > (status responds with 0x18)
> > I2C0DAT = 0x1F; // MSB
> > I2C0CONSET = 0x04; // Set AA bit for ACK
> > I2C0CONCLR = 0x08; // Clear SI flag
> > (status responds with 0x28)
> > I2C0DAT = 0xFF; // LSB
> > I2C0CONSET = 0x04; // Set AA bit for ACK
> > I2C0CONCLR = 0x08; // Clear SI flag
> > (status responds with 0x28)
> > I2C0DAT = 0x77; // Write Byte of 0x77
> > I2C0CONSET = 0x04; // Set AA bit for ACK
> > I2C0CONCLR = 0x08; // Clear SI flag
> > (status responds with 0x28)
> > I2C0CONSET = 0x14; // Issue a STOP
> > I2C0CONCLR = 0x08; // Clear SI flag
> >
> > If I follow this with a Read Sequence, I get correct status, but an
> > 'A1', which incidentally coincides with the Chip ID + WRI. Again, the
> > 'A1' is read whether or not I have the RAM actually connected. Any
> > ideas on what I am missing? I am using an Embedded Artists prototype
> > board for this code. It is also not clear to me how I know if each
> > step listed above is actually accepted by the RAM chip. Thanks.
> >
> > Sutton
> > That's why I2C code is usually in the form of an interrupt drive state
> machine.
>
> philips_aps has posted sample code for the LPC214x but the ideas are
> common for all. Look in the Files section here.
>
> The bad news about I2C is that if at first it doesn't work, nothing
> short of a logic analyzer will capture enough data vs time to be able
> to troubleshoot it. Well, maybe a storage scope but only if you can
> come up with a trigger just before or just at the start condition.
>
> The good news is that the Philips code is likely to work. It is very
> similar to code I used for a project with the LPC2106.
>
> Richard
>
Reply by rtstofer April 20, 20062006-04-20
--- In l..., "Sutton Mehaffey" wrote:
>
> I'm back on my I2C problems since I was pulled off the project for a
> month or so. I'm still having some problems. The problem I thought
> was I was trying to address a 5V RAM instead of a 3.3V RAM. I have
> now a couple of ATMEL 256K serial RAM - 2.7V to 5V. I am still having
> trouble with understanding the status of the I2C bus. It appears I am
> getting the correct status, but even when the RAM is disconnected. I
> 'assume' from the status that those come from the I2C bus (LPC) and
> not the RAM chip itself. How do I know that the RAM has sent back an
> ACK? Does the new status on the bus indicate that it has? Here is
> how my code looks based on writing a 0x77 and reading back from an
> arbitrary address (0x01FFF):
>
> I2C0CONSET = 0x20; // Issue Start
> (status responds with 0x08)
> I2C0DAT = 0xA1; // Issue a write command to RAM
> I2C0CONSET = 0x04; // Set AA bit for ACK
> I2C0CONCLR = 0x28; // Clear STA and SI flags
> (status responds with 0x18)
> I2C0DAT = 0x1F; // MSB
> I2C0CONSET = 0x04; // Set AA bit for ACK
> I2C0CONCLR = 0x08; // Clear SI flag
> (status responds with 0x28)
> I2C0DAT = 0xFF; // LSB
> I2C0CONSET = 0x04; // Set AA bit for ACK
> I2C0CONCLR = 0x08; // Clear SI flag
> (status responds with 0x28)
> I2C0DAT = 0x77; // Write Byte of 0x77
> I2C0CONSET = 0x04; // Set AA bit for ACK
> I2C0CONCLR = 0x08; // Clear SI flag
> (status responds with 0x28)
> I2C0CONSET = 0x14; // Issue a STOP
> I2C0CONCLR = 0x08; // Clear SI flag
>
> If I follow this with a Read Sequence, I get correct status, but an
> 'A1', which incidentally coincides with the Chip ID + WRI. Again, the
> 'A1' is read whether or not I have the RAM actually connected. Any
> ideas on what I am missing? I am using an Embedded Artists prototype
> board for this code. It is also not clear to me how I know if each
> step listed above is actually accepted by the RAM chip. Thanks.
>
> Sutton
>

That's why I2C code is usually in the form of an interrupt drive state
machine.

philips_aps has posted sample code for the LPC214x but the ideas are
common for all. Look in the Files section here.

The bad news about I2C is that if at first it doesn't work, nothing
short of a logic analyzer will capture enough data vs time to be able
to troubleshoot it. Well, maybe a storage scope but only if you can
come up with a trigger just before or just at the start condition.

The good news is that the Philips code is likely to work. It is very
similar to code I used for a project with the LPC2106.

Richard
Reply by Sutton Mehaffey April 20, 20062006-04-20
I'm back on my I2C problems since I was pulled off the project for a
month or so. I'm still having some problems. The problem I thought
was I was trying to address a 5V RAM instead of a 3.3V RAM. I have
now a couple of ATMEL 256K serial RAM - 2.7V to 5V. I am still having
trouble with understanding the status of the I2C bus. It appears I am
getting the correct status, but even when the RAM is disconnected. I
'assume' from the status that those come from the I2C bus (LPC) and
not the RAM chip itself. How do I know that the RAM has sent back an
ACK? Does the new status on the bus indicate that it has? Here is
how my code looks based on writing a 0x77 and reading back from an
arbitrary address (0x01FFF):

I2C0CONSET = 0x20; // Issue Start
(status responds with 0x08)
I2C0DAT = 0xA1; // Issue a write command to RAM
I2C0CONSET = 0x04; // Set AA bit for ACK
I2C0CONCLR = 0x28; // Clear STA and SI flags
(status responds with 0x18)
I2C0DAT = 0x1F; // MSB
I2C0CONSET = 0x04; // Set AA bit for ACK
I2C0CONCLR = 0x08; // Clear SI flag
(status responds with 0x28)
I2C0DAT = 0xFF; // LSB
I2C0CONSET = 0x04; // Set AA bit for ACK
I2C0CONCLR = 0x08; // Clear SI flag
(status responds with 0x28)
I2C0DAT = 0x77; // Write Byte of 0x77
I2C0CONSET = 0x04; // Set AA bit for ACK
I2C0CONCLR = 0x08; // Clear SI flag
(status responds with 0x28)
I2C0CONSET = 0x14; // Issue a STOP
I2C0CONCLR = 0x08; // Clear SI flag

If I follow this with a Read Sequence, I get correct status, but an
'A1', which incidentally coincides with the Chip ID + WRI. Again, the
'A1' is read whether or not I have the RAM actually connected. Any
ideas on what I am missing? I am using an Embedded Artists prototype
board for this code. It is also not clear to me how I know if each
step listed above is actually accepted by the RAM chip. Thanks.

Sutton