Getting started with STM32L053: I2C Write Mode

In this guide, we shall see how to configure I2C in STM32L053 to write to a specific register of an I2C device (DS3231 in this case).

In this guide, we will cover the following:

  • I2C write mode.
  • I2C write code.
  • Demo.

1. I2C write mode:

In order for the I2C peripheral to write to slave device, it starts with sending the slave address with LSB (least significant bit) set to zero as shown in the picture below:

Then when the slave acknowledge that it received the address by setting the SDA line to low indicates that the slave detected the address and it is ready to receive the data.

After the acknowledgment, the master ( STM32L053 in this case) starts sending the data, the fist data which is 0x00 indicates the memory address (0x00 hex is for the seconds register), the slave acknowledge that it received data by setting the SDA line to low again and the process repeats until the stop condition is generated by setting the SCL to high followed by SDA to high.

2. I2C write code:

In this guide, we shall introduce two method of writing, one involves a memory address and another without.

For writing with memory address.

We start off by declaring a function that takes four arguments:

  • Slave address.
  • Memory address.
  • Pointer to array that holds the data to be sent.
  • Number of bytes to be sent
void i2c_write_memory(uint8_t slav_add, uint8_t memadd, uint8_t *data, uint8_t length)

Inside the function, we declare an array with size of length +1 as following:

uint8_t send_arr[length+1];
send_arr[0]=memadd;

The next we shall fill the array with the data to be sent as following:

	for (int i=1;(i<length+1);i++)
		{
		send_arr[i]=*data++;
		}
	/*Enable I2C*/
	I2C1->CR1 |=I2C_CR1_PE;

Set the slave address as following:

	/*Set slave address*/
	I2C1->CR2=(slav_add<<1);
	/*7-bit addressing*/
	I2C1->CR2&=~I2C_CR2_ADD10;

Then we set number of transfer to be length+1:

	/*Set number to transfer to length+1 for write operation*/
	I2C1->CR2|=((length+1)<<I2C_CR2_NBYTES_Pos);
	/*Set the mode to write mode*/
	I2C1->CR2&=~I2C_CR2_RD_WRN;

Then generate auto end:

	/*hardware end*/
	I2C1->CR2|=I2C_CR2_AUTOEND;

generate start condition:

	I2C1->CR2|=I2C_CR2_START;

Then declare local variable as following:

int i=0;

Then we shall go through the elements fo the array:

while(!(I2C1->ISR & I2C_ISR_STOPF))
	{
		/*Check if TX buffer is empty*/
		if(I2C1->ISR & I2C_ISR_TXE)
		{
			/*send memory address*/
			I2C1->TXDR =send_arr[i] ;
			i++;
		}
	}

Finally disable the I2C peripheral:

	/*Disable I2C*/
	I2C1->CR1 &=~I2C_CR1_PE;

For general write to i2c device

void i2c_write(uint8_t slav_add, uint8_t *data, uint8_t length)
	{
	/*Enable I2C*/
		I2C1->CR1 |=I2C_CR1_PE;
		/*Set slave address*/
		I2C1->CR2=(slav_add<<1);
		/*7-bit addressing*/
		I2C1->CR2&=~I2C_CR2_ADD10;
		/*Set number to transfer to length for write operation*/
		I2C1->CR2|=(length<<I2C_CR2_NBYTES_Pos);
		/*Set the mode to write mode*/
		I2C1->CR2&=~I2C_CR2_RD_WRN;
		/*hardware end*/
		I2C1->CR2|=I2C_CR2_AUTOEND;
		/*Generate start*/
		I2C1->CR2|=I2C_CR2_START;
		while(!(I2C1->ISR & I2C_ISR_STOPF))
		{
			/*Check if TX buffer is empty*/
			if(I2C1->ISR & I2C_ISR_TXE)
			{
				/*send memory address*/
				I2C1->TXDR =*data++ ;
			}
		}
		/*Disable I2C*/
		I2C1->CR1 &=~I2C_CR1_PE;

	}

Inside the main file:

#include "i2c.h"
#include "uart.h"
#include "stdio.h"
#include "stdlib.h"
#define slave_add (0x68)


uint8_t data[3],data_send[3];

int bcd_to_decimal(unsigned char x) {
    return x - 6 * (x >> 4);
}

int main()
	{
	uart_init();
	i2c_init(0x00708);
	while(1)
		{
		i2_read(slave_add,0x00,data,3);
		for (volatile int i=0;i<3;i++)
			{
			data[i]=bcd_to_decimal(data[i]);

			}
		printf("rtc data %d %d %d\r\n",data[2],data[1],data[0]);
		for (volatile int i=0;i<100000;i++);
		}

	}

3. Demo:

Happy coding 🙂

Add Comment

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