cancel
Showing results for 
Search instead for 
Did you mean: 

I2C Issues,STM32F440x and L3GD20

jonphenry
Associate II
Posted on May 30, 2013 at 17:00

 

 

The original post was too long to process during our migration. Please click on the attachment to read the original post.
6 REPLIES 6
Posted on May 30, 2013 at 17:49

//Read data from I2C data register and return data byte

*Data = I2C_ReceiveData(I2C1);

//Wait for I2C1 EV7 --> One byte has been received

while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) );

This doesn't look right, while you might want the ReceiveData there to clear RXNE, but the real data you want will be available after the while()

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jonphenry
Associate II
Posted on May 30, 2013 at 18:24

Ok, Ill try it out tonight at home.

I did it this way based off of a diagram from the stm32f4xx reference manual pg 718 fig 244. It shows Data1 read before EV7. This did not correspond with the commented guidelines in the stm32f4xx_i2c.h file which states '1) Master Receiver mode: The master has to wait on the event EV7 then to read the data received from the slave (I2C_ReceiveData() function).' The L3GD20 datasheet shows something similar to the reference manual in that its showing the SAD+R then an ACK from the slave which I assume is EV6 then the slave sending DATA right after sending the ACK. Unless I assume wrong and EV6 is actually an internal acknowledge to the master telling itself 'I sent the address' and then it has to wait for EV7 which is the actual ACK from the slave.

jpeacock2399
Associate II
Posted on May 30, 2013 at 21:33

The EV6 state in RX (in the RM diagram) is to set up a single byte data RX or for DMA transfers.  For DMA that state is where I2C interupts are disabled and the DMA channel enabled to begin receiving data.  In the special case of only one data byte being received it is used to read data, as shown in the diagram, but only after sending an early NACK to terminate the incoming RX stream (the NACK has to be pipelined at N-2, followed by STOP at N-1).

Even in the 1 byte special case you'll still get an EV7 state to read in the data.  One area you have to watch for is the BTF flag included in the I2C state.  The ST examples aren't very good at handling this flag, so be sure to mask it when checking I2C event states.

The RM diagram is essentially correct but difficult to intepret.

  Jack Peacock
jonphenry
Associate II
Posted on May 30, 2013 at 22:11

Hi Jack, thanks for taking the time to clarify it for me. And also thanks to Clive1 for pointing out the sequence problem.

So, in essence, the code should be something like this?

//Starting after second START condition
//Send slave Address for read
I2C_Send7bitAddress(I2C1, L3GD20_MEMS_I2C_ADDRESS, I2C_Direction_Receiver);
//Disable ACK--> Send NACK
I2C_AcknowledgeConfig(I2C1, DISABLE);
//Wait for I2C1 EV6, check if slave has acknowledged Master Receiver
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
//Wait for I2C1 EV7 --> One byte has been received
while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) );
//Read data from I2C data register and return data byte
*Data = I2C_ReceiveData(I2C1);
//Generate STOP
I2C_GenerateSTOP(I2C1, ENABLE);
//stop bit flag
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF));

jpeacock2399
Associate II
Posted on May 31, 2013 at 15:58

The

I2C_CheckEvent

routine is where you will see problems if the BTF flag is set. It may be fixed in some newer libraries but in older libraries this routine doesn't handle the situation where the BTF flag is set in the I2C status, which causes your polling loop to hang with an undefined state. I use interrupt driven I2C instead of polled. At the head of the service routine I use the

I2C_GetLastEvent() library routine, and test for both the defined conditions in the library plus the same conditions with BTF set.

/* Master RECEIVER mode -----------------------------*/ 
/* --EV7 */ 
#define I2C_EVENT_MASTER_BYTE_RECEIVING ((uint32_t)0x00030040) /* BUSY, MSL and RXNE flags */ 
/* --EV7_2 */ 
#define I2C_EVENT_MASTER_BYTE_RECEIVED ((uint32_t)0x00030044) /* BUSY, MSL, RXNE and BTF flags */ 
/* Master TRANSMITTER mode --------------------------*/ 
/* --EV8 */ 
#define I2C_EVENT_MASTER_BYTE_TRANSMITTING ((uint32_t)0x00070080) /* TRA, BUSY, MSL, TXE flags */ 
/* --EV8_2 */ 
#define I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */ 

I test for both conditions on send and receive. Jack Peacock
jonphenry
Associate II
Posted on June 01, 2013 at 16:42

Clive1: Thanks , this did the trick. Ive posted the new ReadReg() function. It works perfectly. I also made the same change to my ReadBuffer() (to have the L3GD20 increment through the output registers for me) function and it works perfectly as well.

status_t L3GD20_ReadReg(u8_t Reg, u8_t* Data) {
//Wait until I2C1 is not busy any more
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
//Re-enable acknowledge
I2C_AcknowledgeConfig(I2C1, ENABLE);
//Send I2C1 START condition
I2C_GenerateSTART(I2C1, ENABLE);
//Wait for I2C1 EV5 --> Slave has acknowledged start condition
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
//Send slave Address for write
I2C_Send7bitAddress(I2C1, L3GD20_MEMS_I2C_ADDRESS, I2C_Direction_Transmitter);
//Wait for I2C1 EV6, check if slave has acknowledged Master transmitter
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
//Send address of register
I2C_SendData(I2C1, Reg);
//Wait for I2C1 EV8, check if slave has acknowledged Master Transmitter
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));
//Send START condition a second time (Re-Start)
I2C_GenerateSTART(I2C1, ENABLE);
//Wait for I2C1 EV5 --> Slave has acknowledged start condition
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
//Send slave Address for read
I2C_Send7bitAddress(I2C1, L3GD20_MEMS_I2C_ADDRESS, I2C_Direction_Receiver);
//Send NMAK
I2C_AcknowledgeConfig(I2C1, DISABLE);
//Wait for I2C1 EV6, check if slave has acknowledged Master Receiver
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
//Wait for I2C1 EV7 --> One byte has been received
while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) );
//Read data from I2C data register and return data byte
*Data = I2C_ReceiveData(I2C1);
I2C_GenerateSTOP(I2C1, ENABLE);
//stop bit flag
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF));
return MEMS_SUCCESS;
}

Jack: Ill have to study your advice on the BTF bit a bit more, I dont quite have full command of the whole concept of I2C communication yet. Your saying that your checking both conditions something like this?

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //EV6
while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVING) ); //EV7
while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) ); //EV7_2
*Data = I2C_ReceiveData(I2C1);

I beleive we are using different versions of the library, the .h file in ver 1.1.0 doesnt define a RECEIVING condition only RECEIVED. It does define both TRANSMITTING AND TRANSMITTED conditions though. I know you had mentioned older libraries, so I assume you are basing your work off of an older one. In reading some more in the 'guidelines' in the stm32f4xx_i2c.h file in the standard library ver 1.1.0, around line 390 there is a note:

* @note In case the user software does not guarantee that this event EV7 is 
* managed before the current byte end of transfer, then user may check on EV7 
* and BTF flag at the same time (ie. (I2C_EVENT_MASTER_BYTE_RECEIVED | I2C_FLAG_BTF)).
* In this case the communication may be slower.

Is this referring to the issue your talking about? My sense of what you said is that the checkevent doesnt check if the BTF bit is set or not and moving on with it set would cause a hang. Using the OR in the note should handle that?

while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED | I2C_FLAG_BTF) );

Also Jack, you use interrupt driven I2C....do you find its more efficient or faster than polling? Im assuming you're doing something like this:

//Send I2C1 START condition
I2C_GenerateSTART(I2C1, ENABLE);
//Interrupt ITEVFEN triggered after start bit sent
//Wait for I2C1 EV5 --> Slave has acknowledged start condition
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
//Send device address for write
I2C_Send7bitAddress(I2C1, L3GD20_MEMS_I2C_ADDRESS, I2C_Direction_Transmitter);
//Interrupt ITEVFEN triggered after address sent
//Wait for I2C1 EV6, check if slave has acknowledged Master transmitter
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
//Send the device's internal address to read from with MSB asserted
I2C_SendData(I2C1,ReadAddr|0x80);
//Interrupt ITEVFEN triggered after data byte transfer complete
//Wait for I2C1 EV8_2, check if slave has acknowledged Master Transmitter
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
And so on....

I dont know what the interrupt costs are in terms of cycles on this uC, is it worth having an interrupt on every check as opposed to just polling an waiting, if that is how you're doing it? Also, what are your opinions on using the CPAL library for i2c as opposed to the standard peripherals library? I really couldnt find too much info on the CPAL. From what I did find, it looks to be more or less an 'API' for I2C, allowing the programmer to write code at a higher program level while possibly having less control over the ground workings of the communication.