cancel
Showing results for 
Search instead for 
Did you mean: 

I²C read operation freezing

wolfram2
Associate II
Posted on June 15, 2012 at 18:08

Hello guys,

I've got the following problem: I'm trying to read a register of a gyroscope with I²C. Everything works fine as long as I'm using several delays during the I²C communication. This wasn't a problem until yet, where I noticed that exact those delays lock the CPU completely down. So now I need to figure out why this code keeps freezing without delays:

uint8_t I2C_Read_Byte(uint8_t __slave_addr, uint8_t __reg_addr)

{

    uint8_t __retval;

    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

    I2C_GenerateSTART(I2C1, ENABLE);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2C1, __slave_addr, I2C_Direction_Transmitter);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    I2C_SendData(I2C1, __reg_addr);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) ;

    I2C_GenerateSTART(I2C1, ENABLE);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2C1, __slave_addr, I2C_Direction_Receiver);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

    Delay_us(57); /* freezing without delay */

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) ;

    I2C_AcknowledgeConfig(I2C1, DISABLE);

    __retval = I2C_ReceiveData(I2C1);

    Delay_us(101); /* same here */

    I2C_GenerateSTOP(I2C1, ENABLE);

    Delay_us(111); /* and here */

    I2C_AcknowledgeConfig(I2C1, ENABLE);

    return __retval;

}

I'd be very greatful for any help!

Best regards and thanks in advance,

Wolfram
12 REPLIES 12
frankmeyer9
Associate II
Posted on June 19, 2012 at 14:55

------------------ Okay, seing that touching the ''edit'' button is a no-go here, I won't touch it anymore --------------------

It's not the ''edit'' button, it just happens from time to time. What mostly works is: save the text to an external editor, than leave the thread, than reenter the thread again, and try to post it again.

I suggest:

Make the delay function non-blocking. Use a timer, and return true/false if time has expired or not. Call it repeatedly instead.

Turn your receive function into a state machine, and not a sequencial code. Again, call it repeatedly, until successful.

You might get several microseconds jitter, don't know if that matters for a 200Hz sensor signal.

emalund
Associate III
Posted on June 19, 2012 at 17:45

The I2C bus is a beast

incorrect

The ST32 implementation of the I2C bus is a beast

correct

Erik
wolfram2
Associate II
Posted on July 08, 2012 at 10:15

Awesome!

I managed to solve it now! The problem indeed was the AcknowledgeConfig. Seems like one of the devices doesn't acknowledge the writte read address operation. Correct code now is:

uint8_t I2C_Read_Byte(uint8_t __slave_addr, uint8_t __reg_addr)

{

    uint8_t __retval;

    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

    I2C_GenerateSTART(I2C1, ENABLE);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2C1, __slave_addr, I2C_Direction_Transmitter);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    I2C_SendData(I2C1, __reg_addr);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) ;

    I2C_GenerateSTART(I2C1, ENABLE);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2C1, __slave_addr, I2C_Direction_Receiver);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

    I2C_AcknowledgeConfig(I2C1, DISABLE);

    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) ;

    __retval = I2C_ReceiveData(I2C1);

    I2C_GenerateSTOP(I2C1, ENABLE);

    I2C_AcknowledgeConfig(I2C1, ENABLE);

    return __retval;

}

I managed to push the time footprint of the function down to 2480 - 2500 µs :) Yay!

But then I realised another problem: the timers weren't working at all. No matter what I set the timer to (TIM1, Period 360000, Clock_Div_1, Count_Up, UpdateInterrupt) I would just constantly run at 1MHz. I then thought ''oh, I have to clar the interrupt pending bit''. But then the timer just fired one single time ang got stuck then. Also manually resetting the counter to 0 didn't work. So I ended up with using the SysTick, set to 200Hz (360000 Ticks @ 72MHz).

Thanks for all your help!

Best regards,

Wolfram