Join our subscribers list to get the latest news, updates and special offers directly in your inbox
Overview
In I2C communication, one device acts as the master and initiates the communication, while the other devices act as slaves and respond to the master's commands. The master generates the clock signal and controls the data transfer, while the slaves respond to the commands sent by the master.
Each device on the bus is identified by a unique 7-bit or 10-bit address, which is used by the master to address the slave device for data transfer. It also supports different data transfer formats such as byte mode, page mode, and sequential mode. The I2C protocol also includes error detection and correction mechanisms such as ACK/NACK signals and CRC checks to ensure reliable data transfer. In this article, master-slave code implementation is covered in detail.
Image shared below is explaining the flow chart of the code.
Each module detail for I2C Master is as below:
Each modules details for I2C Slave is as below:
I2C Initialization
Note: The code examples are just a basic example to get started with I2C communication
The following code examples are written in C language and assume that the I2C peripheral is connected to an external I2C device with a 7-bit slave address of 0x50.
Sample Code for I2C Master:
/*Include all the header files as per the selected controller*/ /*In current example STM32F4 Microcontroller is used as a master device*/ #include "stm32f4xx.h" #include "stm32f4xx_i2c.h" #include "stm32f4xx_rcc.h" #define I2C_SPEED 100000 /*100 KHz*/ #define I2C_SLAVE_ADDRESS 0xA0 /*Slave address would be mentioned in the Datasheet of the device working as a Slave*/ I2C_InitTypeDef I2C_InitStruct; void I2C1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; /*Enable the I2C peripheral clock*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); /*Enable GPIO peripheral clock*/ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // Configure the GPIO pins for I2C1 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStruct); /*Connect I2C1 pins to the alternate function (AF4)*/ GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); /*Initialize I2C1 peripheral*/ I2C_DeInit(I2C1); I2C_InitStruct.I2C_ClockSpeed = I2C_SPEED; I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStruct.I2C_OwnAddress1 = 0x00; I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Init(I2C1, &I2C_InitStruct); /*Enable I2C1 peripheral*/ I2C_Cmd(I2C1, ENABLE); } void I2C1_StartTransmission(uint8_t slaveAddr, uint8_t *buffer, uint8_t length, uint8_t stopBit) { // Wait until I2C1 is not busy while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // Send start condition I2C_GenerateSTART(I2C1, ENABLE); // Wait for start condition sent successfully while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // Send slave address I2C_Send7bitAddress(I2C1, slaveAddr, I2C_Direction_Transmitter); // Wait for slave address sent successfully while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // Send data for (uint8_t i = 0; i < length; i++) { I2C_SendData(I2C1, buffer[i]); // Wait for data sent successfully while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); } if (stopBit) { /*Send stop condition*/ I2C_GenerateSTOP(I2C1, ENABLE); } } void I2C1_ReadData(uint8_t slaveAddr, uint8_t *buffer, uint8_t length) { /*Wait until I2C1 is not busy */ while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); /* Send start condition */ I2C_GenerateSTART(I2C1, ENABLE); /* Wait for start condition sent successfully */ while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); /* Send slave address*/ I2C_Send7bitAddress(I2C1, slaveAddr, I2C_Direction_Receiver); /* Wait for slave address sent successfully */ while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); /* Read data */ for (uint8_t i = 0; i < length; i++) { if (i == (length - 1)) { /* Disable Acknowledgement */ I2C_AcknowledgeConfig(I2C1, DISABLE); } /* Wait for data received successfully */ while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); buffer[i] = I2C_ReceiveData(I2C1); } /* Enable Acknowledgement */ I2C_AcknowledgeConfig(I2C1, ENABLE); /* Send stop condition */ I2C_GenerateSTOP(I2C1, ENABLE); } int main (void) { uint8_t data[2]; /*Initialize I2C1*/ I2C1_Init (); /*Read data from the sensor*/ I2C1_StartTransmission (I2C_SLAVE_ADDRESS, data, 2, 1); /*Process the received data*/ /*Write data to the sensor*/ data[0] = 0x01; data[1] = 0x02; I2C1_StartTransmission (I2C_SLAVE_ADDRESS, data, 2, 1); while (1); return 0; }
Sample Code for I2C Slave:
#include "stm32f10x.h" /*Slave Address, would be different for each slave device*/ #define I2C_SLAVE_ADDRESS 0x50 void I2C1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; I2C_InitTypeDef I2C_InitStruct; /*Enable the I2C & GPIO peripheral clock*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); /*GPIO Configuration*/ GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(GPIOB, &GPIO_InitStruct); /*I2C Configuration*/ I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStruct.I2C_OwnAddress1 = I2C_SLAVE_ADDRESS << 1; I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStruct.I2C_ClockSpeed = 100000; I2C_Init(I2C1, &I2C_InitStruct); /*Enable I2C*/ I2C_Cmd(I2C1, ENABLE); } int main(void) { uint8_t data[2]; /*I2C Initialization*/ I2C1_Init(); while (1) { /*Wait for the start condition*/ while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED)); /*Check the data*/ for (uint8_t i = 0; i < 2; i++) { while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_RECEIVED)); data[i] = I2C_ReceiveData(I2C1); } // Process data /*Wait for stop condition*/ while (!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_STOP_DETECTED)); I2C_ClearFlag(I2C1, I2C_FLAG_STOPF); } return 0; }
In conclusion, the I2C communication protocol is a widely used standard for communication between multiple devices in embedded systems. In master-slave mode, one device acts as the master and initiates the communication, while the other devices act as slaves and respond to the master's requests. With the help of the code example provided, user can get started with I2C communication and build more complex applications that require multiple devices to communicate with each other.
EmbeddedWala
EmbeddedWala Apr 27, 2023 0 19.1K
EmbeddedWala Jun 14, 2023 0 18.8K
EmbeddedWala Apr 26, 2023 0 17.0K
EmbeddedWala Aug 30, 2022 0 15.2K
EmbeddedWala Apr 27, 2023 0 15.1K
EmbeddedWala Jun 19, 2022 0 4.5K
This site uses cookies. By continuing to browse the site you are agreeing to our use of cookies Find out more here