cancel
Showing results for 
Search instead for 
Did you mean: 

I2C slave action to depend on 1st byte of unspecified-length message

HF
Associate

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):

  1. M perform a simple read from S 
    1. M [Read Request - 2B] -> S[Detects Read Request and answers with 2B or more if M does not stop]
  2. M reads 3 times from Slave with different offsets (S continues to send incremented data with offset until M stops)
    1. M[Write offset 1 -> 2B] -> S[Detects/Determines offset and Transmits data correctly]
    2. M[Write offset 2 -> 2B] -> S[Detects/Determines offset and Transmits data correctly]
    3. M[Write offset 3 -> 2B] -> S[Detects/Determines offset and Transmits data correctly]

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?

 

 

 

1 REPLY 1
KDJEM.1
ST Employee

Hello @HF and welcome to the community;

 

I advise you to look at I2C_TwoBoards_ComPolling and I2C_TwoBoards_ComIT examples in STM32CubeF4 package and check the I2C configuration.

These examples guide you through the different configuration steps by mean of HAL API to ensure I2C Data buffer transmission and reception using Polling. The communication is done with 2 Boards through I2C.

Note that these examples have been tested with STM32F411E-Discovery board.

 

I hope these examples can help you.

Thank you.

Kaouthar

 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.