I'm using nRF SDK 12.2.0 and the corresponding soft device. I control a I2C slave for which I use the TWI peripheral through the driver implemented in nrf_drv_twi.c. It happens some times that the device gets stuck on the TWI IRQ handler. Unfortunately, I don't have access to the I2C lines so I can't probe them, but I anyway I can see SPI0_TWI0_IRQHandler triggering all the time. When that happens, the cpu gets blocked on the IRQ handler and nothing else can run and I have to power cycle my device.
I copy pasted below the values of the TWI instance and the TWI driver control block. I can't see anything wrong there:
p_twi NRF_TWI_Type * 0x40003000
TASKS_STARTRX volatile uint32_t 0
RESERVED0 const volatile uint32_t 0
TASKS_STARTTX volatile uint32_t 0
RESERVED1 const volatile uint32_t [2] 0x4000300c
TASKS_STOP volatile uint32_t 0
RESERVED2 const volatile uint32_t 0
TASKS_SUSPEND volatile uint32_t 0
TASKS_RESUME volatile uint32_t 0
RESERVED3 const volatile uint32_t [56] 0x40003024
EVENTS_STOPPED volatile uint32_t 1
EVENTS_RXDREADY volatile uint32_t 0
RESERVED4 const volatile uint32_t [4] 0x4000310c
EVENTS_TXDSENT volatile uint32_t 0
RESERVED5 const volatile uint32_t 0
EVENTS_ERROR volatile uint32_t 0
RESERVED6 const volatile uint32_t [4] 0x40003128
EVENTS_BB volatile uint32_t 1
RESERVED7 const volatile uint32_t [3] 0x4000313c
EVENTS_SUSPENDED volatile uint32_t 1
RESERVED8 const volatile uint32_t [45] 0x4000314c
SHORTS volatile uint32_t 1
RESERVED9 const volatile uint32_t [64] 0x40003204
INTENSET volatile uint32_t 646
INTENCLR volatile uint32_t 646
RESERVED10 const volatile uint32_t [110] 0x4000330c
ERRORSRC volatile uint32_t 0
RESERVED11 const volatile uint32_t [14] 0x400034c8
ENABLE volatile uint32_t 5
RESERVED12 const volatile uint32_t 0
PSELSCL volatile uint32_t 17
PSELSDA volatile uint32_t 13
RESERVED13 const volatile uint32_t [2] 0x40003510
RXD const volatile uint32_t 0
TXD volatile uint32_t 0
RESERVED14 const volatile uint32_t 0
FREQUENCY volatile uint32_t 26738688
RESERVED15 const volatile uint32_t [24] 0x40003528
ADDRESS volatile uint32_t 40
p_cb twi_control_block_t * 0x20002378 <m_cb>
handler nrf_drv_twi_evt_handler_t 0x36111 <twi_event_handler>
p_context void * 0x0
int_mask volatile uint32_t 646
xfer_desc nrf_drv_twi_xfer_desc_t {...}
type nrf_drv_twi_xfer_type_t NRF_DRV_TWI_XFER_RX
address uint8_t 40 '('
primary_length uint16_t 3
secondary_length uint16_t 0
p_primary_buf uint8_t * 0x2000cf58 <nxpncihal_ctrl+364> "`\006\003\001\003\001"
p_secondary_buf uint8_t * 0x0
flags uint32_t 0
p_curr_buf uint8_t [3] 0x2000cf58 <nxpncihal_ctrl+364>
p_curr_buf[0] uint8_t 0x60 (Hex)
p_curr_buf[1] uint8_t 0x6 (Hex)
p_curr_buf[2] uint8_t 0x3 (Hex)
curr_length uint16_t 3
curr_no_stop _Bool false
state nrf_drv_state_t NRF_DRV_STATE_POWERED_ON
error _Bool false
busy volatile _Bool true
repeated _Bool false
bytes_transferred uint16_t 0
hold_bus_uninit _Bool false
receive_byte_cb void (*)(twi_control_block_t *, NRF_TWI_Type *, uint8_t *, uint16_t, uint16_t *) 0x0
The TWI IRQ handler sees that no bytes were transfered and also no error flag is set so it simply does nothing and then the interrupt triggers again. Is there any know issue related to this?
Also, are there any chances that nrf_drv_twi_tx/nrf_drv_twi_rx will return error and the TWI callback will still be called? (so the function immediately returns an error and after a few micros/millis the TWI callback gets called).
[EDIT]
After some more research I see that EVENTS_STOPPED is set to 1. However, in the following call trace irq_handler_twi() -> twi_transfer(), do_stop_check is evaluated into false, as cb->bytes_transferred is 0 and no error flags are set.
static bool twi_transfer(twi_control_block_t *p_cb,
NRF_TWI_Type * p_twi,
bool * p_error,
uint16_t * p_bytes_transferred,
uint8_t * p_data,
uint16_t length,
bool no_stop)
{
bool do_stop_check = ((*p_error) || ((*p_bytes_transferred) == length));
...
In the end of twi_transfer(), the following bit of code is never executed because of do_stop_check = false:
if (do_stop_check && nrf_twi_event_check(p_twi, NRF_TWI_EVENT_STOPPED))
{
nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED);
NRF_LOG_DEBUG("TWI: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWI(NRF_TWI_EVENT_STOPPED));
return false;
}
I must say I have two FreeRTOS tasks accessing the TWI driver, but all accesses are protected with a mutex, so theoretically that shouldn't be caused by any concurrent access.