cancel
Showing results for 
Search instead for 
Did you mean: 

I2C slave transmitter problem

davidrojas9
Associate II
Posted on March 02, 2012 at 11:49

Hello. I'm trying to connect two stm32f4discovery boards through I2C. One will be the master receiver, and will ask for 4 bytes of data to the other (slave receiver). I found an example for the master receiver, and it seems is working. Everything is correctly wired and configured, here is the function in the master receiver (uses polling) that reads the data:

/*re-enable ACK bit incase it was disabled last call*/

I2C_AcknowledgeConfig(I2C1, ENABLE);

/* Test on BUSY Flag */

while (I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));

I2C_GenerateSTART(I2C1, ENABLE);

/* Test on start flag */

while (!I2C_GetFlagStatus(I2C1,I2C_FLAG_SB));

/* Send address for read */

I2C_Send7bitAddress(I2C1, Address, I2C_Direction_Receiver);

/* Test Receive mode Flag */

while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

/* load in all 4 registers */

while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));

CapValueH = I2C_ReceiveData(I2C1);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));

CapValueL = I2C_ReceiveData(I2C1);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));

TempValueH = I2C_ReceiveData(I2C1);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));

TempValueL = I2C_ReceiveData(I2C1);

/*enable NACK bit */

I2C_NACKPositionConfig(I2C1, I2C_NACKPosition_Current); // Indicates that the current byte is the last received byte

I2C_AcknowledgeConfig(I2C1, DISABLE);

/* Send STOP Condition */

I2C_GenerateSTOP(I2C1, ENABLE);

while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF));

I've omitted some debugging stuf (led flashing to see what's happening). I've also omitted the configuration part (GPIO, clocks, i2c parameters), since it's pretty standard and no problem there.

And here, the slave transmitter, that uses interrupts. This is the interrupt EV routine:

void I2C1_EV_IRQHandler(void)

{

switch (I2C_GetLastEvent(I2C1))

{

//Slave Events

case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED: // EV1

{

//I2C_StretchClockCmd(I2C1, ENABLE);

I2C_SendData(I2C1, 1);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_TRANSMITTED));

I2C_SendData(I2C1, 0);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_TRANSMITTED));

I2C_SendData(I2C1, 1);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_TRANSMITTED));

I2C_SendData(I2C1, 3);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_TRANSMITTED));

//I2C_StretchClockCmd(I2C1, DISABLE);

break;

}

case I2C_EVENT_SLAVE_BYTE_RECEIVED: //EV2

{

break;

}

case I2C_EVENT_SLAVE_BYTE_TRANSMITTED: //EV3

{

break;

}

case I2C_EVENT_SLAVE_ACK_FAILURE: // EV3-1

{

break;

}

case I2C_EVENT_SLAVE_STOP_DETECTED: // EV4

{

break;

}

default:

{

break;

}

}

}

I use again some led flashing for debug that I've omitted, and the data I send is just random numbers to test.

If I try it like this, the master performs all the correct functions, receives the data, generates the stop and finish his function. The 4 bytes received are correct, but when it finishes the slave holds down the clock, so I cannot make more requests unless I reset the slave board. Now If I remove the clock stretching commented commands in the interrupt, and also disable clock stretching when I configure the slave i2c, everything seems to work fine, I receive the data and the slave releases the clock, so I can make another request, but the data received is always 0x03 in any new request (in the 4 bytes the same value).

I've been banging my head against this for several days and tested a lot of things. Unfortunately I don't have a logic analyzer at hand now.

#stm32-i2c #stm32-i2c-clock-speed
10 REPLIES 10
Posted on March 02, 2012 at 17:12

You'll certainly want to consider why you have the while() spin loops in the interrupt handler instead of using the event model as a state machine.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
davidrojas9
Associate II
Posted on March 02, 2012 at 18:14

I see your point, clive. I should put a counter I guess, and send 1 byte every time the interrupt fires. Anyway, I got it working this way: just after the last byte sended, I disable/enable the clock stretching:

I2C_StretchClockCmd(I2C1, DISABLE);

I2C_StretchClockCmd(I2C1, ENABLE);

This seems to reset the clock so the master can take over again and send the stop. It's working, and the data is always correct. I haven't tried with multiple sensors/boards attached to the bus, but shouldn't be a problem.

Anyway, I got ahold of an oscilloscope and notice something strange. I set the i2c clock speed to 2.5khz, but it's working at 100khz. If I set it to 5khz, it works at 200khz. I've tested several speeds. I've used one of the stm32f4discovery examples as a template project, and it seems everything is correct (the crystal at 8Mhz and everything). It's probably related to some clock not configured correctly, but I can't find it.

Posted on March 02, 2012 at 18:47

I think you need to explicitly handle the I2C_EVENT_SLAVE_STOP_DETECTED, and then physically clear the I2C_FLAG_STOPF flag.

To use your current construct, perhaps :

I2C_SendData(I2C1, 1);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_TRANSMITTED));

I2C_SendData(I2C1, 0);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_TRANSMITTED));

I2C_SendData(I2C1, 1);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_TRANSMITTED));

I2C_SendData(I2C1, 3);

while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_STOP_DETECTED));

       I2C_ClearFlag(I2C1, I2C_FLAG_STOPF);

That said the trick with I2C is for the slave to make no assumptions about how the master will do things. ie if it wants to keep reading bytes you should supply something until it decides it wants to stop.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
davidrojas9
Associate II
Posted on March 05, 2012 at 08:28

According to the standard, when you use the slave as a transmitter, the slave doesn't detect the stop flag, you need to detect somehow the NACK that the master sends when it finishes reading the last byte, but I can't find any event related.

Anyway, I still haven't figured out what's wrong with the clock speed. I checked the HSE_VALUE and I've even recreated the system_stm32f4xx.c with the excel configuration tool, but still when I set the i2c clock to 2.5khz, it goes to around 100khz.

The 

RCC_GetClocksFreq function is reporting the correct values: SYSCLK and HCLK = 168Mhz, PCLK1 = 42Mhz and PCLK2 = 84Mhz.

 

davidrojas9
Associate II
Posted on March 05, 2012 at 12:10

Well, I've tested several speeds for i2c: at 100khz, 50khz, and 10khz, it performs as expected. If i set it to 5khz, it performs at 200khz!? and at 2.5khz goes at 100khz. Strange...

davidrojas9
Associate II
Posted on March 05, 2012 at 12:11

Found the problem... for those speeds, the I2Cx->CCR part that define the speed ([11:0]) overflows. I think I need to slow down the PCLK1 speed to make it work.

Posted on March 05, 2012 at 13:25

You don't show your initialization code, but you could try using the I2C_InitStruct->I2C_DutyCycle = I2C_DutyCycle_16_9 which would use a div by 25 rather than a div by 3 clocking scheme. Then again the library might get in the way of setting the speed the hardware might permit.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
davidrojas9
Associate II
Posted on March 05, 2012 at 16:27

I've used dutycycle_2, but anyway in the duty cycle only takes effect in fast mode, not in standard mode (I noticed that reading the library source code). Anyway, I've changed the clocks and now it's running perfectly at 2.5khz.

About the NACK event, in the library header says:

  *    - EV3_2: When the master sends a NACK in order to tell slave that data transmission 

  *      shall end (before sending the STOP condition). In this case slave has to stop sending 

  *      data bytes and expect a Stop condition on the bus.

Being EV3_2: I2C_EVENT_SLAVE_ACK_FAILURE                       ((uint32_t)0x00000400)  /* AF flag */

I noticed that the event I2C_EVENT_SLAVE_ACK_FAILURE never gets triggered, but instead the I2C_IT_AF flag in the I2C1_ER_IRQHandler gets triggered when the NACK arrives. Sounds weird, but according to the library it does what it's supposed to.

Posted on March 05, 2012 at 21:02

Just be aware that dropping the APB clocks impacts all peripherals, and slows all accesses to that bus, not just the I2C device. This may result in a substantial loss in performance, while consuming about the same amount of power.

The ''fast'' mode really means anything from DC to 400 KHz, the games with the prescaler, and the 1:1, 2:1 and 16:9 duty cycle are attempt to allow the device to run at 400 KHz (a magic datasheet number) from the rather constraining clocking, divider and PLL design.

Judaical choice of fast/slow, duty and prescale should permit you to select suitable gearing to get the clocking you need to hit the magic numbers you have.

The library was written by a software guy, not the guy that designed the prescaler/clocking circuit.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..