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

I2C Read transfer doesn't happen with ulTaskNotifyTake

I am using nrf_drv_twi_rx to initiate an I2C read transfer but the interrupt isn't fired for the RX transfer when the ulTaskNotifyTake is used, but seems to work without ulTaskNotifyTake() call. When I hooked up to the logic analyzer, it seems the read bytes aren't sent.



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)
    	    {
    	      NRF_LOG_INFO("TX transfer done...\n");
    	    }
            m_xfer_done = true; 
            break;
        default:
            break;
    }
}

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

void MCP9808::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::xferData(uint8_t size)
{
    write(reg, size);	    
}


void Sensor::mainThread()
{
    xferData(1);  

    while(true)
    {   
        initiateI2cRead();

        uint32_t taskNotify = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        
        processData();
    }
}
Parents Reply Children
  • seems like your app's state machine is stuck at ulTaskNotifyTake. Probably best to have ulTaskNotifyTake after processData() to kick off the send/receive properly and then wait for the events?

  • Wouldn't it make more sense to process the data after it's been signalled from the interrupt rather than spinwaiting till the data is ready to be processed first? That was the point I decided to use ulTaskNotifyTake so the task is blocked till the data is received, and processed only after 

  • Sorry for late response morpho.

    Normally processData should do nothing if there is no data (it is normally waste of cpu time the first call) but it avoids one race condition when your application state machine depends on process data to complete for preparing for the next transaction.

    I would still recommend you to have process data first and call ulTaskNotifyTake . It would only make difference the first call. Then it would be a loop like how you have it now (minus the race condition possibility)

  • Thanks for the response. 
    sorry, I'm still not fully convinced as to why would processData() be still after ulTaskNotifyTake. Isn't the main idea behind ulTaskNotifyTake to block the current task till data is ready to be processed? Perhaps why even go inside processData() if there's nothing to process, plus what are you blocking the test after processData() via ulTaskNotifyTake for?

    if this is the flow, and assuming processData() doesn't do anything if there's no data, I assume the following flow:

    - interrupt is enabled but data hasn't been read yet

    - processData() is invoked but nothing to process 

    - block the task till the I2C data is fully read. Unblock it once the data is read but it's not processed

    - repeat the loop --> go on to read the next i2c data

    If I'm understanding correctly, above is the flow. If that's true, don't you think you're missing out the processing of certain reads in case data wasn't read by the time processData() was invoked?     

    while(true)
    {
    initiateI2cRead();

    processData();

    uint32_t taskNotify = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

    }

Related