Skip to main content
Explorer II
December 19, 2023
Question

STM32H503RB I3C target equivalent of HAL_I3C_Ctrl_MultipleTransfer_IT

  • December 19, 2023
  • 1 reply
  • 1813 views

Hello,

Currently I'm playing around with the I3C peripheral and I try to implement a sort of "eeprom" interface on a I3C target where I have 10 slots which I want to be able to address separately.

So on the controller side I have this function:

 

 

int i3cc_target_read(int slot_id, uint8_t *buffer)
{
 static uint8_t tx_buffer[I3C_SLOT_ADDRESS_LENGTH];
 static uint8_t rx_buffer[I3C_SLOT_DATA_LENGTH_LENGTH + I3C_SLOT_BUFFER_LENGTH];

 if (slot_id < 0 || slot_id >= I3C_SLOT_COUNT)
 return -1;

 if (_state != I3CS_IDLE)
 return -2;

 // prepare the tx frame buffer
 tx_buffer[I3C_SLOT_ADDRESS_OFFSET] = slot_id;

 // build the I3C transaction
 uint32_t ctrl_buffer[16];

 I3C_PrivateTypeDef private_messages[2] =
 {
 {
 .TargetAddr = _targets[0].dynamic_address,
 .TxBuf = { tx_buffer, sizeof(tx_buffer) },
 .RxBuf = { NULL, 0 },
 .Direction = HAL_I3C_DIRECTION_WRITE
 },
 {
 .TargetAddr = _targets[0].dynamic_address,
 .TxBuf = { NULL, 0 },
 .RxBuf = { rx_buffer, sizeof(rx_buffer) },
 .Direction = HAL_I3C_DIRECTION_READ
 }
 };

 I3C_XferTypeDef transfer = {
 .CtrlBuf = { ctrl_buffer, 2 },
 .StatusBuf = { NULL, 0 },
 .TxBuf = { tx_buffer, sizeof(tx_buffer) },
 .RxBuf = { rx_buffer, sizeof(rx_buffer) }
 };

 HAL_StatusTypeDef status = HAL_I3C_AddDescToFrame(
 &hi3c1,
 NULL,
 private_messages,
 &transfer,
 transfer.CtrlBuf.Size,
 I3C_PRIVATE_WITH_ARB_RESTART);

 if (status != HAL_OK)
 {
 printf("! ERROR ! HAL_I3C_AddDescToFrame failed with status: %d\r\n", status);
 return -3;
 }

 status = HAL_I3C_Ctrl_MultipleTransfer_IT(&hi3c1, &transfer);

 if (status != HAL_OK)
 {
 printf("! ERROR ! HAL_I3C_Ctrl_MultipleTransfer_IT failed with status: %d\r\n", status);
 return -4;
 }

 while (HAL_I3C_GetState(&hi3c1) == HAL_I3C_STATE_BUSY_TX_RX)
 ;

 // copy the received data into the destination buffer
 memset(buffer, 0, I3C_SLOT_BUFFER_LENGTH);
 memcpy(buffer, &rx_buffer[1], rx_buffer[0]);

 return 0;
}

 

 

 

On the target side I have the following state process handling this request:

 

 

int i3ct_poll(void)
{
 switch (_state)
 {
 case I3CS_WAITING_FOR_ADDRESS:
 {
 if (_got_address)
 {
 DBG_PRINTF("goto state idle\r\n");

 _state = I3CS_IDLE;
 }
 } break;

 case I3CS_IDLE:
 {
 // check if we must send an inband interrupt
 if (_irq_pending)
 {
 // mark the interrupt as not send
 _irq_send = 0;

 // queue the interrupt
 HAL_StatusTypeDef status = HAL_I3C_Tgt_IBIReq_IT(&hi3c1, _irq_data, _irq_data_len);
 if (status != HAL_OK)
 {
 DBG_PRINTF("! ERROR ! failed to send interrupt (status: %d)\r\n", status);
 break;
 }

 DBG_PRINTF("sending inband interrupt\r\n");

 // change the state
 _irq_pending = 0;
 _state = I3CS_INTERRUPTING;
 }
 else
 {
 // reset the receive buffer
 memset(_rx_buffer, 0, sizeof(_rx_buffer));
 memset(_tx_buffer, 0, sizeof(_tx_buffer));

 // prepare the receive descriptor
 _transfer.RxBuf.pBuffer = _rx_buffer;
 _transfer.RxBuf.Size = sizeof(_rx_buffer);

 // start the receive process
 HAL_StatusTypeDef status = HAL_I3C_Tgt_Receive_IT(&hi3c1, &_transfer);
 if (status != HAL_OK)
 {
 DBG_PRINTF("! ERROR ! HAL_I3C_Tgt_Receive_IT failed with status %d\r\n", status);
 break;
 }

 DBG_PRINTF("started listening for incoming data\r\n");

 _state = I3CS_WAITING_FOR_DATA;
 }
 } break;

 case I3CS_WAITING_FOR_DATA:
 {
 if (_irq_pending)
 {
 HAL_StatusTypeDef status = HAL_I3C_Abort_IT(&hi3c1);
 if (status != HAL_OK)
 DBG_PRINTF("! ERROR ! HAL_I3C_Abort_IT failed with status %d\r\n", status);
 else
 DBG_PRINTF("aborting receive to interrupt\r\n");

 _state = I3CS_ABORTING;
 break;
 }

 HAL_I3C_StateTypeDef state = HAL_I3C_GetState(&hi3c1);
 if (state == HAL_I3C_STATE_BUSY_RX || state == HAL_I3C_STATE_BUSY_TX)
 break;

 // decode the received frame
 int slot_id = _rx_buffer[I3C_SLOT_ADDRESS_OFFSET];
 int length = _rx_buffer[I3C_SLOT_DATA_LENGTH_OFFSET];

 // copy the data to the appropriate slot
 memset(_slots[slot_id].data, 0, sizeof(_slots[slot_id].data));
 memcpy(_slots[slot_id].data, &_rx_buffer[I3C_SLOT_BUFFER_OFFSET], length);

 DBG_PRINTF("received data (slot: %d - length: %d - message: %*s)\r\n", slot_id, length, length, &_rx_buffer[2]);

 _state = I3CS_IDLE;
 } break;

 case I3CS_INTERRUPTING:
 {
 if (! _irq_send)
 break;

 DBG_PRINTF("interrupt send, goto state idle\r\n");

 _state = I3CS_IDLE;
 } break;

 case I3CS_ABORTING:
 {
 if (HAL_I3C_GetState(&hi3c1) == HAL_I3C_STATE_ABORT)
 break;

 DBG_PRINTF("abort finished, goto state idle\r\n");

 _state = I3CS_IDLE;
 } break;
 }

 return 0;
}

void HAL_I3C_ErrorCallback(I3C_HandleTypeDef *hi3c)
{
 printf("%s\r\n", __FUNCTION__);

 if (hi3c->ErrorCode == HAL_I3C_ERROR_DATA_HAND_OFF)
 {
 printf("HAL_I3C_ERROR_DATA_HAND_OFF\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_DATA_NACK)
 {
 printf("HAL_I3C_ERROR_DATA_NACK\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_ADDRESS_NACK)
 {
 printf("HAL_I3C_ERROR_ADDRESS_NACK\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_COVR)
 {
 printf("HAL_I3C_ERROR_COVR\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_DOVR)
 {
 printf("HAL_I3C_ERROR_DOVR\r\n");

 _irq_send = 1;
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_STALL)
 {
 printf("HAL_I3C_ERROR_STALL\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_DMA)
 {
 printf("HAL_I3C_ERROR_DMA\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_TIMEOUT)
 {
 printf("HAL_I3C_ERROR_TIMEOUT\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_DMA_PARAM)
 {
 printf("HAL_I3C_ERROR_DMA_PARAM\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_INVALID_PARAM)
 {
 printf("HAL_I3C_ERROR_INVALID_PARAM\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_SIZE)
 {
 printf("HAL_I3C_ERROR_SIZE\r\n");

 // test if only 1 byte was written
 if ((hi3c->Instance->SR & I3C_SR_XDCNT_Msk) == 1)
 {
 // decode the received frame
 int slot_id = _rx_buffer[I3C_SLOT_ADDRESS_OFFSET];

 // copy the data from the appropriate slot
 _tx_buffer[0] = _slots[slot_id].length;
 memcpy(&_tx_buffer[1], _slots[slot_id].data, sizeof(_slots[slot_id].data));

 // prepare the transmit descriptor
 _transfer.TxBuf.pBuffer = _tx_buffer;
 _transfer.TxBuf.Size = sizeof(_tx_buffer);

 // start the transmit process
 HAL_StatusTypeDef status = HAL_I3C_Tgt_Transmit_IT(&hi3c1, &_transfer);
 if (status != HAL_OK)
 DBG_PRINTF("! ERROR ! HAL_I3C_Tgt_Transmit_IT failed with status %d\r\n", status);
 else
 DBG_PRINTF("started sending data for slot %d\r\n", slot_id);
 }
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_NOT_ALLOWED)
 {
 printf("HAL_I3C_ERROR_NOT_ALLOWED\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_DYNAMIC_ADDR)
 {
 printf("HAL_I3C_ERROR_DYNAMIC_ADDR\r\n");
 }
 else if (hi3c->Mode == HAL_I3C_MODE_CONTROLLER)
 {
 if (hi3c->ErrorCode == HAL_I3C_ERROR_CE0)
 {
 printf("! ERROR ! illegal formatted CCC\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_CE1)
 {
 printf("! ERROR ! arbitrary loss\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_CE2)
 {
 printf("! ERROR ! broadcast address NACKed\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_CE3)
 {
 printf("! ERROR ! role swap failure\r\n");
 }
 else
 {
 printf("! ERROR ! 0x%08lx\r\n", hi3c->ErrorCode);
 }
 }
 else if (hi3c->Mode == HAL_I3C_MODE_TARGET)
 {
 if (hi3c->ErrorCode == HAL_I3C_ERROR_TE0)
 {
 printf("! ERROR ! invalid broadcast address\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_TE1)
 {
 printf("! ERROR ! invalid CCC code\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_TE2)
 {
 printf("! ERROR ! write parity error\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_TE3)
 {
 printf("! ERROR ! DAA parity error\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_TE4)
 {
 printf("! ERROR ! missing broadcast after reset\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_TE5)
 {
 printf("! ERROR ! illegal formatted CCC\r\n");
 }
 else if (hi3c->ErrorCode == HAL_I3C_ERROR_TE6)
 {
 printf("! ERROR ! arbitrary loss\r\n");
 }
 else
 {
 printf("! ERROR ! 0x%08lx\r\n", hi3c->ErrorCode);
 }
 }
 else
 {
 printf("! ERROR ! 0x%08lx\r\n", hi3c->ErrorCode);
 }
}

 

 

 

Now in the error handler   i try to start the transfer belonging to that slot. But probably it finishes to late the peripheral already NACK's the read. Would it be possible to implement a HAL_I3C_Tgt_ReceiveTransmit_IT something?

 

PS. I tried removing the printf's in the interrupts to make it faster, but with no success.

 

PS2: while looking with the logic analyzer I see why it's not working. The TRIGGER_TRT high is when the interrupt / ErrorCallback is executed. So it's way to long and way to late for what I want to accomplish. I'll try and create my own function...

NickvanIJzendoorn_0-1703057876541.png

 

    This topic has been closed for replies.

    1 reply

    ST Employee
    February 7, 2024

    Hello @Nick van IJzendoorn , 

    And if you try with DMA instead of the IT mode ?
    Also remove the printf's in the target side .

    Let me know if it works !
    Foued