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.
- 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.
- 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()?
- 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, ®, 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
}
