2025-04-15 6:39 AM - last edited on 2025-04-29 9:56 AM by Andrew Neil
I2C slave that needs to act differently depending on the first written byte in a write request with unspecified length [1 or 3 bytes]
Hi!
I am trying to emulate MCU master to I2C slave devices using two "black-pill" MCUs (STM32F411CEU6 @100MHz) and STM32CubeIDE (STM32Cube_FW_F4 V1.28.1).
I have mainly studied the following I2C tutorials (part1 - part6): https://controllerstech.com/stm32-as-i2c-slave-part-1/
I have managed to get the following scenarios to work as expected(M: Master, S: Slave, the slave operates in interrupt mode):
However I have run into a dead-end for a solution for my third scenario...
I want to be able emulate register writes and device reads using the following high level protocol(hopefully by using existing APIs):
12C Device Write: W[1B write configuration + 2B data]
12C Device Read: W[1B read configuration] + R[12B data]
The main problem is to obtain the first byte in the potential sequence and act accordingly.
I basically end up with the following pseudo code:
static uint8_t g_hi2c3_rx_bytes = 0;
static uint8_t g_hi2c3_rx_data[3] = {0};
static uint8_t g_hi2c3_read_cfg = 0xFF; /* Invalid */
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
/* We have received a new byte (also serves as the next index) */
g_hi2c3_rx_bytes++;
/* Is it the configuration byte? */
if (g_hi2c3_rx_bytes == 1)
{
/* Act differently depending on the configuration value (the first written data byte/value) */
uint8_t w_cfg_data = g_hi2c3_rx_data[0];
if (w_cfg_data == 0x01 )
{
/* Write request (we expect 2 more bytes of data) */
HAL_I2C_Slave_Seq_Receive_IT(&hi2c3,
&g_hi2c3_rx_data[g_hi2c3_rx_bytes],
1, /* Hard coded for now */
I2C_NEXT_FRAME);
}
else /* Everything but 0x01 is considered a read request */
{
/* Save last written read cfg (to be used later) */
g_hi2c3_read_cfg = w_cfg_data;
/* Read request - send no more frames (i.e. I should already have sent I2C_LAST_FRAME)*/
// What to do here?
}
}
else
{
/* We will allways send the 'last_frame' here since we are about to receive the 3rd byte */
HAL_I2C_Slave_Seq_Receive_IT(&hi2c3,
&g_hi2c3_rx_data[g_hi2c3_rx_bytes],
1, /* Hard coded for now */
I2C_LAST_FRAME);
}
}
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
if (TransferDirection == I2C_DIRECTION_TRANSMIT)
{
/* Indicate that we have received 0 bytes */
g_hi2c3_rx_bytes = 0;
/* Invalidate read configuration */
g_hi2c3_read_cfg = 0xFF;
/* How to determine I2C_FIRST_FRAME vs I2C_FIRST_AND_LAST_FRAME? */
HAL_I2C_Slave_Seq_Receive_IT(&hi2c3,
&g_hi2c3_rx_data[g_hi2c3_rx_bytes],
1,
I2C_FIRST_FRAME /* I2C_FIRST_AND_LAST_FRAME */);
}
else
{
/* Do nothing for now */
// 'g_hi2c3_read_cfg' needs to be known (not 0xFF)
// switch (g_hi2c3_read_cfg)
// {
// case 0x00:
// do_something_read(); break;
// case 0x0F:
// do_something_else_read(); break;
// default:
// do_something_default_read(); break;
// }
}
}
I have tried to read the first 'configuration byte' immediately in 'HAL_I2C_AddrCallback' via interrupt or polling and act according but both options makes the MCU get stuck in some kind of interrupt/pending loop.
Hence I have to ask for some assistance/feedback here, how would you solve the issue at hand?