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