This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

TWI driver remains busy right after the nrf_drv_twi_tx() even though TX transfer is completed

I am using a nrf_drv_twi_tx() and nrf_drv_twi_rx() for a simple I2C transaction but I'm noticing a strange behavior.

  1. If I don't use APP_ERROR_CHECK(err_code); right after read(), I see the interrupt is firing as expected and I also see the read bytes sent and actual sensor data is received in response in the logic analyzer. However, when I use APP_ERROR_CHECK(err_code);, I see the execution stops with an error code 0x11 which indicates the driver is busy.
  2. Adding a random delay of 2s seemed to have solved the issue regarding the busy driver error. Even though the interrupt is finished executing, what's still causing the driver to be busy right after the write()?
  3. if I include ulTaskNotifyTake() after read(), for some weird reason, the read() function doesn't seem to fully execute i.e don't see the read bytes sent in the logic analyzer. As soon as I put the breakpoint on a read(), I see things working as expected i.e read bytes are sent and interrupt is fired. 

void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)	   
{
    Sensor *obj = static_cast<Sensor*>(p_context);
    
    switch (p_event->type)
    {
        case NRF_DRV_TWI_EVT_DONE:
            if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_RX)
            {
	            vTaskNotifyGiveFromISR(obj->taskHandle, pdFALSE);
            }
    	    else if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_TX)
    	    {
                // ...
    	    }
            m_xfer_done = true; 
            break;
        default:
            break;
    }
}

void Sensor::read()
{
    ret_code_t err_code = nrf_drv_twi_rx(&m_twi, ADDR, _buffer, 2);
    APP_ERROR_CHECK(err_code);
}

void Sensor::write(uint8_t reg, uint8_t *buffer, uint8_t size)
{
    ret_code_t err_code;

    err_code = nrf_drv_twi_tx(&m_twi, ADDR, &reg, size, false);
    APP_ERROR_CHECK(err_code);
    while (m_xfer_done == false);
}


void Sensor::readTask()
{
    write();  

    while(true)
    {   
        read();

        uint32_t taskNotify = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // block till data is read
        
        processData();
    }
}

Any help is greatly appreciated


Edit:

One other thing I noticed was if I include the m_xfer_done flag inside read(), I see everything working fine as expected but then I wonder what purpose is ulTaskNotifyTake serving since the m_xfer_done flag is mainly used for synchronizing the ISR and the task? Without the m_xfer_done flag used, looks to me the readTask remains blocked as there are no read bytes sent resulting in NO ISR being fired

void Sensor::read()
{
    ret_code_t err_code = nrf_drv_twi_rx(&m_twi, ADDR, _buffer, 2);
    APP_ERROR_CHECK(err_code);
    while(!m_xfer_done);  // flag that's set to true inside ISR
}

Parents Reply Children
  • Hi 

    Thanks for sharing the datasheet. 

    Looking at it I notice that the pseudo code for reading the temperature registers shows a more traditional tx_rx sequence, where you write the register address (0x05), and then immediately issue a repeated start to read the result. 

    Could you try this and see if it works better than doing reads only?

    If you use the nrf_drv_twi_xfer(..) function you are able to issue a TX->RX operation in a single call, which means you don't have to wait for the TX operation to complete before issuing an RX operation. 

    Best regards
    Torbjørn

  • Are you implying to invoke write before every read? In the datasheet, they only show examples for sending and receiving once?

    My main concern is the same code for repeatedly reading in a loop NOT working only when task notification is used, which does imply repeated reads are supported. Can you see that?

Related