cancel
Showing results for 
Search instead for 
Did you mean: 

Sending and receiving data by I2C bus on STM32F401

ps19
Associate

Hello,

I am trying to write software to send and receive data using I2C - communication between STM and BNO055.

I have problem with that my software hangs on loops checking states on BUSY or START flags.

Based on: https://www.st.com/en/embedded-software/stsw-stm32020.html

Init:

void i2c_Init(uint16_t bus_speed)
{
	gpio_pin_cfg(GPIOC, 9, GPIO_AF4_OD_50MHz_PULL_UP);	//sda
	gpio_pin_cfg(GPIOA, 8, GPIO_AF4_OD_50MHz_PULL_UP);	//scl
 
	RCC->APB1ENR |= RCC_APB1ENR_I2C3EN;
 
	I2C3->CR1 |= I2C_CR1_SWRST; //Software reset 1
	I2C3->CR1 &= ~ I2C_CR1_SWRST; //Software reset 0
 
	I2C3->CR1 = 0;
	I2C3->CR2 = APB1_CLOCK_FREQ/1000U; //Value in Mhz ex. 42Mhz
	I2C3->CCR = (APB1_CLOCK_FREQ / (4U*1000U * bus_speed)); //APB1/(i2c_freq*2) - 400 khz;
	I2C3->TRISE = 43; //1000ns ((max t_rise[ns]) / (1/APB1_CLOCK_FREQ))+1
	I2C3->CR1 = I2C_CR1_PE;
 
	/* 1 bit for pre-emption priority, 3 bits for subpriority */
	//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
 
	// NVIC_SetPriority(I2C3_EV_IRQn, 0x00);
	NVIC_EnableIRQ(I2C3_EV_IRQn);
 
	//NVIC_SetPriority(I2C3_ER_IRQn, 0x01);
	NVIC_EnableIRQ(I2C3_ER_IRQn);
}

Function for receiving data:

void i2cReadInterrupt(I2C_TypeDef *I2Cx, uint8_t *data_buffer, uint8_t slave_address, uint8_t register_address, uint32_t lenght)
{
	//Enable I2C errors interrupts
	I2Cx->CR2 |= I2C_CR2_ITERREN;
	/* Enable EVT IT*/
	I2Cx->CR2 |= I2C_CR2_ITEVTEN;
	/* Enable BUF IT */
	I2Cx->CR2 |= I2C_CR2_ITBUFEN;
	/* Set the I2C direction to reception */
	I2CDirection = I2C_DIRECTION_RX;
	slave_address |= I2CDirection;
	Address = slave_address;
	NumbOfBytes1 = lenght;
	//Send START condition */
	I2Cx->CR1 |= I2C_CR1_START;
	/* Wait until the START condition is generated on the bus: START bit is cleared by hardware */
	while ((I2Cx->CR1 & I2C_CR1_START));
	/* Wait until BUSY flag is reset (until a STOP is generated) */
	while (!(I2Cx->SR2 & I2C_SR2_BUSY));
	/* Enable Acknowledgement to be ready for another reception */
	I2Cx->CR1 |= I2C_CR1_ACK;
}

function for sending data:

void i2cWriteInterrupt(I2C_TypeDef *I2Cx,uint8_t slave_adress, uint8_t *data, uint32_t lenght)
{
	//Enable Error IT
	I2Cx->CR2 |= I2C_CR2_ITERREN;
	/* Enable EVT IT*/
	I2Cx->CR2 |= I2C_CR2_ITEVTEN;
	/* Enable BUF IT */
	I2Cx->CR2 |= I2C_CR2_ITBUFEN;
	/* Set the I2C direction to Transmission */
	I2CDirection = I2C_DIRECTION_TX;
	slave_adress &= I2C_OAR1_ADD0_Msk;
	Address = slave_adress;
	/*if (I2Cx == I2C1) */ NumbOfBytes1 = lenght;
	/*else*//* NumbOfBytes2 = lenght;*/
	/* Send START condition */
	I2Cx->CR1 |= I2C_CR1_START;
	/* Wait until the START condition is generated on the bus: the START bit is cleared by hardware */
	while ((I2Cx->CR1 & I2C_CR1_START));
	/* Wait until BUSY flag is reset: a STOP has been generated on the bus signaling the end
    of transmission */
	while (!(I2Cx->SR2 & I2C_SR2_BUSY));
}

Interrupts:

void I2C3_EV_IRQHandler(void)
{
	/* If SB = 1, I2C3 master sent a START on the bus: EV5) */
	if ((I2C3->SR1 & I2C_SR1_SB))
	{
		/* Send the slave address for transmssion or for reception (according to the configured value
	               in the write master write routine */
		I2C3->DR = Address;
		I2C3->SR1 = 0;
		I2C3->SR2 = 0;
	}
	/* If I2C3 is Master (MSL flag = 1) */
	if (I2C3->SR2 & I2C_SR2_MSL)
	{
		/* If ADDR = 1, EV6 */
		if ((I2C3->SR1 & I2C_SR1_ADDR))
		{
			/* Write the first data in case the Master is Transmitter */
			if (I2CDirection == I2C_DIRECTION_TX)
			{
				/* Initialize the Transmit counter */
				Tx_Idx1 = 0;
				/* Write the first data in the data register */
				I2C3->DR = Buffer_Tx1[Tx_Idx1++];
				/* Decrement the number of bytes to be written */
				NumbOfBytes1--;
				/* If no further data to be sent, disable the I2C BUF IT
	                   in order to not have a TxE  interrupt */
				if (NumbOfBytes1 == 0)
				{
					I2C3->CR2 &= (uint16_t)~I2C_CR2_ITBUFEN;
				}
			}
			/* Master Receiver */
			else
			{
				/* Initialize Receive counter */
				Rx_Idx1 = 0;
				/* At this stage, ADDR is cleared because both SR1 and SR2 were read.*/
				/* EV6_1: used for single byte reception. The ACK disable and the STOP
	                   Programming should be done just after ADDR is cleared. */
				if (NumbOfBytes1 == 1)
				{
					/* Clear ACK */
					I2C3->CR1 &=~ I2C_CR1_ACK;
					/* Program the STOP */
					I2C3->CR1 |= I2C_CR1_STOP;
				}
			}
			I2C3->SR1 = 0;
			I2C3->SR2 = 0;
		}
		/* Master transmits the remaing data: from data2 until the last one.  */
		/* If TXE is set */
		if ((I2C3->SR1 & (I2C_SR1_TXE | I2C_SR1_BTF)))
		{
			/* If there is still data to write */
			if (NumbOfBytes1!=0)
			{
				/* Write the data in DR register */
				I2C3->DR = Buffer_Tx1[Tx_Idx1++];
				/* Decrment the number of data to be written */
				NumbOfBytes1--;
				/* If  no data remains to write, disable the BUF IT in order
	                   to not have again a TxE interrupt. */
				if (NumbOfBytes1 == 0)
				{
					/* Disable the BUF IT */
					I2C3->CR2 &= (uint16_t)~I2C_CR2_ITBUFEN;
				}
			}
			I2C3->SR1 = 0;
			I2C3->SR2 = 0;
		}
		/* If BTF and TXE are set (EV8_2), program the STOP */
		if ((I2C3->SR1 & I2C_SR1_BTF) && (I2C3->SR1 & I2C_SR1_TXE))
		{
			/* Program the STOP */
			I2C3->CR1 |= I2C_CR1_STOP;
			/* Disable EVT IT In order to not have again a BTF IT */
			I2C3->CR2 &= (uint16_t)~I2C_CR2_ITEVTEN;
			I2C3->SR1 = 0;
			I2C3->SR2 = 0;
		}
		/* If RXNE is set */
		if ((I2C3->SR1 & I2C_SR1_RXNE))
		{
			/* Read the data register */
			Buffer_Rx1[Rx_Idx1++] = I2C3->DR;
			/* Decrement the number of bytes to be read */
			NumbOfBytes1--;
			/* If it remains only one byte to read, disable ACK and program the STOP (EV7_1) */
			if (NumbOfBytes1 == 1)
			{
				/* Clear ACK */
				I2C3->CR1 &=~ I2C_CR1_ACK;
				/* Program the STOP */
				I2C3->CR1 |= I2C_CR1_STOP;
			}
			I2C3->SR1 = 0;
			I2C3->SR2 = 0;
		}
	}
}
void I2C3_ER_IRQHandler(void)
{
	/* Read the I2C3 status register */
	I2C3->SR1 = I2C3->SR1;
	/* If AF = 1 */
	if ((I2C3->SR1 & I2C_SR1_AF) )
	{
		I2C3->SR1 &= 0xFBFF;
		I2C3->SR1 = 0;
	}
	/* If ARLO = 1 */
	if ((I2C3->SR1 & I2C_SR1_ARLO))
	{
		I2C3->SR1 &= 0xFBFF;
		I2C3->SR1 = 0;
	}
	/* If BERR = 1 */
	if ((I2C3->SR1 & I2C_SR1_BERR))
	{
		I2C3->SR1 &= 0xFEFF;
		I2C3->SR1 = 0;
	}
	/* If OVR = 1 */
	if ((I2C3->SR1 & I2C_SR1_OVR))
	{
		I2C3->SR1 &= 0xF7FF;
		I2C3->SR1 = 0;
	}
}

Executing function for sending and receiving data in BNO055 API:

s8 BNO055_I2C_bus_read(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
    s32 BNO055_iERROR = BNO055_INIT_VALUE;
    //u8 array[I2C_BUFFER_LEN] = { BNO055_INIT_VALUE };
   // Buffer_Rx1[0] = { BNO055_INIT_VALUE };
    u8 stringpos = BNO055_INIT_VALUE;
 
    Buffer_Rx1[BNO055_INIT_VALUE] = reg_addr;
 
    i2cReadInterrupt(I2C3,0,dev_addr,reg_addr,cnt);
 
    /* Please take the below API as your reference
     * for read the data using I2C communication
     * add your I2C read API here.
     * "BNO055_iERROR = I2C_WRITE_READ_STRING(DEV_ADDR,
     * ARRAY, ARRAY, 1, CNT)"
     * BNO055_iERROR is an return value of SPI write API
     * Please select your valid return value
     * In the driver BNO055_SUCCESS defined as 0
     * and FAILURE defined as -1
     */
    for (stringpos = BNO055_INIT_VALUE; stringpos < cnt; stringpos++)
    {
        *(reg_data + stringpos) = Buffer_Rx1[stringpos];
    }
 
    return (s8)BNO055_iERROR;
}
s8 BNO055_I2C_bus_write(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
    s32 BNO055_iERROR = BNO055_INIT_VALUE;
   // u8 array[I2C_BUFFER_LEN];
    u8 stringpos = BNO055_INIT_VALUE;
 
    Buffer_Tx1[BNO055_INIT_VALUE] = reg_addr;
    for (stringpos = BNO055_INIT_VALUE; stringpos < cnt; stringpos++)
    {
    	Buffer_Tx1[stringpos + BNO055_I2C_BUS_WRITE_ARRAY_INDEX] = *(reg_data + stringpos);
    }
    i2cWriteInterrupt(I2C3,dev_addr, 0,cnt);
 
    return (s8)BNO055_iERROR;
}

2 REPLIES 2
KnarfB
Principal III

Just a remark: Be aware that the BNO055 makes use of the I2C clock stretching feature.

Observe SCL and SDA using oscilloscope/logic analyzer.

JW