2023-12-19 08: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 01: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.