cancel
Showing results for 
Search instead for 
Did you mean: 

I2C Communication problem with STM32F103C8 Blue Pill Board.

Siva Ram
Associate II

I am using STM32F103C8 Blue Pill Board. I am trying to communicate with ATH20 Temperature & Humidity sensor from past One week. Configured STM32F103 Blue Pill - System Clock As (8mHz * PLL8 )= 64mHz and APB1 as 32mHz.

I have Trouble wit START bit, When i try to START I2C and waits for SR1_SB bit it remains LOW.

Please check my code here and give me some suggestions to work done...

uint32_t read_reg = 0;
 
void Sys_Clock_Config (void){
	RCC->CR |= RCC_CR_HSEON;
	while (!(RCC->CR & RCC_CR_HSERDY));
	RCC->APB1ENR |= RCC_APB1ENR_PWREN;
	FLASH->ACR |= (1<<1)|(1<<4);
	RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
	RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
	RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
	RCC->CFGR |= ((RCC_CFGR_PLLSRC) | (RCC_CFGR_PLLMULL8));
	RCC->CR |= RCC_CR_PLLON;
 
	while (!(RCC->CR & RCC_CR_PLLRDY));
	RCC->CFGR |= RCC_CFGR_SW_PLL;
	while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
void I2C2_Pin_Config(void){
	RCC->APB1ENR	|= RCC_APB2ENR_IOPBEN;
	RCC->APB1ENR	|= RCC_APB1ENR_I2C2EN;
        
	GPIOB->CRH		|=  GPIO_CRH_MODE10 | GPIO_CRH_MODE11; // PB10 & PB11 
	GPIOB->CRH		|= GPIO_CRH_CNF10 | GPIO_CRH_CNF11; //AF Open Drain
}
 
void I2C2_Master_Init(void){
	I2C2_Pin_Config();
	I2C2->CR1		&= ~I2C_CR1_PE;
	I2C2->CR1		|= I2C_CR1_SWRST;
	I2C2->CR1		&= ~I2C_CR1_SWRST;
	I2C2->OAR1		= 0x4000;
	I2C2->CR2		= 0x20;		// for APB1 clock(32 mHz)
	I2C2->CCR		= 0xA0;       // (1/32) = 31.25 & 5/31.25 = 160 	
	I2C2->TRISE		= 0x21;		// decimal 33
	I2C2->CR1		|= I2C_CR1_PE;
	while(!(I2C2->SR2 & I2C_SR2_BUSY) ){}
}
void I2C2_Write_Condition(){	
	I2C2->CR1		&= ~I2C_CR1_POS;
	I2C2->CR1		|= I2C_CR1_START;	
	while(!(I2C2->SR1 & I2C_SR1_SB)){} 
        print("I2C Started\n");
	read_reg = I2C2->SR1;
}

Line number 28

it stuck inside the while loop.

while(!(I2C2->SR1 & I2C_SR1_SB)){}

Can someone help me please? Did i mis anything?

1 ACCEPTED SOLUTION

Accepted Solutions
Foued_KH
ST Employee

Hello @Siva Ram​ ,

For the I2C communication with sensor , please try to :

1. Enable the I2C and GPIO CLOCKS

2. Configure the Pins for I2C

3. Reset the I2C

4. Set the I2C clock

5. Enable the I2C

I2C START

void I2C_Start (void)
{
/* 1. Send the START condition 
2. Wait for the SB ( Bit 0 in SR1) to set. This indicates that the start condition is generated
*/	
	I2C1->CR1 |= (1<<10);  // Enable the ACK
	I2C1->CR1 |= (1<<8);  // Generate START
	while (!(I2C1->SR1 & (1<<0)));  // Wait fror SB bit to set
}

Send address

void I2C_Address (uint8_t Address)
{
/* 1. Send the Slave Address to the DR Register
2. Wait for the ADDR (bit 1 in SR1) to set. This indicates the end of address transmission
3. clear the ADDR by reading the SR1 and SR2*/
 
	I2C1->DR = Address;  //  send the address
	while (!(I2C1->SR1 & (1<<1)));  // wait for ADDR bit to set
	uint8_t temp = I2C1->SR1 | I2C1->SR2;  // read SR1 and SR2 to clear the ADDR bit
}

I2C WRITE

void I2C_Write (uint8_t data)
{
 
/* 1. Wait for the TXE (bit 7 in SR1) to set. This indicates that the DR is empty
2. Send the DATA to the DR Register
3. Wait for the BTF (bit 2 in SR1) to set. This indicates the end of LAST DATA transmission */
	
	while (!(I2C1->SR1 & (1<<7)));  // wait for TXE bit to set
	I2C1->DR = data;
	while (!(I2C1->SR1 & (1<<2)));  // wait for BTF bit to set
}

I2C Read

void I2C_Read (uint8_t Address, uint8_t *buffer, uint8_t size)
{
/* 1. If only 1 BYTE needs to be Read
	a) Write the slave Address, and wait for the ADDR bit (bit 1 in SR1) to be set
	b) the Acknowledge disable is made during EV6 (before ADDR flag is cleared) and the STOP condition generation is made after EV6
	c) Wait for the RXNE (Receive Buffer not Empty) bit to set
	d) Read the data from the DR
 
2. If Multiple BYTES needs to be read
  a) Write the slave Address, and wait for the ADDR bit (bit 1 in SR1) to be set
	b) Clear the ADDR bit by reading the SR1 and SR2 Registers
	c) Wait for the RXNE (Receive buffer not empty) bit to set
	d) Read the data from the DR 
	e) Generate the Acknowlegment by settint the ACK (bit 10 in SR1)
	f) To generate the nonacknowledge pulse after the last received data byte, the ACK bit must be cleared just after reading the 
		 second last data byte (after second last RxNE event)
	g) In order to generate the Stop/Restart condition, software must set the STOP/START bit 
	   after reading the second last data byte (after the second last RxNE event)
*/		
	
	int remaining = size;
	
/**** STEP 1 ****/	
	if (size == 1)
	{
		/**** STEP 1-a ****/	
		I2C1->DR = Address;  //  send the address
		while (!(I2C1->SR1 & (1<<1)));  // wait for ADDR bit to set
		
		/**** STEP 1-b ****/	
		I2C1->CR1 &= ~(1<<10);  // clear the ACK bit 
		uint8_t temp = I2C1->SR1 | I2C1->SR2;  // read SR1 and SR2 to clear the ADDR bit.... EV6 condition
		I2C1->CR1 |= (1<<9);  // Stop I2C
 
		/**** STEP 1-c ****/	
		while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
		
		/**** STEP 1-d ****/	
		buffer[size-remaining] = I2C1->DR;  // Read the data from the DATA REGISTER
		
	}
 
/**** STEP 2 ****/		
	else 
	{
		/**** STEP 2-a ****/
		I2C1->DR = Address;  //  send the address
		while (!(I2C1->SR1 & (1<<1)));  // wait for ADDR bit to set
		
		/**** STEP 2-b ****/
		uint8_t temp = I2C1->SR1 | I2C1->SR2;  // read SR1 and SR2 to clear the ADDR bit
		
		while (remaining>2)
		{
			/**** STEP 2-c ****/
			while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
			
			/**** STEP 2-d ****/
			buffer[size-remaining] = I2C1->DR;  // copy the data into the buffer			
			
			/**** STEP 2-e ****/
			I2C1->CR1 |= 1<<10;  // Set the ACK bit to Acknowledge the data received
			
			remaining--;
		}
		
		// Read the SECOND LAST BYTE
		while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
		buffer[size-remaining] = I2C1->DR;
		
		/**** STEP 2-f ****/
		I2C1->CR1 &= ~(1<<10);  // clear the ACK bit 
		
		/**** STEP 2-g ****/
		I2C1->CR1 |= (1<<9);  // Stop I2C
		
		remaining--;
		
		// Read the Last BYTE
		while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
		buffer[size-remaining] = I2C1->DR;  // copy the data into the buffer
	}	
	
}

Hope I helped you!

Foued

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

View solution in original post

4 REPLIES 4
Foued_KH
ST Employee

Hello @Siva Ram​ ,

For the I2C communication with sensor , please try to :

1. Enable the I2C and GPIO CLOCKS

2. Configure the Pins for I2C

3. Reset the I2C

4. Set the I2C clock

5. Enable the I2C

I2C START

void I2C_Start (void)
{
/* 1. Send the START condition 
2. Wait for the SB ( Bit 0 in SR1) to set. This indicates that the start condition is generated
*/	
	I2C1->CR1 |= (1<<10);  // Enable the ACK
	I2C1->CR1 |= (1<<8);  // Generate START
	while (!(I2C1->SR1 & (1<<0)));  // Wait fror SB bit to set
}

Send address

void I2C_Address (uint8_t Address)
{
/* 1. Send the Slave Address to the DR Register
2. Wait for the ADDR (bit 1 in SR1) to set. This indicates the end of address transmission
3. clear the ADDR by reading the SR1 and SR2*/
 
	I2C1->DR = Address;  //  send the address
	while (!(I2C1->SR1 & (1<<1)));  // wait for ADDR bit to set
	uint8_t temp = I2C1->SR1 | I2C1->SR2;  // read SR1 and SR2 to clear the ADDR bit
}

I2C WRITE

void I2C_Write (uint8_t data)
{
 
/* 1. Wait for the TXE (bit 7 in SR1) to set. This indicates that the DR is empty
2. Send the DATA to the DR Register
3. Wait for the BTF (bit 2 in SR1) to set. This indicates the end of LAST DATA transmission */
	
	while (!(I2C1->SR1 & (1<<7)));  // wait for TXE bit to set
	I2C1->DR = data;
	while (!(I2C1->SR1 & (1<<2)));  // wait for BTF bit to set
}

I2C Read

void I2C_Read (uint8_t Address, uint8_t *buffer, uint8_t size)
{
/* 1. If only 1 BYTE needs to be Read
	a) Write the slave Address, and wait for the ADDR bit (bit 1 in SR1) to be set
	b) the Acknowledge disable is made during EV6 (before ADDR flag is cleared) and the STOP condition generation is made after EV6
	c) Wait for the RXNE (Receive Buffer not Empty) bit to set
	d) Read the data from the DR
 
2. If Multiple BYTES needs to be read
  a) Write the slave Address, and wait for the ADDR bit (bit 1 in SR1) to be set
	b) Clear the ADDR bit by reading the SR1 and SR2 Registers
	c) Wait for the RXNE (Receive buffer not empty) bit to set
	d) Read the data from the DR 
	e) Generate the Acknowlegment by settint the ACK (bit 10 in SR1)
	f) To generate the nonacknowledge pulse after the last received data byte, the ACK bit must be cleared just after reading the 
		 second last data byte (after second last RxNE event)
	g) In order to generate the Stop/Restart condition, software must set the STOP/START bit 
	   after reading the second last data byte (after the second last RxNE event)
*/		
	
	int remaining = size;
	
/**** STEP 1 ****/	
	if (size == 1)
	{
		/**** STEP 1-a ****/	
		I2C1->DR = Address;  //  send the address
		while (!(I2C1->SR1 & (1<<1)));  // wait for ADDR bit to set
		
		/**** STEP 1-b ****/	
		I2C1->CR1 &= ~(1<<10);  // clear the ACK bit 
		uint8_t temp = I2C1->SR1 | I2C1->SR2;  // read SR1 and SR2 to clear the ADDR bit.... EV6 condition
		I2C1->CR1 |= (1<<9);  // Stop I2C
 
		/**** STEP 1-c ****/	
		while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
		
		/**** STEP 1-d ****/	
		buffer[size-remaining] = I2C1->DR;  // Read the data from the DATA REGISTER
		
	}
 
/**** STEP 2 ****/		
	else 
	{
		/**** STEP 2-a ****/
		I2C1->DR = Address;  //  send the address
		while (!(I2C1->SR1 & (1<<1)));  // wait for ADDR bit to set
		
		/**** STEP 2-b ****/
		uint8_t temp = I2C1->SR1 | I2C1->SR2;  // read SR1 and SR2 to clear the ADDR bit
		
		while (remaining>2)
		{
			/**** STEP 2-c ****/
			while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
			
			/**** STEP 2-d ****/
			buffer[size-remaining] = I2C1->DR;  // copy the data into the buffer			
			
			/**** STEP 2-e ****/
			I2C1->CR1 |= 1<<10;  // Set the ACK bit to Acknowledge the data received
			
			remaining--;
		}
		
		// Read the SECOND LAST BYTE
		while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
		buffer[size-remaining] = I2C1->DR;
		
		/**** STEP 2-f ****/
		I2C1->CR1 &= ~(1<<10);  // clear the ACK bit 
		
		/**** STEP 2-g ****/
		I2C1->CR1 |= (1<<9);  // Stop I2C
		
		remaining--;
		
		// Read the Last BYTE
		while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
		buffer[size-remaining] = I2C1->DR;  // copy the data into the buffer
	}	
	
}

Hope I helped you!

Foued

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Javier1
Principal

Did you placed the correct pullup resistors?

maybe they are already included in you ATH20 board?

we dont need to firmware by ourselves, lets talk

Hi Javier,

(Edit)

i am using customized sensor.

connected 4.7K Pull up resistors on SDA & SCL lines.

Sensor VCC 3.3V & GND Connected with Blue Pill.

when i test the Voltage on SDA & SCL line its fluctuating between 2.9 to 3.15

i am using CCR as Standard mode.

i tested the sensor with Arduino board and its working.

>>i tested the sensor with Arduino board and its working.

Which arduino?

I dont think thats the problem but what if your arduino has 5V I2c lines

we dont need to firmware by ourselves, lets talk