Getting Started with STM32F103: I2C Write Multiple Bytes

In the previous guide (here), we saw how to read three different register from a slave device (DS3231 RTC) and display the time stored in the RTC. In this guide, we shall see how to write multiple bytes.

In this guide, we shall cover the following:

  • I2C write multiple bytes.
  • I2C write multiple bytes code.
  • Code.
  • Demo.

1. I2C write multiple bytes:

To write multiple byte for a slave device, we start off by sending start condition then wait to start to be generated then send the slave address and wait for the address to set then sending the memory location (0x00 in our case) then the reset of the data as shown in figure below

With each byte transfer, we need to wait for Byte Transfer Finished (BTF in SR1 register).

2. I2C write multiple bytes code:

In i2c.c source file, we shall declare the following new function:

void i2c1_writeMemoryMulti(uint8_t saddr,uint8_t maddr, uint8_t *data, uint8_t length)

The function takes four arguments:

  • Slave address.
  • Memory address.
  • Pointer to a buffer which hold the data to be written.
  • Length of the buffer.

Within the function, we start off by waiting until the bus is free:

while (I2C1->SR2 & I2C_SR2_BUSY);           //wait until bus not busy

After the bus is freed, send the start condition:

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

Wait until the start is generated:

while (!(I2C1->SR1 & I2C_SR1_SB)){;}		//wait until start is generated

After the start is generated, send the slave address shifted to left by 1 bit.

The reason behind the shifting is the address takes the bit1 to bit7 from the data register and bit0 is used for read/write operation. When bit0 is 0, it the operation is write operation and when it is 1, it means read operation.

I2C1->DR = saddr<< 1;                 	 	// Send slave address

Wait until the address is set:

while (!(I2C1->SR1 & I2C_SR1_ADDR)){;}       //wait until address flag is set

Clear SR2 register:

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

Wait until transmit buffer is empty by checking TXE bit in SR1 register:

while (!(I2C1->SR1 & I2C_SR1_TXE));           //Wait until Data register empty

Send the memory address:

I2C1->DR = maddr;                      		// send memory address

Wait until transmit buffer is empty by checking TXE bit in SR1 register:

while (!(I2C1->SR1 & I2C_SR1_TXE));           //wait until data register empty

Now, using for loop, we shall send the data one after the other:

Within the loop, waiting until byte is transmitted by checking BTF bit in SR1 register:

for (uint8_t i=0;i<length;i++)
	{
		I2C1->DR=data[i]; 													//filling buffer with command or data
		while (!(I2C1->SR1 & I2C_SR1_BTF));
	}

Once all the data have been transmitted, send the stop condition:

I2C1->CR1 |= I2C_CR1_STOP;

Hence, the function as following:

void i2c1_writeMemoryMulti(uint8_t saddr,uint8_t maddr, uint8_t *data, uint8_t length)
{
	while (I2C1->SR2 & I2C_SR2_BUSY);           //wait until bus not busy
	I2C1->CR1 |= I2C_CR1_START;                 //generate start
	while (!(I2C1->SR1 & I2C_SR1_SB)){;}		//wait until start is generated
	I2C1->DR = saddr<< 1;                 	 	// Send slave address
	while (!(I2C1->SR1 & I2C_SR1_ADDR)){;}       //wait until address flag is set
	(void) I2C1->SR2; 						     //Clear SR2
	while (!(I2C1->SR1 & I2C_SR1_TXE));           //Wait until Data register empty
	I2C1->DR = maddr;                      		// send memory address
	while (!(I2C1->SR1 & I2C_SR1_TXE));           //wait until data register empty
	for (uint8_t i=0;i<length;i++)
	{
		I2C1->DR=data[i]; 													//filling buffer with command or data
		while (!(I2C1->SR1 & I2C_SR1_BTF));
	}
	I2C1->CR1 |= I2C_CR1_STOP;
}

Also, update the header file by adding the following function:

void i2c1_writeMemoryMulti(uint8_t saddr,uint8_t maddr, uint8_t *data, uint8_t length);

3. Code:

You may download the source code from here:

4. Demo:

Happy coding 🙂

Add Comment

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