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. 

  • Hello,

    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: 

    Could you possibly share a code snippet of your NRFX_TWIM_XFER_TX declaration and use?
    Could you also elaborate on how you initiate your transfers - whether you are using the nrfx_twim_xfer for, or are you doing this through PPI, or any other way?
    It would also be helpful to see your TWIM init function, if possible.

    Please use the 'Insert code' option when sharing code on the forum.

    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. 

    Yes, the TWIM driver uses EasyDMA be default.

    Looking forward to resolving this issue together!

    Best regards,
    Karl

  • 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

  • Hello again! 

    1) I removed the NRFX_TWIM_INSTANCE macro as a result of troubleshooting why the "old_config.h" file was rewriting my sdk_config. I've added it back without any problem. 

    2) I've been in debug mode and have verified that all of my return codes are "NRF_SUCCESS". 

    By 'logger' do you mean the J-Link application that captures NRF_LOG_INFO messages? I've heard that its possible 

    to have your firmware store these messages in flash and that they can be retrieved later.  I'm new to this chip/IDE. Is this possible? As a side-note, is there a page with describing how to use this functionality? 

    Thank you so much for your help! 

  • Julio said:
    Thank you so much for your help!

    It is no problem at all Julio, I am happy to help!

    Julio said:
    1) I removed the NRFX_TWIM_INSTANCE macro as a result of troubleshooting why the "old_config.h" file was rewriting my sdk_config. I've added it back without any problem. 

    Oh, I see. It seems I forgot to answer that part of your previous comment. The apply_old_config file is included for backwards compatibility - but it has been reported several times as perhaps less-than-ideal..
    Please see this ticket for a more detailed discussion of the issue. In essence, it will override your sdk_config define if you leave something defined, regardless of if it is defined to 0 ( which then intuitively should mean that it is excluded for the build, of course ). I am sorry that you too encountered issues with this configuration.

    Julio said:
    2) I've been in debug mode and have verified that all of my return codes are "NRF_SUCCESS". 

    Ok, at least that is good - but I would strongly recommend that you always from here on out check your returned error codes in your application. You might still encounter != NRF_SUCCESS error codes when the firmware is out of development and deployed, and these error code checks are you only way of knowing if this has occurred(for whatever reason) and that your program may not proceed as usual.

    Julio said:

    By 'logger' do you mean the J-Link application that captures NRF_LOG_INFO messages? I've heard that its possible 

    to have your firmware store these messages in flash and that they can be retrieved later.  I'm new to this chip/IDE. Is this possible? As a side-note, is there a page with describing how to use this functionality? 

    I am here referring to the Logger module of the SDK - your configuration of the logger module will determine which backends it uses, and thus also where it outputs its logs (such as either RTT, UART, CLI, or FLASH). Which backend are you currently using - RTT?
    You may configure the logger to store logs in flash, yes. Or are you here asking about the deferred logging option?
    Deferred logging lets you postpone logging processing till the CPU is idle, so that it minimally impacts your application performance. You can see how this is done in most - if not all - of the SDK examples, where NRF_LOG_PROCESS is called as part of the idle_state_handler function, right before the device goes to low power mode.
    Deferred logging then also means that the logs are not written out until this call to NRF_LOG_PROCESS is made, so they may appear later than expected when compared to in-place logging(non-deferred).
    You may read more about the logger module in the logger module documentation.

    If this was not what you were asking about, or if anything still should be unclear about the logger module, please do not hesitate to ask! :) 

    Best regards,
    Karl 

Related