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
  • When I use the flag, it works as expected. I have edited the description, please take a look.
    When does the RTOS specialist plan on coming back?

  • Hi 

    Thanks for testing it, that seems to point to an issue with the use of the FreeRTOS Notify feature. 

    Would it be an option to use a semaphore instead?

    The FreeRTOS specialist is not expected back until early August unfortunately. 

    Best regards
    Torbjørn

  • I feel it's more of a question for nordic as to why does not having while(!m_xfer_done); cause issues with subsequent reads?

    perhaps there's a better approach to it?

  • Hi 

    The obvious reason would be that the next read is issued too fast, depending on how long the processData() takes to execute, but then you should have seen a BUSY error when trying to read the second time. 

    morpho said:
    perhaps there's a better approach to it?

    It seems a bit suboptimal to schedule reads as fast as possible, unless you really need to read the status that often. 

    Have you considered scheduling these reads from a timer instead?
    Then you can reduce power draw and CPU usage, while getting a more consistent and even readout from the sensor. 

    Also, if the sensor has an interrupt pin it makes sense to hook that up, so you only need to read when there is new data available. 

    Best regards
    Torbjørn

  • My initial hunch was I was reading too fast but I have a delay of 3s between each read (for now). I wouldn't want to be reading without any delay anyways...just seems too abrupt for no particular reason.

    I don't see any BUSY error; it's just for some reason excluding the while(!m_xfer_done) from the read() call causes issues with subsequent reads, and I'm unable to understand why would that be. I don't see any error. If I don't exclude it, it works fine but then I don't see the point of using it with task notifications.

    Plus it's hard to debug because if I put the breakpoint everywhere, it would probably work.

    Timer is a good option too but then i'd need to decide between using a thread and a timer in that case. 

    nrf_drv_twi_rx()  enables the interrupt which fires the ISR and the handler, isn't it?

Related