i2c slave device cannot prepare data done before i2c master's read command.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

i2c slave device cannot prepare data done before i2c master's read command.

3,438 Views
carter_wang
Contributor I

Hi,

I based on freertos_i2c sample code in lpcxpresso66s28 SDK to develop my i2c slave program.

However, I found if I don't add a delay between write and read commands, the master side will get wrong data from the slave side. The root cause is the slave side cannot prepare data in time.

The master device calls the API "I2C_RTOS_Transfer(&master_rtos_handle, &masterXfer)" to send the write and read commands to the slave one.

Could you tell me how to fix this kind of problem?

 

BR,

carter

0 Kudos
Reply
9 Replies

3,432 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

Can you give more detailed information about the issue? for example, pls give the master code and processor, the slave code and the slave processor, what is the sequence for example just I2C_address + READ, reading data0, reading data1, ...or whatever

BR

XiangJun Rong

0 Kudos
Reply

3,430 Views
carter_wang
Contributor I

 

Hi XiangJun,

 

Yes, it is just I2C_address + READ.
I wanna check the I2C slave function first.

It seems that i2c slave device should stretch the clock before all data are ready.
However, I am not sure which API can stretch the clock.

 

BR,

carter

 

BR,

carter

0 Kudos
Reply

3,426 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Carter

Can you refer to the ticket?

https://community.nxp.com/t5/LPC-Microcontrollers/LPC804-I2C-Clock-stretching/td-p/1381366

It appears that there is not any api function to set/clear the CFG[MONCLKSTR] bit, you have to write the bit your self.

BR

XiangJun Rong

0 Kudos
Reply

3,415 Views
carter_wang
Contributor I

 

Hi XiangJun,

Now, I know set/clear the CFG[MONCLKSTR] bit can solve the issue.

However, when is the proper time for the slave side to set the bit to avoid that the master side tries to access the i2c bus?

BR,

carter

0 Kudos
Reply

3,406 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

As you know that the both SCL and SDA pin are open drain, once an open drain pin is driven LOW by one entity, it can not be driven to high by any other entity on the bus at same time. So if the slave drives the SCL to low, the master will wait until the SCL is high.

BR

XiangJun Rong

 

0 Kudos
Reply

3,401 Views
carter_wang
Contributor I

 

Hi XiangJun,

 

Thanks for your explanation.

However, my question is when I should pull the clock low to let the master keeps waiting?

This is my code structure of i2c slave implementation below.

create a task to:

1. call I2C_SlaveTransferCreateHandle() to register the callback function with interrupt mode.

2. wait for the semaphore from the registered callback function.

3. parse the read data and prepare the data will be returned back soon.

 

Now, I do clock stretching after I got the semaphore(It means the slave got the complete data from the host side). However, even that, I am not sure whether it is too late to pull the clock low before the host side sends the read command.

I based on freertos_i2c sample code to implement the i2c slave function.

It seems my manipulations are too high level.

Should I check more information of i2c registers?

Could you tell me more about that?

 

BR,

carter

0 Kudos
Reply

3,388 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

I have checked your i2c_slave_callback.txt file, I do not think you can extend the SCL in the callback function, because after a message has transmitted or a buch of characters has received, the callback function is called.

You should modify the ISR function and extend the SCL for example in the code:

if ((handle->transfer.rxData != NULL) && (rxSize != 0U))
{
/* continue transaction */
base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK;
tmpdata = (uint8_t)base->SLVDAT;

 

Hope it can help you

BR

XiangJun Rong

 

void I2C_SlaveTransferHandleIRQ(I2C_Type *base, i2c_slave_handle_t *handle)
{
uint32_t i2cStatus = base->STAT;
uint8_t tmpdata;
size_t txSize;
size_t rxSize;

if ((i2cStatus & I2C_STAT_SLVDESEL_MASK) != 0U)
{
I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveDeselectedEvent);
I2C_SlaveClearStatusFlags(base, I2C_STAT_SLVDESEL_MASK);
}

/* SLVPENDING flag is cleared by writing I2C_SLVCTL_SLVCONTINUE_MASK to SLVCTL register */
if ((i2cStatus & I2C_STAT_SLVPENDING_MASK) != 0U)
{
bool slaveAddress =
(((i2cStatus & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == (uint32_t)I2C_STAT_SLVST_ADDR);

if (slaveAddress)
{
(void)I2C_SlaveAddressIRQ(base, handle);
I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveAddressMatchEvent);
}
else
{
switch (handle->slaveFsm)
{
case kI2C_SlaveFsmReceive:
{
bool slaveReceive = (((i2cStatus & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) ==
(uint32_t)I2C_STAT_SLVST_RX);

if (slaveReceive)
{
rxSize = handle->transfer.rxSize;
/* if we have no receive buffer in this transfer, call callback to get new */
if ((handle->transfer.rxData == NULL) || (rxSize == 0U))
{
I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveReceiveEvent);
}

rxSize = handle->transfer.rxSize;
/* receive a byte */
if ((handle->transfer.rxData != NULL) && (rxSize != 0U))
{
/* continue transaction */
base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK;
tmpdata = (uint8_t)base->SLVDAT;
*(handle->transfer.rxData) = tmpdata;
(handle->transfer.rxSize)--;
(handle->transfer.rxData)++;
(handle->transfer.transferredCount)++;
}

rxSize = handle->transfer.rxSize;
txSize = handle->transfer.txSize;
/* is this last transaction for this transfer? allow next transaction */
if ((0U == rxSize) && (0U == txSize))
{
handle->isBusy = false;
I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveCompletionEvent);
}
}
else
{
base->SLVCTL = I2C_SLVCTL_SLVNACK_MASK;
}
}
break;

case kI2C_SlaveFsmTransmit:
{
bool slaveTransmit = (((i2cStatus & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) ==
(uint32_t)I2C_STAT_SLVST_TX);

if (slaveTransmit)
{
txSize = handle->transfer.txSize;
/* if we have no data in this transfer, call callback to get new */
if ((handle->transfer.txData == NULL) || (txSize == 0U))
{
I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveTransmitEvent);
}

txSize = handle->transfer.txSize;
/* transmit a byte */
if ((handle->transfer.txData != NULL) && (txSize != 0U))
{
base->SLVDAT = *(handle->transfer.txData);
/* continue transaction */
base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK;
(handle->transfer.txSize)--;
(handle->transfer.txData)++;
(handle->transfer.transferredCount)++;
}

rxSize = handle->transfer.rxSize;
txSize = handle->transfer.txSize;
/* is this last transaction for this transfer? allow next transaction */
if ((0U == rxSize) && (0U == txSize))
{
handle->isBusy = false;
I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveCompletionEvent);
}
}
else
{
base->SLVCTL = I2C_SLVCTL_SLVNACK_MASK;
}
}
break;

default:
/* incorrect state, slv_abort()? */
break;
}
}
}
}

0 Kudos
Reply

3,365 Views
carter_wang
Contributor I

 

Hi XiangJun,

I got it.

Thanks for your sharing.

BR,

carter

0 Kudos
Reply

3,421 Views
carter_wang
Contributor I

 

Hi XiangJun,

 

Thanks for your information.

 

BR,

carter

0 Kudos
Reply