Getting Started with STM32F103: I2C Write Byte

In the previous guide (here), we took a look at the i2c protocol and how to use the i2c to scan for the slave addresses. In this guide, we shall transmit data over i2c to slave device.

In this guide, we shall cover the following:

  • I2C write byte with memory address.
  • I2C write byte without memory address.
  • Code.
  • Results.

1. I2C write byte with memory address:

Open i2c.c source file and declare the following function:

void i2c1_MemoryWrite_Byte(uint8_t saddr, uint8_t maddr, uint8_t data)

The function takes three arguments:

  • Slave address.
  • Memory address.
  • Data to be send.

Within the function:

We start by waiting until the bus is free by checking busy bit in SR2 register:

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

After the bus is freed, send the start condition by setting bit start in CR1 to 1 as following:

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

Wait until the start is generated by check SB bit in SR1 register:

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

Then 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 with write operation*/

Wait until the address is matched by checking ADDR bit in SR1 register:

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

Clear the SR2 register (recommended):

(void)I2C1->SR2; 						/*clear SR2 by reading it */

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 TXE bit to set:

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

Send the data:

I2C1->DR = data; 						/*Send the data*/

Wait until transfer is completed by checking BTF bit in SR1 register:

while (!(I2C1->SR1 & I2C_SR1_BTF));      /*wait until transfer finished*/

Finally, send the stop condition by setting stop bit in CR1 to 1:

I2C1->CR1 |=I2C_CR1_STOP;				/*Generate Stop*/

Update the header file with the new function:

void i2c1_MemoryWrite_Byte(uint8_t saddr,uint8_t maddr, uint8_t data);

Hence, the entire function as following:

void i2c1_MemoryWrite_Byte(uint8_t saddr, uint8_t maddr, uint8_t data)
{
	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 bit is set*/
	I2C1->DR = saddr<< 1;                 	 	/* Send slave address with write operation*/
	while(!(I2C1->SR1&I2C_SR1_ADDR)){;}      	/*wait until address flag is set*/
	(void)I2C1->SR2; 						/*clear SR2 by reading it */
	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*/
	I2C1->DR = data; 						/*Send the data*/
	while (!(I2C1->SR1 & I2C_SR1_BTF));      /*wait until transfer finished*/
	I2C1->CR1 |=I2C_CR1_STOP;				/*Generate Stop*/
}

2. I2C write byte without memory address:

The function is similar to the with memory. However, minus the section of sending the memory address.

Hence the function as following:

void i2c1_Write_Byte(uint8_t saddr, uint8_t data)
{
	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 bit is set*/
	I2C1->DR = saddr<< 1;                 	 /* Send slave address with write operation*/
	while(!(I2C1->SR1&I2C_SR1_ADDR)){;}      /*wait until address flag is set*/
	(void)I2C1->SR2; 						/*clear SR2 by reading it */
	while(!(I2C1->SR1&I2C_SR1_TXE)){;}       /*Wait until Data register empty*/
	I2C1->DR = data;                        /* send memory address*/
	while (!(I2C1->SR1 & I2C_SR1_BTF));      /*wait until transfer finished*/
	I2C1->CR1 |=I2C_CR1_STOP;				/*Generate Stop*/
}

3. Code:

You may download the entire code from here:

4. Results:

Since this guide uses ds3231 as slave device, we shall 0x05 to the second register as following:

i2c1_MemoryWrite_Byte((uint8_t)0x68,(uint8_t)0x00,0x05);

Using logic analyzer, you should get the following results:

Happy coding 🙂

Add Comment

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