cancel
Showing results for 
Search instead for 
Did you mean: 

The problem with the i2c interface on the controller stm32f103

Hosting
Associate III

I have a problem with the operation of the I2C interface on stm32f103. Hangs waiting for ADDR bit(while(!(I2C1->SR1 & I2C_SR1_ADDR))) in the master transmit mode.

In the Internet, many people have this problem, but is no solution. Who was the problem and who solved it?

RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;			//enable AF
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;			/enable clock port b
AFIO->MAPR &= ~AFIO_MAPR_I2C1_REMAP;
 
GPIOB->CRL |= GPIO_CRL_CNF6;				//Alternate function open drain
GPIOB->CRL |= GPIO_CRL_CNF7;				//Alternate function open drain
 
GPIOB->CRL |= GPIO_CRL_MODE6;				// Output mode, max speed 50 MHz.
GPIOB->CRL |= GPIO_CRL_MODE7;				// Output mode, max speed 50 MHz.
 
/*********** Init I2C *******************************/
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;		//Enable clock I2C1
I2C1->CR1 |= I2C_CR1_SWRST;                                  //reset i2c
I2C1->CR1 &= ~I2C_CR1_SWRST;
I2C1->CR1 &= ~I2C_CR1_PE;
 
I2C1->CR2 |= 36;							//set FREQ = APB1= 36MHz
 
I2C1->CCR |= 180;
I2C1->TRISE = 0x00;
I2C1->TRISE |= 37;						//TRISE = 36MHz APB1 bus + 1
I2C1->CR1 |= I2C_CR1_PE;					// I2C_CR1 register to enable the peripheral
 
/*********************send data***************************/
    //Start
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB)){};           ///Here is normal
(void) I2C1->SR1;
 
        //send addres unit
I2C1->DR = adr_unit;
while(!(I2C1->SR1 & I2C_SR1_ADDR)){};    //// Here is loop!
(void) I2C1->SR1;
(void) I2C1->SR2;
 
        //send addres reg
I2C1->DR = reg_addr;
while(!(I2C1->SR1 & I2C_SR1_TXE)){};
 
        //write data
I2C1->DR = data;
while(!(I2C1->SR1 & I2C_SR1_BTF)){};
I2C1->CR1 |= I2C_CR1_STOP;

1 ACCEPTED SOLUTION

Accepted Solutions

It means the master isn't receiving the ACK bit for some reason. Check the connectivity between master and slave, check the pull-ups, check the slave address. Use a logic analyzer or an oscilloscope to check whether the ACK is coming from the slave.

In real-world application you would want to add timeout mechanisms to all those infinite loops so your firmware doesn't hang indefinitely.

View solution in original post

3 REPLIES 3

It means the master isn't receiving the ACK bit for some reason. Check the connectivity between master and slave, check the pull-ups, check the slave address. Use a logic analyzer or an oscilloscope to check whether the ACK is coming from the slave.

In real-world application you would want to add timeout mechanisms to all those infinite loops so your firmware doesn't hang indefinitely.

Thank you very much friend, the address of the device is 7 bits, so need shift to the left by 1 bit to the address was correct.👍

In real-world application you would want to add timeout mechanisms to all those infinite loops so your firmware doesn't hang indefinitely.

Agreed. I seen many implementations, which one do you use?

> I seen many implementations, which one do you use?

One based on SysTick's counter (I assume it is configured to tick every millisecond). I make my polling functions return int 0 for good result, negative values for bad results (like timeout), and positive values when it must return something useful (like number of characters read). Part of your code in such a function would become something like this (... for omitted parts):

#define I2C_TIMEOUT_MS (1000U)
...
...
...
int i2c_read_sensor(void)
{
    uint32_t timeout;
    ...
    ...
    I2C1->CR1 |= I2C_CR1_START;
    timeout = I2C_TIMEOUT_MS;
    while (!(I2C1->SR1 & I2C_SR1_SB)) {
        if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
            if (timeout-- == 0) {
                ERR("I2C_SR1_SB timeout\n");
                return -4;
            }
        }
    }
    (void) I2C1->SR1;
    ...
    ...
    I2C1->DR = adr_unit;
    timeout = I2C_TIMEOUT_MS;
    while (!(I2C1->SR1 & I2C_SR1_ADDR)) {
        if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
            if (timeout-- == 0) {
                ERR("I2C_SR1_ADDR timeout\n");
                return -5;
            }
        }
    }
    (void) I2C1->SR1;
    (void) I2C1->SR2;
    ...
    ...
    return 0;
}