Skip to main content
Hosting
Associate III
Feb 5, 2019 1:01 PM UTC
Solved

The problem with the i2c interface on the controller stm32f103

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;

This topic has been closed for replies.
After Forever
After ForeverBest answer
Senior III
Feb 5, 2019 1:26 PM UTC

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.

3 replies

After Forever
After ForeverBest answer
Senior III
Feb 5, 2019 1:26 PM UTC

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.

Hosting
HostingAuthor
Associate III
Feb 5, 2019 3:49 PM UTC

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.:thumbs_up:

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?

After Forever
Senior III
Feb 6, 2019 7:44 AM UTC

> 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;
}