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

TWIM XFER Hangs Unless NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER used as a flag

Hello! 

I have this interesting bug with the twim driver.

It works perfectly with the NRFX_TWIM_XFER_TXRX transfer type: I see the transaction go out on the bus lines and the interrupt calls my handler when the transfer is complete. 

When I switch the xfer type to NRFX_TWIM_XFER_TX, however, I get the following: 

1) The transaction occurs successfully: I see the start and stop bits and all the bits in between are as expected. 

2) My handler is never called. 

3) If I poll wait using 'nrfx_twim_is_busy', it never leaves the while loop. 

4) If I use the NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER flag, the data goes out on the pins, the handler is never called (as expected) BUT 'nrfx_twim_is_busy' eventually 

    returns 'false'. 

What am I missing? I see that I have to enable EasyDMA on the active Twim instance for the project to compile but I don't have any easy DMA code setup. 

Parents
  • Here is the initialization: 

    //Initialization
    const nrfx_twim_t drv = {
       .p_twim			 = NRF_TWIM0,
       .drv_inst_idx     = NRFX_TWIM0_INST_IDX
    };
    
    const nrfx_twim_config_t cfg = {
       .scl                = I2C_SCL,
       .sda                = I2C_SDA,
       .frequency          = NRF_TWIM_FREQ_400K,
       .interrupt_priority = NRFX_TWIM_DEFAULT_CONFIG_IRQ_PRIORITY,
       .hold_bus_uninit     = true
    };
    	
    void i2c_init(void)
    {
    	ret_code_t errCode = 0;
    
    	//	Init variables
    	memset( (void *) &xferStruct, 0, sizeof(nrfx_twim_xfer_desc_t));
    	memset( (void *) addrBuff, 0, (sizeof(uint8_t) * ADDR_LEN) );
    	blockCall = 0;
    	usrCallBack = NULL;
    
    	//	Set-up SDA and SCL as outputs first
    	nrf_gpio_pin_set(I2C_SCL);
    	nrf_gpio_pin_set(I2C_SDA);
    	nrf_gpio_cfg(I2C_SCL, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,
    			NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE);
    
    	nrf_gpio_cfg(I2C_SDA, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL,
    			NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE);
    
    
    	//Initialize inputs
    	if(!nrfx_gpiote_is_init())
    	{
    		nrfx_gpiote_init();
    	}
    
    	//	Initialize driver and enable
    	errCode = nrfx_twim_init(&drv, &cfg, handler, (void *)&context);
        NRF_LOG_INFO("Twi init: %d", errCode);
        NRF_LOG_FLUSH();
    
    	nrfx_twim_enable(&drv);
    	return;
    }

    and here is the write function. I do not use PPI. I just use the call to twim_xfer: 

    //here is the tx function 
    
    uint8_t setRegBuff[RAM_REG_LEN + 1];
    nrfx_twim_evt_t tmpEvt;
    ret_code_t write_i2c_reg(uint8_t regAddr, uint8_t *regBuff, done_call_back_t cb)
    {
    	ret_code_t errCode = NRF_SUCCESS;
    
    	if(nrfx_twim_is_busy(&drv))
    	{
    		errCode = NRF_ERROR_BUSY;
    	}
    	else
    	{
    
    		setRegBuff[0] = regAddr;
    		setRegBuff[1] = regBuff[0];
    		setRegBuff[2] = regBuff[1];
    
    		xferStruct.address = BUS_I2C_REG_ADDR;
    		xferStruct.p_primary_buf = setRegBuff;
    		xferStruct.primary_length = RAM_REG_LEN + 1;
    		xferStruct.p_secondary_buf = NULL;
    		xferStruct.secondary_length = 0;
    		xferStruct.type = NRFX_TWIM_XFER_TX;
    
    		usrCallBack = NULL;
    		blockCall = 1;
    
    		//Should NOT be called with the NO_XFER_EVT flag but otherwise it hangs up ...
    		//My work-around is to block wait until the transfer is done, and then call
    		//the handler directy
    		errCode = nrfx_twim_xfer(&drv, &xferStruct, NRFX_TWIM_FLAG_NO_XFER_EVT_HANDLER);
    
    
    		//wait till done if no call back defined
    		// For some reason _TX transfers do not call an interrupt when finished
    		while(nrfx_twim_is_busy(&drv))
    		{
    			nrf_delay_us(MAX17330_BLOCK_DELAY);
    		}
    
    		//Force a delay... bytes are still being shifted out at this point
    		//so subsequent writes might override 
    		nrf_delay_us(BLOCK_DELAY * 3);
    
            //simulate the interrupt by calling the handler directly 
    		tmpEvt.type = NRFX_TWIM_EVT_DONE;
    		memcpy( &tmpEvt.xfer_desc, &xferStruct, sizeof(nrfx_twim_xfer_desc_t) );
    		handler(&tmpEvt, NULL);
    
    	}
    	return errCode;
    }

    As much as I hate editing SDK files, I also commented out the "apply_old_configs.h" file in the include tree. This was 

    over-riding my sdk_config. BTW, that is super confusing. Why set the SDK up this way? the sdk_config should be the source of truth for the project but I guess thats another ticket haha 

  • Hello again,

    Julio said:
    Here is the initialization: 

    Thank you for sharing this! I have a couple of questions:
    I suppose it accomplishes the same thing here, but is there a reason why you are not using the NRFX_TWIM_INSTANCE macro for creating the drv twim instance?
    You are calling nrf_gpio_pin_set on the pins which would set them high if already configured as outputs, else it does nothing. You also configure the pins right before calling nrfx_twim_init which will override this configuration. Is the pins already in use somewhere else when you enter the i2c_init function?

    Most importantly, I see that you do not pass the error code returned by your nrfx_twim_init call to an APP_ERROR_CHECK - I would highly recommend doing so, for all functions returning error codes. You might have this check placed elsewhere, but your i2c_init function does not return the error code either. The same goes for your call to nrfx_gpiote_init - you should always check the returned error codes.

    Julio said:
    I do not use PPI. I just use the call to twim_xfer: 

    Thank you for clarifying!

    Could you add APP_ERROR_CHECKs to all your nrfx calls that return error codes?
    Without the checks you will never know if the functions fail, nor the reason why they fail.
    Please do this, and run the program again and let me know if any error messages is output to your logger output.
    Please also ensure that you have DEBUG defined in your preprocessor defines - like shown in the included image - to make the APP_ERROR_CHECK write out an error message to the logger if a != NRF_SUCCESS error code is passed to it.

    Looking forward to resolving this issue together!

    Best regards,
    Karl

Reply
  • Hello again,

    Julio said:
    Here is the initialization: 

    Thank you for sharing this! I have a couple of questions:
    I suppose it accomplishes the same thing here, but is there a reason why you are not using the NRFX_TWIM_INSTANCE macro for creating the drv twim instance?
    You are calling nrf_gpio_pin_set on the pins which would set them high if already configured as outputs, else it does nothing. You also configure the pins right before calling nrfx_twim_init which will override this configuration. Is the pins already in use somewhere else when you enter the i2c_init function?

    Most importantly, I see that you do not pass the error code returned by your nrfx_twim_init call to an APP_ERROR_CHECK - I would highly recommend doing so, for all functions returning error codes. You might have this check placed elsewhere, but your i2c_init function does not return the error code either. The same goes for your call to nrfx_gpiote_init - you should always check the returned error codes.

    Julio said:
    I do not use PPI. I just use the call to twim_xfer: 

    Thank you for clarifying!

    Could you add APP_ERROR_CHECKs to all your nrfx calls that return error codes?
    Without the checks you will never know if the functions fail, nor the reason why they fail.
    Please do this, and run the program again and let me know if any error messages is output to your logger output.
    Please also ensure that you have DEBUG defined in your preprocessor defines - like shown in the included image - to make the APP_ERROR_CHECK write out an error message to the logger if a != NRF_SUCCESS error code is passed to it.

    Looking forward to resolving this issue together!

    Best regards,
    Karl

Children
No Data
Related