STM32 as I2C SLAVE || PART 2
This is the second tutorial in the STM32 I2C Slave series. In the previous tutorial we saw how to setup the STM32 as a slave device, and how to receive data from the master, but in a very basic manner.
Today we will modify the same project and make the data reception a little more flexible. We will see how to receive an unknown amount of data from the master without triggering the NACK response.
The setup will be same as what we did in the previous tutorial. Anyway it’s shown below.
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 these files today.
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.
HAL_I2C_EnableListen_IT(&hi2c1);
I2C_Slave.c
The changes are going to be made in the slave source file. They are as follows
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
{
// receive using sequential function.
HAL_I2C_Slave_Sequential_Receive_IT(hi2c, RxData+rxcount, 1, I2C_FIRST_FRAME);
}
else // if the master requests the data from the slave
{
Error_Handler(); // call error handler
}
}
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, the slave will start reading it.
- 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.
- The data will be stored in the RxData buffer, but the position inside the buffer is updated using the variable rxcount.
Once the slave successfully receives 1 byte data, the Rx complete callback will be called.
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
rxcount++;
if (rxcount >= RxSIZE) rxcount = 0;
HAL_I2C_Slave_Seq_Receive_IT(hi2c, RxData+rxcount, 1, I2C_NEXT_FRAME);
}
- In the Rx complete callback we increment the rxcount variable, so that the new data to be received can be stored at a new position in the buffer.
- If the rxcount exceeds the buffer size, we will again reset it to 0.
- This method allows the circular buffer behaviour, where the new received data will overwrite the old data in the buffer.
- Now we will again call the receive fuunction in the interrupt mode, to receive 1 byte of data.
- This time the Option being used is I2C_NEXT_FRAME
- The NEXT FRAME aloow to manage a sequence with a restart condition. It is generally used when the master generates a restart condition to switch from write to read.
- Here we are using the NEXT FRAME to ensure that the slave keeps receiving data without sending a stop condition.
- The received data will store in the updated position in the RxData buffer.
That is all the changes we need to make in this tutorial. The slave device should be able to receive as much data the master sends.
The only issue is, since the slave is alway looking for the data, when the master sends a stop condition, an error will trigger in the slave. This is the AF (ACK Failure) error and if we ignore it, the code works just fine.
Result
Check out the video to see the working in detail.