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.