Hello everyone,
I am using the Flow-Chart of Typical I2C Interrupt Routine on MPC5606s Controller. We can write multiple bytes successfully. The problem is when we try to send a read command.
We send the slave and the register address that we want to read, then we generate a repeated START and we send the (slave address + Read bit), the slave send us the value of the register, after that the Arbitration Lost bit (IBAL) is set. We try to clear this bit on the Interrupt function but when we are outside this bit is set again. So we can only read 1 byte and then we cannot have access on I2C bus.
Any help will be appreciated.
Thank,
Josep
Hi,
So after you sent a repeated START and control word (slave address + Read bit) you should get IBIF set, or interrupt. Then you will select receive mode and do a dummy IBDR read to initiate byte data receiving. Slave will send data and Master get IBIF set again. Master should decide if initiate next byte read or generate STOP. In this time the IBAL is set, am I right? Does a Master switch over to slave? But if you clear the IBAL it should not be set again, unless Master want to drive the lines again. It could be great to check the I2C lines with a scope to see real signals.
BTW what kind of devices are on the bus? Just single Slave? What device is it?
BR,Petr
Hi,
I send the repeated Start and the control word (slave address + Read bit) then we clean the IBIF bit. When the IBIF is set again we change to receive mode and do a dummy IBDR read to initiate byte data receiving. Slave send a data and Master get IBIF set. In this time the IBAL is set. The Master do not switch over to slave.
On the bus there are a EEPROM (M24C02), it is the only device on the bus.
On the I2C lines we can see the following signals, after that the SDA continues LOW and the SCL continues HIGH.
The function of I2C interrupts is the following:
void I2C_Interupt(void)
{ uint8_t dummy; // Dummy variable to read I2C
if(I2C_0.IBCR.B.MS==1) /* If is in master mode */
{
if(I2C_0.IBCR.B.TX == 1) /* If TX */
{
if(I2C_0.IBSR.B.RXAK==0) /* If a acknowledge is received */
{
entery++;
ack_flag= 0;
if(entery == 1)
{
I2C_0.IBDR.R = iic_addr; /* Update IBDR, send internal address of I2C slave */
}
else if (entery <= (iic_len+1))
{
I2C_0.IBCR.B.RSTA = 1; // Set IBCR, generate repeated START
I2C_0.IBDR.R = iic_buffer[entery-2];
ack_flag = 1;
}
else
{
rd_flag = 0;
I2C_0.IBCR.B.TX = 0; // Set IBCR, Receive mode select
dummy = I2C_0.IBDR.R; // initiates next byte data receiving
entery_r = 0;
}
}
}
else /* If RX */
{
if(entery_r<(iic_rlen-1))
{
*(RData+entery_r) = I2C_0.IBDR.R; // Read the received byte from slave
entery_r++;
}
else if(entery_r==(iic_rlen-1))
{
I2C_0.IBCR.B.NOACK = 1; // In the last byte, change the ACK for NOACK
}
else
{
I2C_0.IBCR.B.MS = 0; // Set IBCR, Generate stop signal;
I2C_0.IBCR.B.NOACK = 0; // Reset the NOACK to default value
}
}
}
else /* Slave mode */
{
if(I2C_0.IBSR.B.IBAL == 1) /* Abitration Lost */
{
I2C_0.IBSR.B.IBAL = 1;
I2C_0.IBCR.B.MS=0;
if(I2C_0.IBSR.B.IAAS == 1)
{
if(I2C_0.IBSR.B.SRW == 1)
{
I2C_0.IBCR.B.TX = 1; // Set IBCR, Receive mode select
}
else
{
I2C_0.IBCR.B.TX = 0; // Set IBCR, Receive mode select
dummy = I2C_0.IBDR.R; // initiates next byte data receiving
}
}
}
else /* Abitration No Lost */
{
if(I2C_0.IBSR.B.IAAS == 1)
{
I2C_0.IBCR.B.TX = 1; // Set IBCR, Receive mode select
}
else
{
if(I2C_0.IBCR.B.TX == 1) /* If TX */
{
if(I2C_0.IBSR.B.RXAK==0) /* If a acknowledge is received */
{
entery++;
ack_flag= 0;
if(entery == 1)
{
I2C_0.IBDR.R = iic_addr; /* Update IBDR, send internal address of I2C slave */
}
else if (entery <= (iic_len+1))
{
I2C_0.IBCR.B.RSTA = 1; // Set IBCR, generate repeated START
I2C_0.IBDR.R = iic_buffer[entery-2];
ack_flag = 1;
}
else
{
rd_flag = 0;
I2C_0.IBCR.B.TX = 0; // Set IBCR, Receive mode select
dummy = I2C_0.IBDR.R; // initiates next byte data receiving
entery_r = 0;
}
}
}
else
{
if(entery_r<(iic_rlen-1))
{
*(RData+entery_r) = I2C_0.IBDR.R; // Read the received byte from slave
entery_r++;
}
else if(entery_r==(iic_rlen-1))
{
I2C_0.IBCR.B.NOACK = 1; // In the last byte, change the ACK for NOACK
}
else
{
I2C_0.IBCR.B.MS = 0; // Set IBCR, Generate stop signal;
I2C_0.IBCR.B.NOACK = 0; // Reset the NOACK to default value
}
}
}
}
}
I2C_0.IBSR.B.IBIF=1;
}
Best Regards,
Josep
Hi,
I think the reading part of Master mode is not completely correct. Try to use following one. I assume the iic_rlen specify the number of bytes to be read.
…
else
{
rd_flag = 0;
I2C_0.IBCR.B.TX = 0; // Set IBCR, Receive mode select
if(iic_rlen==1) I2C_0.IBCR.B.NOACK = 1; // if just 1 byte is going to be read
// NOACK next received byte
dummy = I2C_0.IBDR.R; // initiates next byte data receiving
entery_r = 0;
}
}
}
else /* If RX */
{
if(entery_r==(iic_rlen-2)
{
I2C_0.IBCR.B.NOACK = 1; // NOACK next received byte
}
if(entery_r==(iic_rlen-1)
{
I2C_0.IBCR.B.MS = 0; // Set IBCR, Generate stop signal;
I2C_0.IBCR.B.NOACK = 0; // Reset the NOACK to default value
}
*(RData+entery_r) = I2C_0.IBDR.R; // Read the received byte from slave
entery_r++;
}
Also for the IBAL and IBIF clearing use the register access. So
I2C_0.IBSR.R = 0x10; // clear IBAL
I2C_0.IBSR.R = 0x02; // clear IBIF
BR, Petr