STM32 as I2C SLAVE || PART 4

This is the 4th tutorial in the STM32 I2C slave series. In the previous tutorial we saw how the slave was able to receive data less than or equal to the size of the RX buffer. It was also calling the function to process the received data.

Today we will work with a slightly different approach. The master will send the data, whose first byte will inform the slave about how many more bytes of data is coming. This way the slave will be ready to receive a fixed amount of data bytes and it will not face any errors.

For example, if the slave wants to send 4 bytes of data {1,2,3,4}, it will send a total of 5 bytes with first byte as 4, informing the slave that another 4 bytes are coming, and then send the 4 bytes of main data.

We have to make minor changes in the code from the PART3 tutorial.

CubeMX Setup

Above shown is the configuration for the I2C1

  • The mode is set as standard mode with the clock speed of 100000 Hz
  • The Clock No Stretch Mode is disabled, that means the Clock stretching is enabled.
  • The Primary slave address length is 7 bit and the address for the device is set to 0x12 (7 bit)
  • The STM32 I2C is capable of acting as 2 different slave devices with 2 different addresses, but it is disabled, and there will be only 1 slave.
  • We will cover more about Clock stretching and General call address detection in the upcoming tutorials.

We also need to enable the Event Interrupt and Error Interrupt in the NVIC Tab

The pinout is shown below

The pin PB6 is the SCL (Clock) pin and must be connectde to the SCL pin of the master. The pin PB7 is the SDA (Data) pin and must be connected to the SDA of the master. If you are connecting 2 similar MCUs, you can connect the same pins together. For eg- PB6 -> PB6 and PB7 -> PB7.

Some Insight into the CODE

We created separate file to write the source code for the I2C Slave. We will modify this file again.

The i2c_slave.c is in the src folder and the i2c_slave.h is in the inc folder. The image is shown below.

The main function remains the same. We put the I2C in the Listen mode.


Address Callback

int is_first_recvd = 0;

void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
	if (TransferDirection == I2C_DIRECTION_TRANSMIT)  // if the master wants to transmit the data
		if (is_first_recvd == 0)
			rxcount = 0;
			// receive using sequential function.
			HAL_I2C_Slave_Sequential_Receive_IT(hi2c, RxData+rxcount, 1, I2C_FIRST_FRAME);


The Address Callback is called when the address sent by master matches with the slave address.

  • Here we will check if the Master wants to Write the data or Read it, using the variable TransferDirection.
  • If the Master wants to write (Transmit) the data, we will check if the first byte has been received or not.
  • The rest of the code will only work if the first byte has not been received yet (if (is_first_recvd == 0)).
  • The variable rxcount keeps track of the buffer position, so we will reset it to 0. This way the new data will start storing from the beginning of the RxData buffer.
  • The slave will receive only 1 byte in the interrupt mode, and the Option is set as I2C_FIRST_FRAME.
  • The FIRST FRAME option allow to manage a sequence with start condition, and is generally used when the slave receives the fresh new byte.

Once the slave successfully receives 1 byte data, the Rx complete callback will be called.

Receive Callback

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
	if (is_first_recvd == 0)
		is_first_recvd = 1;
		HAL_I2C_Slave_Seq_Receive_IT(hi2c, RxData+rxcount, RxData[0], I2C_LAST_FRAME);

		rxcount = rxcount+RxData[0];
  • In the receive callback, we will again check if the first byte has been received or not.
  • If this is the first byte we are receiving, then increment the rxcount variable, so that the data can be stored at the very next position in the RX buffer.
  • Set the is_first_recvd to 1, so that this loop does not run again.
  • As I mentioned earlier, the first byte sent by the master represents how many main data bytes it is sending next.
    • Here the slave will receive the number of bytes as mentioned in the first data byte.
    • The slave uses the option of the LAST FRAME, so that the reception will end here itself.

After receiving the final data byte, the callback will be called again but the variable is_first_recvd is 1 this time. So the lese condition will run.

We will update the rxcount variable, which represents how much total data has been received so far. Then reset the variable is_first_recvd to 0, so that the entire process can start from the beginning. And finally call the function to process the data.

Process Data

Here I will just give an example about how to extract the actual data from the buffer. You can later use that data in anyway possible.

void process_data (void)
	//do something here
	// memcpy (mainbuf, RxData+1, rxcount-1);

You can use the memcpy fucntion to copy the data from the RX buffer into some other buffer (mainbuf).

The start position for the data in the RX buffer is at the offset of 1, as the 0th position is occupied by the information byte, which informs how many bytes of actual data is there.

The variable rxcount is equal to the total number of data bytes received in the RX buffer. This consists of the information byte + actual data bytes. This means the total number of actual data bytes = rxcount-1.

The main data will be stored in the buffer mainbuf. Later you can use this data as per the requirement.


Below is the image showing the data received in the debugger.

As shown in the above picture, the master sent the 3 bytes [2,10,11]. The first byte ‘2’ is the information byte, which informs that 2 main data bytes are coming next.

The slave also receives them in the same order. The RxData[0] contains the information byte and the main data starts from the RxData[1].

The rxcount has the value 3 as the slave has received a total of 3 bytes.

Check out the Video Below


You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

Leave a Reply

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

Fill out this field
Fill out this field
Please enter a valid email address.


Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add to your ad blocking whitelist or disable your adblocking software.