AnsweredAssumed Answered

i2c - No Slave Address Acknowledgement - Standard Peripheral Library - STM32L1xx

Question asked by romero.paul on Dec 27, 2014
Latest reply on Dec 31, 2014 by romero.paul
Hi Folks:

After calling I2C_Send7BitAddress(), it correctly transfers the
slave address--0x32, across the I2C bus to the LIS3DH accelerometer,
and the I2C1 SR1 and SR2 registers contain the right successful
response values--0x82 and 0x08 respectively.

However, an assembly generation problem in I2C_CheckResponse()
causes the value of SR1 to be incorrectly read. In particular,
SR1 is read twice before the value is transferred to the
flag1 variable. The first read of SR1 causes the Bit 1 to
be cleared before its value is transferred to flag1--thus 0x80
is transferred to flag1. Section 26.6.6 of the ST documentation
explains how SR1 behaves when read.
(i.e. ST document RM0038 -- The Reference Manual for the STM32L151xx etc.)

Here are more details.

The sequence of I2C1 events that leads to the problem is as follows:
1) After the START condition is generated the CR1 register PE bit is set.
2) I2C_Send7BitAddress() writes the slave address 0x32 to the DR register.
3) After that SR1 = 0x82 and SR2 = 0x07
(Note: Bit 1 of the SR1 register is "Received Address Match" bit.)
4) Next I2C_CheckEvent() begins executing and SR1 and SR2 still
   have the same values.
5) The problem occurs during the execution of the following line of code:

/* Read the I2Cx status register */
flag1 = I2Cx->SR1;

The intent of the line of code is read the entire contents of
the 32 bit SR1 register into the flag1 variable. However,
the corresponding assembly instruction reads the register
twice before transferring the contents to flag1. The first read
clears Bit 1 of SR1.

The following commented assembly language segment--which corresponds
to the above line of code, illustrates how this occurs.
The commented assembly code is as follows:

                                        // R7 = 0x20002c38 at this point thus
                                        // R7 + 4 = 0x20002C3C. It contains
                                        // the address of I2C1 = 0x40005400--
                                        // the variable I2Cx. The instruction should
    687B        ldr r3, [r7, #4]        // only store the data pointed at by
                                        // (R7 + 4) in R3. However,
                                        // executing this instruction somehow
                                        // causes the SR1 register to be read which
                                        // clears Bit 1. R3 contains 0x40005400
                                        // as expected at this point.
                                        // But it is mystery why register SR1--
                                        // at 0x40005414, has been read.

                                        // R3 = 0x40005400 at this point
                                        // thus R3 + 20 = 0x40005014--the
                                        // address of the SR1 register--
                                        // variable I2Cx->SR1.
                                      // variable I2Cx->SR1.
    8A9B        ldrh r3, [r3, #20]      // After this instruction executes
                                        // R3 = 0x00000080 which is the
                                        // value of the SR1 register.
                                        // However, since the SR1 register
                                        // has already been read, it no
                                        // longer has Bit 1 Set.

    B29B        uxth r3, r3
    60FB        str r3, [r7, #12]       // Writes 0x80 to flag1 at 0x20002c44

Higher level overview

I2C_Send7bitAddress() is called as follows with slave_addr = 0x32.

        I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);

I2C_CheckEvent() never succeeds because the contents of the I2C1 SR1
register are no correctly extracted.


Hi Folks:

I now have a better understanding of the problem and can describe
it more accurately. In case I forgot to mention it, the STL32L
processor is the only I2C master and the LIS3DH accelerometer is
the only slave.

The problem is intermittent and usually occurs after many I2C
data transfers in both directions. What happens is that the
processor never receives an acknowledgement after sending the
slave address with the following API call.

    I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);

If you examine the STM32L151RD processor I2C1 SR registers, they both have
a value of 0 when the problem occurs.  (i.e. In this case SR1 should have the
value 0x80 and SR2 should have the value 0x07.) I have not observed the problem
in the receiver direction but believe it can occur and there may be other
cases where there is no response to an I2C transaction initiated by the master.

I configured the I2C interface to a speed of 100 KHZ which is the standard
speed according the the ST documentation and examples I have seen.
Some possible causes of the problem are as follows:

* Wrong I2C speed.

* Clock drift.

* Interference from some other clock source which needs to be disabled
  or configured differently.

* Spurious LIS3DH accelerometer interrupts which occur when no
  sample data is available.

Please let me know if you have any ideas about the cause and solution
to the problem. In the short term, it may be acceptable to reset
the I2C bus interface. Is there a standard method for resetting it
is busy due to a transaction initiated by the master ?

Also, I made the following technical modifications to the code
which did not result in any noticeable behavioral change.

Best Regards,

Paul R.

Add the following clock configuration call to the clock_init() routine:

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

In P_I2C_Init() add the following interrupt configuration call:

    I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);

Following this comment:

    // Now Enable the interface.

> Hi Folks:
> I am using an LIS3DH accelerometer connected to an STM32L151RD processor.
> The OS is FreeRTOS and Rowley Crossworks for ARM is the toolchain.
> I am using the LIS3DH Platform independent driver from ST Micro in
> conjunction with version 3.0 of the STM32L1xx Standard Peripherals
> Library for communication between the processor and accelerometer
> over an I2C interface.
> I have provided two hardware specific routines to read and
> write registers which are called by LIS3DH_ReadReg() and LIS3DH_WriteReg().
> I am experiencing the following problem:
> What generally happens is that the I2C_Send7bitAddress() never receives
> a response from the accelerometer after an INT1 interrupt when the
> INT1_SRC register contains a value of 0x60. It occurs randomly after many
> accelerometer interrupts have--apparently, resulted in reception
> of a byte. However, note that the INT1_SRC register almost always
> has a value of 0x60 after the interrupt occurs which makes me
> question whether the data read from OUT_X, OUT_Y, and OUT_Z
> registers is valid. Also, this appears to be true regardless
> of the motion of the device containing the accelerometer.
> An file containing the relevant code segments and a register dump
> is attached to this letter.  See "COMMENT" in the attachment for more details.
> The contents of the attachment are as follows:
> A register dump after initialization is provided in section F
> and the contents of each section is as follows:
> SECTION A: Routine for reading a register over the I2C interface.
> (i.e. This is where the problem is.)
> SECTION B: LIS3DH ReadReg routine from lis3dh_driver.c
> SECTION C: Code that executes as a result of INT1 interrupt.
> SECTION D: Interrupt handler which implements CMSIS EXTI0_IRQHandler().
> SECTION E: I2C Initialization routine.
> SECTION F: Register dump after initialization.
> Best Regards,
> Paul R.