Building Board Support Package (BSP) for STM32F411-Nucleo64 Part14.2: I2C Full duplex Communication

In the second section of the fourteenth part of I2C BSP, we shall develop functions that will communicate with slave devices to write/read to/from MPU9250.

In this guide, we shall cover the following:

  • Updating the header file.
  • Updating the source file.
  • Main code.
  • Results.

1. Updating the header file:

In i2c_bps.h header file, include the following three functions.

The first function:

I2C_StatusTypedef BSP_I2C_Write(I2C_TypeDef *i2c, uint8_t address, uint8_t *data,uint16_t length, uint32_t timeout);

The function takes five parameters as following:

  • I2C_Typedef to indicate which i2c to be used.
  • The address of the slave device.
  • Array that holds the data to be written to the slave device.
  • Length of the data to be written.
  • Timeout for time management.

Also, the function will return the I2C status as following:

  • Success
  • Failed.
  • Timeout

The second function:

I2C_StatusTypedef BSP_I2C_Read_Memory(I2C_TypeDef *i2c, uint8_t address,uint8_t MemAddr, uint8_t *data,uint16_t length, uint32_t timeout);

This function will send the memory address to be read . It takes six parameters:

  • I2C_Typedef to indicate which i2c to be used.
  • The address of the slave device.
  • The memory address within the slave.
  • Array which holds the data to be read.
  • Length of the data to be read.
  • Timeout for time management.

The function will return the status of I2C as mentioned earlier.

The third and final function as following:

I2C_StatusTypedef BSP_I2C_Read(I2C_TypeDef *i2c, uint8_t address, uint8_t *data,uint16_t lenght, uint32_t timeout);

This function will read the slave directly without sending the memory to read first.

Hence, the update header file as following:

#ifndef I2C_BSP_H_
#define I2C_BSP_H_

#include "stm32f4xx.h"
#include "stdint.h"
#include "bsp_debug.h"
#include "bsp.h"

#define __I2C1_CLOCK_ENABLE()		RCC->APB1ENR|=RCC_APB1ENR_I2C1EN
#define __I2C2_CLOCK_ENABLE()		RCC->APB1ENR|=RCC_APB1ENR_I2C2EN
#define __I2C3_CLOCK_ENABLE()		RCC->APB1ENR|=RCC_APB1ENR_I2C3EN


typedef enum
{
	standardSpeed=0,
	fastMode=1
}I2C_MasterModeTypedef;


typedef enum
{
	Duty_2=0,
	Duty_16_9=1
}I2C_DutyModeTypedef;


typedef enum
{
	i2c_success=0,
	i2c_failed=1,
	i2c_timeOut=2
}I2C_StatusTypedef;


typedef struct
{

	uint8_t PeripheralFrequency;

	uint8_t MasterMode;

	uint8_t DutyMode;

	uint16_t RiseTime;

	uint16_t Clock;

}I2C_ConfigTypedef;


void BSP_I2C_Init(I2C_TypeDef *i2c, I2C_ConfigTypedef *config);

void BSP_I2C_Bus_Scan(I2C_TypeDef *i2c);

I2C_StatusTypedef BSP_I2C_Write(I2C_TypeDef *i2c, uint8_t address, uint8_t *data ,uint16_t length, uint32_t timeout);

I2C_StatusTypedef BSP_I2C_Read_Memory(I2C_TypeDef *i2c, uint8_t address,uint8_t MemAddr, uint8_t *data,uint16_t length, uint32_t timeout);

I2C_StatusTypedef BSP_I2C_Read(I2C_TypeDef *i2c, uint8_t address, uint8_t *data,uint16_t lenght, uint32_t timeout);



#endif /* I2C_BSP_H_ */

2. Updating the source file:

Before heading into updating the source file, please refer to these two guide:

  • Writing multiple bytes (here).
  • Reading multiple bytes (here).

The write function as following:

I2C_StatusTypedef BSP_I2C_Write(I2C_TypeDef *i2c, uint8_t address, uint8_t *data,uint16_t length, uint32_t timeout)
{

	uint32_t start_timer=BSP_Get_Ticks();

	while (i2c->SR2 & I2C_SR2_BUSY)             //wait until bus not busy
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->CR1 |= I2C_CR1_START;                   //generate start

	while (!(i2c->SR1 & I2C_SR1_SB))			 //wait until start is generated
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR = address<< 1;                 	 	 // Send slave address

	while (!(i2c->SR1 & I2C_SR1_ADDR))          //wait until address flag is set
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	(void) i2c->SR2; 						     //Clear SR2

	while (!(i2c->SR1 & I2C_SR1_TXE))           //Wait until Data register empty
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}


	for (uint16_t i=0;i<length;i++)
	{
		i2c->DR=data[i]; 						//filling buffer with command or data
		while (!(i2c->SR1 & I2C_SR1_BTF))
		{
			if (BSP_Get_Ticks()- start_timer>timeout)
			{
				return i2c_timeOut;
			}
		}
	}
	i2c->CR1 |= I2C_CR1_STOP;

	return i2c_success;
}

For the read from memory address:

I2C_StatusTypedef BSP_I2C_Read_Memory(I2C_TypeDef *i2c, uint8_t address,uint8_t MemAddr, uint8_t *data,uint16_t length, uint32_t timeout)
{

	uint32_t start_timer=BSP_Get_Ticks();

	while (i2c->SR2 & I2C_SR2_BUSY)
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->CR1|=I2C_CR1_START;

	while(!(i2c->SR1 & I2C_SR1_SB))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR=address<<1;

	while(!(i2c->SR1 & I2C_SR1_ADDR))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	(void)i2c->SR2;

	while(!(i2c->SR1&I2C_SR1_TXE))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR = MemAddr;

	while(!(i2c->SR1&I2C_SR1_TXE))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->CR1|=I2C_CR1_START;

	while(!(i2c->SR1 & I2C_SR1_SB))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR=address<<1|1;

	while(!(i2c->SR1 & I2C_SR1_ADDR))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	(void)i2c->SR2;

	i2c->CR1|=I2C_CR1_ACK;

	while(length>0U)
	{
		if(length==1U)
	{
			i2c->CR1&=~I2C_CR1_ACK;
			i2c->CR1|=I2C_CR1_STOP;
			while(!(i2c->SR1&I2C_SR1_RXNE))
			{
				if (BSP_Get_Ticks()- start_timer>timeout)
				{
					return i2c_timeOut;
				}
			}
			*data++=i2c->DR;
			break;
	}
	else
	{
		while(!(i2c->SR1&I2C_SR1_RXNE))
		{
			if (BSP_Get_Ticks()- start_timer>timeout)
			{
				return i2c_timeOut;
			}
		}
		(*data++)=i2c->DR;
		length--;

	}


	}

	return i2c_success;
}

For reading the i2c slave only without memory:

I2C_StatusTypedef BSP_I2C_Read(I2C_TypeDef *i2c, uint8_t address, uint8_t *data, uint16_t length,  uint32_t timeout)
{
	uint32_t start_timer=BSP_Get_Ticks();

	while (i2c->SR2 & I2C_SR2_BUSY)
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->CR1|=I2C_CR1_START;

	while(!(i2c->SR1 & I2C_SR1_SB))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR=address<<1|1;

	while(!(i2c->SR1 & I2C_SR1_ADDR))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	(void)i2c->SR2;

	i2c->CR1|=I2C_CR1_ACK;

	while(length>0U)
	{
		if(length==1U)
		{
			i2c->CR1&=~I2C_CR1_ACK;
			i2c->CR1|=I2C_CR1_STOP;
			while(!(i2c->SR1&I2C_SR1_RXNE))
			{
				if (BSP_Get_Ticks()- start_timer>timeout)
				{
					return i2c_timeOut;
				}
			}
			*data++=i2c->DR;
			break;
		}
	else
	{
		while(!(i2c->SR1&I2C_SR1_RXNE))
		{
			if (BSP_Get_Ticks()- start_timer>timeout)
			{
				return i2c_timeOut;
			}
		}
		(*data++)=i2c->DR;
		length--;

	}

	}

	return i2c_success;
}

Hence, the updated source file as following:

#include "i2c_bsp.h"


void BSP_I2C_Init(I2C_TypeDef *i2c, I2C_ConfigTypedef *config)
{

	i2c->CR1=I2C_CR1_SWRST;

	i2c->CR1&=~I2C_CR1_SWRST;

	i2c->CR2|= config->PeripheralFrequency;

	i2c->TRISE=config->RiseTime;

	i2c->CCR|=(config->MasterMode<<I2C_CCR_FS_Pos);

	i2c->CCR|=(config->DutyMode<<I2C_CCR_DUTY_Pos);

	if((config->Clock)==0)
	{
		float period =1.0/(float)(config->PeripheralFrequency);

		if((config->MasterMode)==standardSpeed)
		{

			uint16_t tmp = 5/period;
			i2c->CCR|=(tmp<<I2C_CCR_CCR_Pos);
		}

		else
		{
			if(config->DutyMode==Duty_2)
			{
				uint16_t tmp=5/period;
				i2c->CCR|=(tmp<<I2C_CCR_CCR_Pos);
			}

			else
			{
				uint16_t tmp=13/period;
				i2c->CCR|=(tmp<<I2C_CCR_CCR_Pos);
			}
		}


	}
	else
	{
		i2c->CCR|=(config->Clock<<I2C_CCR_CCR_Pos);
	}

	i2c->CR1|=I2C_CR1_PE;

}


void BSP_I2C_Bus_Scan(I2C_TypeDef *i2c)
{
	char data[100];
	uint8_t a=0;
	for (uint8_t i=0;i<128;i++)
	{
		i2c->CR1 |= I2C_CR1_START;
		while(!(i2c->SR1 & I2C_SR1_SB));
		i2c->DR=(i<<1|0);
		while(!(i2c->SR1)|!(I2C1->SR2)){};
		i2c->CR1 |= I2C_CR1_STOP;
		BSP_Delay(1);
		a=(i2c->SR1&I2C_SR1_ADDR);
		if (a==2)
	    {
			sprintf(data,"Found I2C device at address 0x%X (hexadecimal), or %d (decimal)\n\r",i,i);
			log_info(data);
	    }
	 }
}


I2C_StatusTypedef BSP_I2C_Write(I2C_TypeDef *i2c, uint8_t address, uint8_t *data,uint16_t length, uint32_t timeout)
{

	uint32_t start_timer=BSP_Get_Ticks();

	while (i2c->SR2 & I2C_SR2_BUSY)             //wait until bus not busy
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->CR1 |= I2C_CR1_START;                   //generate start

	while (!(i2c->SR1 & I2C_SR1_SB))			 //wait until start is generated
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR = address<< 1;                 	 	 // Send slave address

	while (!(i2c->SR1 & I2C_SR1_ADDR))          //wait until address flag is set
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	(void) i2c->SR2; 						     //Clear SR2

	while (!(i2c->SR1 & I2C_SR1_TXE))           //Wait until Data register empty
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}


	for (uint16_t i=0;i<length;i++)
	{
		i2c->DR=data[i]; 						//filling buffer with command or data
		while (!(i2c->SR1 & I2C_SR1_BTF))
		{
			if (BSP_Get_Ticks()- start_timer>timeout)
			{
				return i2c_timeOut;
			}
		}
	}
	i2c->CR1 |= I2C_CR1_STOP;

	return i2c_success;
}

I2C_StatusTypedef BSP_I2C_Read_Memory(I2C_TypeDef *i2c, uint8_t address,uint8_t MemAddr, uint8_t *data,uint16_t length, uint32_t timeout)
{

	uint32_t start_timer=BSP_Get_Ticks();

	while (i2c->SR2 & I2C_SR2_BUSY)
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->CR1|=I2C_CR1_START;

	while(!(i2c->SR1 & I2C_SR1_SB))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR=address<<1;

	while(!(i2c->SR1 & I2C_SR1_ADDR))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	(void)i2c->SR2;

	while(!(i2c->SR1&I2C_SR1_TXE))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR = MemAddr;

	while(!(i2c->SR1&I2C_SR1_TXE))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->CR1|=I2C_CR1_START;

	while(!(i2c->SR1 & I2C_SR1_SB))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR=address<<1|1;

	while(!(i2c->SR1 & I2C_SR1_ADDR))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	(void)i2c->SR2;

	i2c->CR1|=I2C_CR1_ACK;

	while(length>0U)
	{
		if(length==1U)
	{
			i2c->CR1&=~I2C_CR1_ACK;
			i2c->CR1|=I2C_CR1_STOP;
			while(!(i2c->SR1&I2C_SR1_RXNE))
			{
				if (BSP_Get_Ticks()- start_timer>timeout)
				{
					return i2c_timeOut;
				}
			}
			*data++=i2c->DR;
			break;
	}
	else
	{
		while(!(i2c->SR1&I2C_SR1_RXNE))
		{
			if (BSP_Get_Ticks()- start_timer>timeout)
			{
				return i2c_timeOut;
			}
		}
		(*data++)=i2c->DR;
		length--;

	}


	}

	return i2c_success;
}

I2C_StatusTypedef BSP_I2C_Read(I2C_TypeDef *i2c, uint8_t address, uint8_t *data, uint16_t length,  uint32_t timeout)
{
	uint32_t start_timer=BSP_Get_Ticks();

	while (i2c->SR2 & I2C_SR2_BUSY)
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->CR1|=I2C_CR1_START;

	while(!(i2c->SR1 & I2C_SR1_SB))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	i2c->DR=address<<1|1;

	while(!(i2c->SR1 & I2C_SR1_ADDR))
	{
		if (BSP_Get_Ticks()- start_timer>timeout)
		{
			return i2c_timeOut;
		}
	}

	(void)i2c->SR2;

	i2c->CR1|=I2C_CR1_ACK;

	while(length>0U)
	{
		if(length==1U)
		{
			i2c->CR1&=~I2C_CR1_ACK;
			i2c->CR1|=I2C_CR1_STOP;
			while(!(i2c->SR1&I2C_SR1_RXNE))
			{
				if (BSP_Get_Ticks()- start_timer>timeout)
				{
					return i2c_timeOut;
				}
			}
			*data++=i2c->DR;
			break;
		}
	else
	{
		while(!(i2c->SR1&I2C_SR1_RXNE))
		{
			if (BSP_Get_Ticks()- start_timer>timeout)
			{
				return i2c_timeOut;
			}
		}
		(*data++)=i2c->DR;
		length--;

	}

	}

	return i2c_success;
}

3. Main code.

In main.c file:

Include the following:

#include "bsp.h"
#include "uart_bsp.h"
#include "exti_bsp.h"
#include "bsp_debug.h"
#include "spi_bsp.h"
#include "dma_bsp.h"
#include "i2c_bsp.h"
#include "stdlib.h"

Declare i2c configuration data structure:

I2C_ConfigTypedef i2c1Config;

Declare an array of 6 bytes to hold the acceleration data:

uint8_t MPU9250_ACC_Data[6];

In main function:

	#if FPU_EN
		SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));
	#endif

	clock_config();
	BSP_Ticks_Init(100000000);

	GPIO_Configure_Typedef I2C_PB8, I2C_PB9;

	GPIOB_CLOCK_ENABLE();

	I2C_PB8.PinNumber=8;
	I2C_PB8.OutputType=Open_Drain;
	I2C_PB8.Mode=Alternate_function;
	I2C_PB8.PullUp_PullDown=PullUp;
	I2C_PB8.AlternateType=AF4;

	I2C_PB9.PinNumber=9;
	I2C_PB9.OutputType=Open_Drain;
	I2C_PB9.Mode=Alternate_function;
	I2C_PB8.PullUp_PullDown=PullUp;
	I2C_PB9.AlternateType=AF4;

	GPIO_Initialization(GPIOB,&I2C_PB8);
	GPIO_Initialization(GPIOB,&I2C_PB9);

	__I2C1_CLOCK_ENABLE();

	i2c1Config.MasterMode=standardSpeed;

	i2c1Config.PeripheralFrequency=50;

	i2c1Config.RiseTime=9;

	BSP_I2C_Init(I2C1, &i2c1Config);

In while(1) loop:

	while(1)
	{
		BSP_I2C_Read_Memory(I2C1, 0x68, 0x3B, MPU9250_ACC_Data, 6, 200);

	}

Hence, the main code as following:

#include "bsp.h"
#include "uart_bsp.h"
#include "exti_bsp.h"
#include "bsp_debug.h"
#include "spi_bsp.h"
#include "dma_bsp.h"
#include "i2c_bsp.h"
#include "stdlib.h"

void clock_config(void);

I2C_ConfigTypedef i2c1Config;

uint8_t MPU9250_ACC_Data[6];


int main()
{


	#if FPU_EN
		SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));
	#endif

	clock_config();
	BSP_Ticks_Init(100000000);

	GPIO_Configure_Typedef I2C_PB8, I2C_PB9;

	GPIOB_CLOCK_ENABLE();

	I2C_PB8.PinNumber=8;
	I2C_PB8.OutputType=Open_Drain;
	I2C_PB8.Mode=Alternate_function;
	I2C_PB8.PullUp_PullDown=PullUp;
	I2C_PB8.AlternateType=AF4;

	I2C_PB9.PinNumber=9;
	I2C_PB9.OutputType=Open_Drain;
	I2C_PB9.Mode=Alternate_function;
	I2C_PB8.PullUp_PullDown=PullUp;
	I2C_PB9.AlternateType=AF4;

	GPIO_Initialization(GPIOB,&I2C_PB8);
	GPIO_Initialization(GPIOB,&I2C_PB9);

	__I2C1_CLOCK_ENABLE();

	i2c1Config.MasterMode=standardSpeed;

	i2c1Config.PeripheralFrequency=50;

	i2c1Config.RiseTime=9;

	BSP_I2C_Init(I2C1, &i2c1Config);




	while(1)
	{
		BSP_I2C_Read_Memory(I2C1, 0x68, 0x3B, MPU9250_ACC_Data, 6, 200);

	}
}

void clock_config(void)
{
	Clock_Config_Typedef clockConfig;

	clockConfig.PLL_M= 4;
	clockConfig.PLL_N= 200;
	clockConfig.PLL_P= 4;

	clockConfig.AHB1Prescaler=AHB1_Prescaler1;
	clockConfig.APB1Prescaler=APB1_Prescaler2;
	clockConfig.APB2Prescaler=APB2_Prescaler1;

	clockConfig.clockSourc=External_Oscillator;
	clockConfig.flash_latency= Three_wait_state;

	Clock_Configuration(&clockConfig);
}

4. Results:

Upload the code to your STM32 and connect MPU9250 and you should get the following:

Happy coding 🙂

4 Comments

  • H.S.Raghavendra Rao Posted September 8, 2023 7:05 pm

    Dear Sir,
    Good Morning,
    in UART2 Serial Prinitng, we Use, function
    int __io_putchar(int ch) { uart2_write(ch); return ch; }
    where as in SWO, we use the Same function as..
    int __io_putchar(int ch) { ITM_SendChar(ch); return ch; }
    if we need both SWO output for debugging, and Serial Display using Printf(),
    simultaneously in the same program how to address, them. Please Respond,
    thanks with regards,
    HSR-Rao.

    • Husamuldeen Posted September 9, 2023 9:52 am

      Hi,
      just put ITM_SendChar(ch); after the uart and you can get both.

      • H.S.Raghavendra Rao Posted September 9, 2023 10:52 am

        Thank you sir,

  • H.S.Raghavendra Rao Posted September 11, 2023 10:02 am

    Dear Sir,
    Good morning, Suggestion Worked successfully,
    thanks, with regards,
    HSR-Rao.

Add Comment

Your email address will not be published. Required fields are marked *