I2C Stuck on TWI IRQ

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).


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.