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

TWI Event Handler Bypassing RX Transfer

I am in the initial phases of porting an arduino project using the NRF52832 to the Nordic SDK (14.2 for the moment) and am running into issues when using TWIM and an event handler. This stage of my port effort is based on the twi_sensor example and I have made the necessary changes to my sensor library to successfully read values from my accelerometer in blocking mode. I am now trying to implement the non-blocking operation and am having difficulty with the TWI event handler running to completion.

If I place a breakpoint in the event handler on NRF_DRV_TWI_EVT_DONE and the function I have initiating the TWI RX/TX operations and step through them I have noticed the following:

  1. The library function call to read from the sensor (consisting of both a TX and RX operation) first initiates a NRF_DRV_TWI_XFER_TX event xfer type and passes through the handler. This is expected.
  2. The subsequent RX operation to read the selected register from above is called, passes through the event handler, calls the data handler to output to the console, and returns. What I am seeing at this point of stepping through however is that I am not receiving values from the sensor. This process does run through the event handler though.

I have tried changing the event handler to handle RX and TX event separately and changed the interrupt priority in the TWI config to no avail. I suppose it is worth mentioning that I am using an Adafruit NRF52 board and Visual Studio + VisualGDB. As I mentioned before, I do have TWI working in blocking mode and have been getting expected readings from my sensor so I am hesitant to blame the board or the dev environment. I am planning to move to SDK15 as soon as a BSP is created for VisualGDB but I do not think that has any bearing on the issue I am facing. Relevant code is included below.

main.cpp

#include <stdio.h>
#include "boards.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "nrf_drv_twi.h"
#include "H3LIS331DL.h"



#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

/* TWI instance ID. */
#define TWI_INSTANCE_ID     0

/* TWI instance. */
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

static volatile bool m_xfer_rx_done = false;
int16_t x, y, z;
static H3LIS331DL h3lis;

__STATIC_INLINE void accel_data_handler(int16_t x, int16_t y, int16_t z)
{
	NRF_LOG_INFO("X: %d \t Y: %d \t Z: %d \n", x, y, z);
}

static void read_sensor_data()
{
	m_xfer_rx_done = false;
	h3lis.readXYZ(&x, &y, &z);
//	accel_data_handler(x, y, z);
}

void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
{
	switch (p_event->type)
	{
		case NRF_DRV_TWI_EVT_DONE:
			if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_RX)
			{
				accel_data_handler(x, y, z);
			}
			m_xfer_rx_done = true;
			break;
	
		case NRF_DRV_TWI_EVT_ADDRESS_NACK:
			break;
	
		case NRF_DRV_TWI_EVT_DATA_NACK:
			break;
	
		default:
			break;
	}
}

/**
 * @brief TWI initialization.
 */
void twi_init(void)
{
	ret_code_t err_code;

	const nrf_drv_twi_config_t twi_config = {
		.scl = ARDUINO_SCL_PIN,
		.sda = ARDUINO_SDA_PIN,
		.frequency = NRF_TWI_FREQ_100K,
		.interrupt_priority = APP_IRQ_PRIORITY_LOW,
		.clear_bus_init = false
	};

	err_code = nrf_drv_twi_init(&m_twi, &twi_config, twi_handler, NULL);
	APP_ERROR_CHECK(err_code);

	nrf_drv_twi_enable(&m_twi);
}


/**
 * @brief Function for main application entry.
 */
int main(void)
{
	ret_code_t err_code;

	APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
	NRF_LOG_DEFAULT_BACKENDS_INIT();

	NRF_LOG_FLUSH();
	twi_init();
	
	h3lis.init(&m_twi, H3LIS331DL_ODR_100Hz, H3LIS331DL_NORMAL, H3LIS331DL_FULLSCALE_2);
	
	//int16_t x, y, z;

	while(true)
	{
		nrf_delay_ms(500);
			
		do
		{
			__WFE();
		} while (m_xfer_rx_done == false);
		
		read_sensor_data();
		NRF_LOG_FLUSH();
	}
}

H3LIS331DL.cpp read and write functions. _twi_inst passed from main.

uint8_t H3LIS331DL::readReg(uint8_t deviceAddr, uint8_t Reg, uint8_t* Data, uint8_t len)
{
	ret_code_t err_code;
	err_code = nrf_drv_twi_tx(_twi_inst, deviceAddr, &Reg, 1, true);	
	if (err_code == NRF_SUCCESS)
	{
		nrf_drv_twi_rx(_twi_inst, deviceAddr, Data, len);
	}
	else
	{
		return MEMS_ERROR;
	}
	return MEMS_SUCCESS;
	
}
//
///*******************************************************************************
//* Function Name     : writeReg
//* Description       : Generic Writing function. It must be full filled with either
//*                   : I2C or SPI writing function
//* Input             : Register Address, Data to be written
//* Output            : None
//* Return            : Status [MEMS_ERROR, MEMS_SUCCESS]
//*******************************************************************************/
uint8_t H3LIS331DL::writeReg(uint8_t deviceAddr, uint8_t WriteAddr, uint8_t Data)
{
	ret_code_t err_code;
	uint8_t dat[2];
	dat[0] = WriteAddr;
	dat[1] = Data;
	
	err_code = nrf_drv_twi_tx(_twi_inst, deviceAddr, dat, sizeof(dat), true);
	if (err_code != NRF_SUCCESS)
	{
		return MEMS_ERROR;
	}
	return MEMS_SUCCESS;
}

Parents
  • Hi,

     

    In the ::readReg() function, you are not waiting for the callback main.c::twi_handler() to finish processing the first call to nrf_drv_twi_tx, which makes the next call to nrf_drv_twi_rx fail.

    I'd recommend that you check the error code for the _rx call as well.

     

    Could you try to add a wait in the ::readReg() function and see if this works?

     

    Best regards,

    Håkon

Reply
  • Hi,

     

    In the ::readReg() function, you are not waiting for the callback main.c::twi_handler() to finish processing the first call to nrf_drv_twi_tx, which makes the next call to nrf_drv_twi_rx fail.

    I'd recommend that you check the error code for the _rx call as well.

     

    Could you try to add a wait in the ::readReg() function and see if this works?

     

    Best regards,

    Håkon

Children
  • Håkon,

    Thank you for taking a look. If I could get some clarification on your suggestion of adding a wait to the readReg function. I assume that I need to add the below (directly before the _rx call) to not add a blocking loop to my function while waiting for the callback function to complete. Is this what you had in mind?

    do
    {
    	__WFE();
    } while (m_xfer_rx_done == false);

    By adding the above I will also need to maintain a local m_xfer_rx_done variable inside of my sensor library and set it accordingly from twi_handler() and any other related function. I think what I have described will work, as long as the obvious pitfall of keeping m_xfer_rx_done synced between my library and main is kept in check.

    All of this has me questioning if my design decision to have the TWI instance reference passed into the sensor library and letting main.c manage TWI, as opposed to configuring and handling TWI within the library was a smart move. My rationale was that once I add other devices to the bus I did not want to have multiple libraries trying to manage a TWI instance, just do it in one place, main. This is a little off topic I'm sure and more of a design question but if you have any thoughts on this design I'd like to hear them.

    Thanks again,
    Patrick

  • Your assumption regarding where to put the wait (for non-blocking operation) is correct.

    If you do not want the module to use any global's, an alternative is to keep the tx/rx logic in main, or to pass the m_xfer_rx_done to the function, but this adds complexity on how to use the module. If you want the module to be able to handle both blocking and non-blocking TWI transactions, the complexity will grow.

     

    Kind regards,

    Håkon

Related