cancel
Showing results for 
Search instead for 
Did you mean: 

Help with i2c slave device

FMass.1
Associate III

Hi, I want to implement an I2C slave with a STM32L476 micro that basically behaves like a memory. Therefore it must respond to another micro which executes the HAL_I2C_Mem_Write() and HAL_I2C_Mem_Read() operations to this slave.

Searching on the internet I came to write this code and it seems to work.

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c) {
	printf("--> DEBUG: Write [%02X - %02X - %02X]\r\n", i2c_rx_buffer[0],
			i2c_rx_buffer[1], i2c_rx_buffer[2]);
}
 
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode) {
	if (TransferDirection == 0) {
		memset(i2c_rx_buffer, 0x00, MAX_I2C_RX_LEN);
		if (HAL_I2C_Slave_Seq_Receive_IT(EXT_I2C, i2c_rx_buffer, MAX_I2C_RX_LEN, I2C_FIRST_AND_LAST_FRAME) != HAL_OK) {
			Error_Handler();
		}
	} else {
		printf("--> Read \r\n");
		i2c_tx_buffer[0] = 0xFF;
		if (HAL_I2C_Slave_Seq_Transmit_IT(EXT_I2C, i2c_tx_buffer, MAX_I2C_RX_LEN, I2C_FIRST_AND_LAST_FRAME) != HAL_OK) {
			Error_Handler();
		}
	}
}
 
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c) {
	if(HAL_I2C_EnableListen_IT(EXT_I2C) != HAL_OK) {
		  printf("********************** ERROR START LISTEN I2C\r\n");
		  Error_Handler();
	  }
}
 
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) {
	printf("HAL_I2C_ErrorCallback\r\n");
}

But now I have two problems:

1 - How do I deal with the fact that I don't know how many bytes will be written, since I don't know how long the packet that the master sends will be, what number should I put in the HAL_I2C_Slave_Seq_Receive_IT function. I tried to make a very large buffer, but if the master sends a variable number of bytes the HAL_I2C_ErrorCallback is always called.

How can I handle receiving a variable number of bytes?

2 - During read operations, the master first sends the address it wants to read from, but like my code, how do I see this address? Before doing the HAL_I2C_Slave_Seq_Transmit_IT, I should know which address the master wants to read, but I don't understand how to do it.

Can anyone help me, thanks.

6 REPLIES 6
Foued_KH
ST Employee

Hello @FMass.1​ ,

1.To handle receiving a number of bytes, you can try :

/* Size of Reception buffer */
#define RXBUFFERSIZE                    (COUNTOF(RxBuffer) - 1)
  
/* Exported macro ------------------------------------------------------------*/
#define COUNTOF(__BUFFER__)   (sizeof(__BUFFER__) / sizeof(*(__BUFFER__)))

2.To determine the address that the master STM32L476 wants to read from during an I2C read operation, you can use the HAL_I2C_AddrCallback() function.

// Define a global variable to store the address
uint16_t slaveAddress;
 
// Implement the HAL I2C Address Callback
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
    if (TransferDirection == I2C_DIRECTION_RECEIVE)
    {
        // Save the received address
        slaveAddress = AddrMatchCode;
    }
}

Hope I helped you!

Foued

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.

FMass.1
Associate III

2 . Regarding the second question: OK. As soon as I'm in the lab I'll do the test, but I think it's the solution I was looking for.

For the first one, maybe I explained myself badly, I know how big the buffer is; I declared that buffer in main.c and MAX_I2C_RX_LEN is also a define I made. (In the specific case it is 10)

I try to rephrase my question with an example: When the master tries to write something, he calls the

HAL_I2C_Mem_Write (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t * pData, uint16_t Size, uint32_t Timeout);

function or a similar function, as you can see in this function the uint16_t Size value is inserted which is the size of the packet being sent. The question is, how does the slave read exactly that many bytes.

In my example, as I said, MAX_I2C_RX_LEN is 10, but it's a number that I put because I know it's the maximum bytes in my case the master will send.

But the master could also send shorter packets, for example he could send only 2 or 3 bytes, in all those cases HAL_I2C_ErrorCallback is always called and HAL_I2C_SlaveRxCpltCallback is never called.

How do I get the HAL_I2C_SlaveRxCpltCallback to be called even when the master sends less than 10 bytes, or anyway less of the max buffer size?

I also have another program that I have to make soon, in which the master can send packets of variable length between 3 bytes and 64 bytes, how can I handle situations like these?

FMass.1
Associate III

Yes ok, I understood that there was something that didn't happen with my code, but on all the examples I find on the internet I see that there is always this problem. The number of bytes that the master sends is always indicated.

The only thing I found, is a post that says to use the error cabblack as if it were a HAL_I2C_SlaveRxCpltCallback and ignore the errors, it still seems to me an incorrect way to do.

Can you tell me how to make an I2C slave, which can accept a variable number of bytes from the master?

you can just prepare the size of reception buffer before the reception

/* Size of Reception buffer */
#define RXBUFFERSIZE                    (COUNTOF(RxBuffer) - 1)
 
/* Exported macro ------------------------------------------------------------*/
#define COUNTOF(__BUFFER__)   (sizeof(__BUFFER__) / sizeof(*(__BUFFER__)))

Foued

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.

FMass.1
Associate III

Excuse me, but if I don't know how many bytes the master will send, how can I prepare the buffer?

Maybe in the first transfer the master could send 20 bytes, then in the second it could send 2 bytes, in the third 5 bytes and so on; not knowing how many bytes it will send, how can I prepare the buffer before receiving?

only way to solve that problem is send a byte or bytes indicating how many bytes master will transmit to the slave....