2023-12-19 08:03 AM - edited 2023-12-19 08:04 AM
Hello,
I'm trying to implement an I3C target device which sends inband interrupts when I push the blue Nucleo button.
But I also want it to be ready to receive data via a private message.
To do this I call `HAL_I3C_Tgt_Receive_IT` after receiving a dynamic address. But to send an inband interrupt I must abort this action and before being able to call `HAL_I3C_Tgt_IBIReq_IT`.
The problem I'm facing is that the abort only finalizes if the controller tries to send a private command. My expectation is that `HAL_I3C_Abort_IT` would abort the pending receive action right away so I can send an inband interrupt.
Is there any way to realize this by modifying the HAL library? Or am I using the HAL library in a faulty manner?
The state process I'm currently using to implement the target.
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));
// 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;
}
if (HAL_I3C_GetState(&hi3c1) == HAL_I3C_STATE_BUSY_RX)
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;
}
The flow between the controller and the target is now as followed, I don't know if this gives a good impression of what is going on...: (lines starting with > are commands given via the console)
On the controller:
I3C-Controller v0.1.0
build on: Dec 19 2023 15:30:05
compiler: 11.3.1 20220712
console: initialized
rcc: reset causes
- PINRSTF
i3cc: assigning dynamic addresses
HAL_I3C_TgtReqDynamicAddrCallback
HAL_I3C_CtrlDAACpltCallback
i3cc: dynamic assigned targets:
i3cc: 0 = (manufacturer_id: 0x0104 - part_id: 0x1381 - dev_id: 0xc6)
On the target:
I3C-Target v0.1.0
build on: Dec 19 2023 16:29:21
compiler: 11.3.1 20220712
console: initialized
rcc: reset causes
- PINRSTF
HAL_I3C_NotifyCallback
EVENT_ID_DAU (address: 0x30)
i3ct: goto state idle
i3ct: started listening for incoming data
On the controller:
> send 1 test
HAL_I3C_CtrlTxCpltCallback
successfully stored the message on the target device
message: "test"
On the target:
HAL_I3C_ErrorCallback
HAL_I3C_ERROR_SIZE
i3ct: received data (slot: 1 - length: 4 - message: test)
i3ct: started listening for incoming data
<<< PRESSED THE BLUE NUCLEO BUTTON >>>
i3ct: aborting receive to interrupt
On the controller: (this is against my expectation, i would expect that this transfer should not be possible)
> send 1 test
HAL_I3C_CtrlTxCpltCallback
successfully stored the message on the slave device
message: "test"
On the target:
HAL_I3C_AbortCpltCallback
i3ct: abort finished, goto state idle
HAL_I3C_ErrorCallback
HAL_I3C_ERROR_DOVR
i3ct: sending inband interrupt
On the controller:
> send 1 test
HAL_I3C_ErrorCallback
HAL_I3C_ERROR_ADDRESS_NACK
successfully stored the message on the slave device
message: "test"
HAL_I3C_NotifyCallback
EVENT_ID_IBI
EVENT_ID_IBI
i3cc: IBI (address: 0x30 - len: 4 - payload: 0x12345678)
On the target:
i3ct: interrupt send, goto state idle
i3ct: started listening for incoming data
2023-12-20 01:45 AM
Hello @Nick van IJzendoorn ,
I suggest to check the available example in the STM32CubeFW :
STM32Cube_FW_H5_V1.1.1\Projects\NUCLEO-H503RB\Examples\I3C
* I3C_Controller_InBandInterrupt_IT
* I3C_Target_InBandInterrupt_IT
For more details about I3C communication you can take a look on the I3C AN :
st.com/resource/en/application_note/an5879-introduction-to-i3c-for-stm32h5-series-mcu-stmicroelectronics.pdf
Br,
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.
2023-12-20 08:14 AM
Yeah the example works perfectly, the problem is that if you are a target you have a receive or transmit action active when you are in "idle" because you want to be accessible by the controller.
You must abort this action before being able to send an inband interrupt. (According to the HAL)
This abort action is failing or not working as expected.
2023-12-21 01:18 AM
Hello @Nick van IJzendoorn ,
I will check and get back to you ASAP!
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.
2023-12-25 03:25 AM
Hello @Nick van IJzendoorn ,
Controller side:
In a first step :
the Controller initiate the sending of the ENTDAA CCC Command through HAL_I3C_Ctrl_DynAddrAssign_IT()
to I3C Targets which receive the Command and treat it by sending its own payload.
Then when ENTDAA is terminated thanks to reception of the completion callback HAL_I3C_CtrlDAACpltCallback(),
the controller store the Target capabilities in the peripheral hardware register side through HAL_I3C_Ctrl_ConfigBusDevices().
Then Controller wait in a no activity loop.
Then, at reception of an In-Band-Interrupt event request from a Target, the I3C Controller retrieve Target Dynamic Address with associated data if any.
The Controller In-Band-Interrupt procedure is terminated when the IBI event treatment is completed by calling the callback HAL_I3C_NotifyCallback().
The whole IBI process is placed in an infinite loop for the Controller to be able to receive
and treat any new IBI request from the Target.
Target side:
In a first step after retrieve a Dynamic address,
I3C Target starts the communication by sending the In-Band-Interrupt request to the Controller.
This first action on Target side can be done at the same time or independently on one or other Targets.
In fact, after this starting In-Band-Interrupt procedure, the I3C Controller catch the event and the associated information like IBI additional data.
hope this flow helps!
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.