2023-12-19 8:46 AM - edited 2023-12-19 11:41 PM
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...
2024-02-07 1:14 AM
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
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.
We’re moving the ST Community to a new platform to give you a better and more reliable community experience.