cancel
Showing results for 
Search instead for 
Did you mean: 

I2C Rx buffering

brianforde9
Associate II
Posted on January 11, 2009 at 13:29

I2C Rx buffering

5 REPLIES 5
brianforde9
Associate II
Posted on May 17, 2011 at 12:57

Hi all,

I'm using STM32F103xxx. I am having endless problems with the I2C devices, due to the presence of the Rx buffer. Sometimes, if my interrupt is delayed, Rx data is present in both the buffer and shift register. This in itself is not a problem, as when both contain data, the clock is stretched.

However, it becomes a critical issue when the device is acting in master mode, and the last byte received has to be NAKed. The problem is that the interrrupt for the second to last byte (the instance when NAK is decided) might be delayed, causing the device to continue clocking SCK. When I do receive the interrupt for the last byte, I reenable ACK (as I want to be able to act as a Slave also) and attempt to generate a STOP condition. The problem is that the NAK never appears on the bus, and an additional byte (which I don't want) appears on the bus from the slave.

Anyone any ideas? How do I disable the Rx buffer, and read only from the shift register?

jj
Associate II
Posted on May 17, 2011 at 12:57

Hi Brian,

Have also found the I2C implementation a challenge.

1) Is it correct that yours is a ''multi-master'' application - and the problem you've described occurs as you switch from master transmit to ''slave receive'' mode?

2) Have you tried - and if so - do you operate correctly if you remain ''fixed'' as either a master or slave?

3) Are your ''other masters'' also STM32 devices?

[ This message was edited by: jj.sprague on 22-12-2008 16:02 ]

brianforde9
Associate II
Posted on May 17, 2011 at 12:57

Quote:

1) Is it correct that yours is a ''multi-master'' application - and the problem you've described occurs as you switch from master transmit to ''slave receive'' mode?

2) Have you tried - and if so - do you operate correctly if you remain ''fixed'' as either a master or slave?

3) Are your ''other masters'' also STM32 devices?

Hi JJ,

I am using the devices on a Multi-Master bus. However this is not the cause of the problem. I observe the problem in Master-Receive mode (my devices may act in any of four modes - MasterTx, Master Rx, Slave Tx or Slave Rx). I am transferring a large amount of streaming data (compressed 128kbps audio) successfully over this framework. It is not dependent on whether switching is active or not. It is dependent only on whether the interrupt for the last but one byte is delayed or not. Each RX interrupt determines whether to ACK or NAK the next received byte. If the interrupt for the last but one byte is delayed, then the last byte will be ACKed instead of NAKed (the decision to NAK is made too late).

All devices on the bus are STM32s.

To my mind, it is not possible to guarantee that the ACK bit is set correctly for every byte in a transfer. This is a deficiency of the I2C implementation in the STM32, due to the additional byte buffer. If this buffer were not present, all would be well.

Brian F.

loeffel
Associate II
Posted on May 17, 2011 at 12:57

Hi Brian

Is it right that the BTF flag is set if the intrrupt get delayed? Is the last received data byte lost at the input shift register or does it get copied into the DR register after reading this register? It seems that the SCL only get stoped if the BTF falg is set. In receiver mode this should happens after receiving the second byte including the ACK bit, which is to late!!! I have no idea how to fix this problem except it is possible to allow this extra dummy read sequence to transmit the NACK.

In our application the I2C is used in Master mode only to read and write data to an external EEPROM. We also have higher priority interrupts and flash erase/write cycles which delays the I2C interrupt handling. We had problems if the last interrupt get delayed at Master-Receive mode and the STOP condition was already transferd. If this happens the BUSY and MSL flag are not set. To fix this probelem I used 0x00000040 as an additional event beside the existing I2C_EVENT_MASTER_BYTE_RECEIVED.

One open question is the error handling. Is there a proper way to reset the I2C device? The SWRST and the PE flags seems to have to many exeptions even if there is an error situation and the state of the I2C is unkown. By the way the I2C_DeInit() doesn't help as well.

brianforde9
Associate II
Posted on May 17, 2011 at 12:57

Quote:

Is it right that the BTF flag is set if the intrrupt get delayed?

If the uC does not read the Data Register when a byte has been received, the clock continues to be active. When the second byte is received (and acknowledged), this second byte is kept in the receive buffer, and the clock is stretched (stopped).

The BTF flag is set if two receive bytes are pending (the first in the Data Register, and the second in the recceive buffer). The clock is stretched when this happens.

Quote:

Is the last received data byte lost at the input shift register or does it get copied into the DR register after reading this register?

This second byte is not lost, but is stored in the receive register until the first byte is read from the Data Register.

Quote:

It seems that the SCL only get stoped if the BTF falg is set.

That is correct. The clock is stretched if the device cannot store any further receive data (i.e. both the DR and receive buffer are full).

Quote:

In receiver mode this should happens after receiving the second byte including the ACK bit, which is to late!!! I have no idea how to fix this problem except it is possible to allow this extra dummy read sequence to transmit the NACK.

I agree. The clock is stopped, by the device, too late. In my case, I wanted to NAK the byte in the receive buffer (in the case that it is the last byte of the transfer, and will be followed by a STOP condition), but because the interrupt was delayed, it has already been ACKed.

The only other possibility is to use DMA. I guess it was the intention of the chip architect that anyone using I2C would use DMA. By using DMA, there is no danger of interrupt delay, so the last received byte is guaranteed to be NAKed correctly (indicated by the DMA engine). However, you need to know exactly how many bytes are to be received before starting the transfer. I have not used this method, but on paper it will resolve this problem.

Quote:

In our application the I2C is used in Master mode only to read and write data to an external EEPROM. We also have higher priority interrupts and flash erase/write cycles which delays the I2C interrupt handling. We had problems if the last interrupt get delayed at Master-Receive mode and the STOP condition was already transferd. If this happens the BUSY and MSL flag are not set. To fix this probelem I used 0x00000040 as an additional event beside the existing I2C_EVENT_MASTER_BYTE_RECEIVED.

I found the sample I2C code, using the event definitions to be a very inefficient way of construction the interrupt handler, and so do not use those event definitions. Instead, the bits in the ''event'' are individually tested (I found there are many conditions which are not defined by the events). The following is an example...

Code:

void *i2cIdle(

u32 lvEvent)

/*

* BUSY, MSL and SB

*/

{

if(!(I2C_FLAG_BUSY & lvEvent))

return(i2cIdle);

if((I2C_FLAG_MSL & lvEvent) &&

(I2C_FLAG_SB & lvEvent))

return(i2cMasterMode(lvEvent));

if((I2C_FLAG_ADDR & lvEvent) ||

(I2C_FLAG_DUALF & lvEvent))

return(i2cSlaveAddressed(lvEvent));

return(i2cIdle);

}

Quote:

One open question is the error handling. Is there a proper way to reset the I2C device? The SWRST and the PE flags seems to have to many exeptions even if there is an error situation and the state of the I2C is unkown. By the way the I2C_DeInit() doesn't help as well.

Again, the ''events'' defined are less than helpful. I test each bit individually, and act accordingly (see the following);

Code:

void __attribute__((__interrupt__)) I2C2_ER_IRQHandler(void)

{

unsigned lvEvent;

lvEvent = I2C_GetLastEvent(I2C2);

if(I2C_FLAG_ARLO & lvEvent)

{

I2C_ClearFlag(I2C2,

I2C_FLAG_ARLO);

fpHandler = i2cIdle;

return;

}

if(I2C_FLAG_BERR & lvEvent)

{

I2C_ClearFlag(I2C2,

I2C_FLAG_BERR);

fpHandler = i2cIdle;

return;

}

fpHandler = fpHandler(lvEvent);

return;

}

Loeffel,

I am no longer working with the STM32 device (I have changed company) so am unlikely to be able to provide any further assistance on this matter. I have found overall, that the STM32 is well architected and very much a pleasure to work with (the access functions provided by the firmware are, in general, well written and intuitive).

Best regards,

Brian F.