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

TWI transaction failed

Hi, i am using:

-nRF 52 DK

-nRF 52832

-Sensor of Maxim Integrated

-SDK 17.0.2

I have some problems in handling the I2C through the nrfx module.

My I2C communication with the sensor is defined as a TX/RX.

I am using slightly different Read/Write function than the one used in the NRFX examples because i need some non-void function.

Here is my initialization,handler and read byte function:



 static const nrfx_twim_t twim_instance = NRFX_TWIM_INSTANCE(0);
 volatile bool twim_tx_done = false;
 volatile bool twim_rx_done = false;


void twim_handler(nrfx_twim_evt_t const * p_event, void * p_context)
{

    size_t primaryBufferSize = p_event->xfer_desc.primary_length;
    uint8_t *bytesTransferred = p_event->xfer_desc.p_primary_buf;
    size_t secondaryBufferSize = p_event->xfer_desc.secondary_length;
    uint8_t *bytesRead = p_event->xfer_desc.p_secondary_buf;

    switch (p_event->type)
    {
        case NRFX_TWIM_EVT_DONE:
    
            NRF_LOG_INFO("STATE OF BUFFERS IN EVENT HANDLER:");
            NRF_LOG_INFO("\t Size of primary buffer: %d", primaryBufferSize);
            NRF_LOG_INFO("\t Size of secondary buffer: %d", secondaryBufferSize);
          
					if (p_event->xfer_desc.type == NRFX_TWIM_XFER_TX)
            {
                twim_tx_done = true;
            }
          
						if (p_event->xfer_desc.type == NRFX_TWIM_XFER_RX)
            {
                twim_rx_done = true;
            }
            break;
        
				case NRFX_TWIM_EVT_ADDRESS_NACK:
            NRF_LOG_INFO("Received NACK after sending address!");
            break;
        
				case NRFX_TWIM_EVT_DATA_NACK:
            NRF_LOG_INFO("Received NACK after sending data.");
            break;
        default:
            break;
    }

	
uint32_t nrf_drv_oxi_init(void)
{
    uint32_t err_code;
    
    const nrfx_twim_config_t twi_oxi_config = {
       .scl                = OXI_TWI_SCL_PIN,
       .sda                = OXI_TWI_SDA_PIN,
       .frequency          = NRF_TWIM_FREQ_400K,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGHEST,
       .hold_bus_uninit     = true
    };
    
		err_code = nrfx_twim_init(&twim_instance, &twi_oxi_config, twim_handler, NULL);
    APP_ERROR_CHECK(err_code);
    
    
	  nrfx_twim_enable(&twim_instance);
	
	  return NRF_SUCCESS;
}
//        ------------------------------------------------------------------------------------------------



uint8_t readByte( uint8_t familyByte, uint8_t indexByte)
{

		nrfx_err_t err_code;

		uint8_t StatusByte;    
        uint8_t ReturnByte;
   
        uint8_t rx_buffer[2];
		size_t rx_lenght =2;

		uint8_t tx_buffer[] = {familyByte, indexByte};    
		size_t tx_lenght = sizeof(familyByte) + sizeof(indexByte);

		nrfx_twim_xfer_desc_t tx_xfer = NRFX_TWIM_XFER_DESC_TX(OXI_ADDRESS_W, tx_buffer, tx_lenght);
		err_code = nrfx_twim_xfer(&twim_instance, &tx_xfer, 0);
		APP_ERROR_CHECK(err_code);
		
       while (!twim_tx_done)
        {
            __WFE();
        } 
        twim_tx_done  = false;

		
		nrfx_twim_xfer_desc_t rx_xfer = NRFX_TWIM_XFER_DESC_RX(OXI_ADDRESS_R, rx_buffer, rx_lenght);
		err_code = nrfx_twim_xfer(&twim_instance, &rx_xfer, 0);
		APP_ERROR_CHECK(err_code);
	
         while (!twim_rx_done )
        {
            __WFE();
        } 
        twim_rx_done  = false;
  
	
	
		StatusByte = rx_buffer[0];
        NRF_LOG_INFO("Status Byte: %lu \n",StatusByte);
    
		ReturnByte = rx_buffer[1];  
		return ReturnByte;
}

 

I don't receive any error (err_code always 0 (NRF_SUCCESS)) but it seems that after the first transaction (TX) nothing happen as i can see from the TWIM0 window:

No task is started and no byte is transferred.

I am a newbie in embedded system, probably my functions are correct (thanks to the help of Nordic Support Team) but probably i am missing some basics of I2C configuratiion.

Do you have any idea why this could happen? 

thanks,

polimarte

Parents
  • Hello Polimarte!

    From your included screenshot you seem to be encountering some errors. Is this correct? Which errors are these, and where do they come from?

    Are you ever entering your twim_handler?

    No task is started and no byte is transferred.

    Do you have access to a logic analyzer, so you may see what is happening on the pins themselves? 

    I don't receive any error (err_code always 0 (NRF_SUCCESS)) but it seems that after the first transaction (TX) nothing happen as i can see from the TWIM0 window:
    No task is started and no byte is transferred.

    Please elaborate where you are reading this. From what I can see in your screenshot, both ERROR and TXSTARTED is true - leading me to believe that a TX in fact is started, and an error is returned somewhere.
    Let us start by finding the source of this error, so we may get to the bottom of why this is not working as expected.

    Looking forward to resolving this issue together!

    Best regards,
    Karl

Reply
  • Hello Polimarte!

    From your included screenshot you seem to be encountering some errors. Is this correct? Which errors are these, and where do they come from?

    Are you ever entering your twim_handler?

    No task is started and no byte is transferred.

    Do you have access to a logic analyzer, so you may see what is happening on the pins themselves? 

    I don't receive any error (err_code always 0 (NRF_SUCCESS)) but it seems that after the first transaction (TX) nothing happen as i can see from the TWIM0 window:
    No task is started and no byte is transferred.

    Please elaborate where you are reading this. From what I can see in your screenshot, both ERROR and TXSTARTED is true - leading me to believe that a TX in fact is started, and an error is returned somewhere.
    Let us start by finding the source of this error, so we may get to the bottom of why this is not working as expected.

    Looking forward to resolving this issue together!

    Best regards,
    Karl

Children
  • Hi Karl, it is a pleasure to hear you again!

    I have to admit that i found some problems in enabling the log uart in the last SDK so i am using the RTT viewer but nothing happen on my RTT terminal if i am stack in a loop as in my case.

    My code is stack at the loop for twim_tx_done, so NRFX_TWIM_EVT_DONE is never set.

    If i comment these loop on both the Read/Write Bytes functions on my window, my code run without block and i can see this on the RTT viewer:

    Are you ever entering your twim_handler?

    Yes i can enter on the twim_handler and as i can see from the RTT viewer i get : NRFX_TWIM_EVT_ADDRESS_NACK.

    Do you have access to a logic analyzer, so you may see what is happening on the pins themselves? 

    Yes, i do. I am using KEIL. Unfortunately i m still studying how to setup the logic analyzer because i am not able to let it work properly (i am getting always "Unknown Signal").

    Mange Takk Karl!

    polimarte

  • Hi Polimarte,

    polimarte said:
    it is a pleasure to hear you again!

    Likewise! It is great to see you progress with your application! 

    polimarte said:
    I have to admit that i found some problems in enabling the log uart in the last SDK so i am using the RTT viewer but nothing happen on my RTT terminal if i am stack in a loop as in my case.

    I understand. Is this something you would like to look into also, or are you content with using the RTT viewer? RTT is just as good a backend for the logger as UART, by the way, it is just that it requires an RTT compatible terminal.

    polimarte said:

    My code is stack at the loop for twim_tx_done, so NRFX_TWIM_EVT_DONE is never set.

    If i comment these loop on both the Read/Write Bytes functions on my window, my code run without block and i can see this on the RTT viewer:

    polimarte said:
    Yes i can enter on the twim_handler and as i can see from the RTT viewer i get : NRFX_TWIM_EVT_ADDRESS_NACK.

    From your code, I would expect this behavior. Looking at your read and write function, they do not account for the possibility that you may receive an error.
    In essence, if you call readByte now, it will go into the while-loops, and wait for the DONE event to let it exit the waiting. If instead an TWIM error is received, then the error event is generated, and twim_*x_done is never set. Since the transaction failed, and is not restarted, the twim_*x_done will never be set, and you will be stuck in the loop forever.

    I think the easiest fix ( but not prettiest) to this problem would be to just add another variable that is set if the transaction failed, and then tries it again or does something else. Alternatively, you could also add a timer that only lets your while-loops run for so long. Is your APP_ERROR_CHECK on line 16 of readByte returning any errors?

    We should also look into why the address is returning a NACK. What does the datasheet for your sensor say, what is the possible reasons for the device to NACK the address?

    polimarte said:
    Yes, i do. I am using KEIL. Unfortunately i m still studying how to setup the logic analyzer because i am not able to let it work properly (i am getting always "Unknown Signal").

    I see. It is a great tool to have available when working with serial protocols, since this would uncover any unexpected behavior on the lines.
    I think the current problem lays in the code - which we will continue debug - but for future debugging it would be great to have the logic analyzer up and running.

    polimarte said:
    Mange Takk Karl!

    Prego, Polimarte! :)

    Best regards,
    Karl

  • Hi Karl!

    are you content with using the RTT viewer?

    That's fine, i will continue to work with RTT viewer.

    Alternatively, you could also add a timer that only lets your while-loops run for so long.

    I have modified my code with a timer in this way, introducing a timeout: 

    uint8_t readByte( uint8_t familyByte, uint8_t indexByte)
    {
            uint32_t timeout = OXI_TWI_TIMEOUT;
    		nrfx_err_t err_code;
    
    		uint8_t StatusByte;    
            uint8_t ReturnByte;
       
            uint8_t rx_buffer[2];
    		size_t rx_lenght =2;
    
    		uint8_t tx_buffer[] = {familyByte, indexByte};    
    		size_t tx_lenght = sizeof(familyByte) + sizeof(indexByte);
    
    		nrfx_twim_xfer_desc_t tx_xfer = NRFX_TWIM_XFER_DESC_TX(OXI_ADDRESS_W, tx_buffer, tx_lenght);
    		err_code = nrfx_twim_xfer(&twim_instance, &tx_xfer, 0);
    		APP_ERROR_CHECK(err_code);
    		
    		while((!twim_tx_done) && --timeout)
            {
                __WFE();
            } 
    		if(!timeout) err_code=NRF_ERROR_TIMEOUT;
    		else  twim_tx_done  = false;
    		APP_ERROR_CHECK(err_code);
     
    
    		nrfx_twim_xfer_desc_t rx_xfer = NRFX_TWIM_XFER_DESC_RX(OXI_ADDRESS_R, rx_buffer, rx_lenght);
    		err_code = nrfx_twim_xfer(&twim_instance, &rx_xfer, 0);
    		APP_ERROR_CHECK(err_code);
    	
            while((!twim_rx_done) && --timeout)
            {
                __WFE();
            } 
    		if(!timeout) err_code=NRF_ERROR_TIMEOUT;
    		else  twim_rx_done  = false;
    		APP_ERROR_CHECK(err_code);
      
    	
    		StatusByte = rx_buffer[0];
            NRF_LOG_INFO("Status Byte: %lu \n",StatusByte);
        
    		ReturnByte = rx_buffer[1];  
    		return ReturnByte;
    }

    I think the easiest fix ( but not prettiest) to this problem would be to just add another variable that is set if the transaction failed, and then tries it again or does something else.

    What do you mean with something else? What you suggest me is to use a flag in case of NACK and restart the transition, is it correct? 

    Is your APP_ERROR_CHECK on line 16 of readByte returning any errors?

    This is a thing that really i don't understand, as you can see from the screenshot of the Debugger  i don't receive any err_code. It gives me NRF_SUCCESS. So without the loop waiting for the NRFX_TWIM_EVT_DONE my code run over the transition without stopping at the CHECK. What am i missing here? 

    We should also look into why the address is returning a NACK.

    As you can see here in the first lines:

    it's said: "The MAX32664 uses 0xAA as the I2C 8-bit slave write address and 0xAB is used as the I2C 8-bit slave read address" and than in the following pages "The eight bits to be transferred as a slave address for the MAX32664 is 0xAA for a write transaction". So i am using different slave adresses for the TX (0xAA) and the RX(0xAB) as indicated. Am i wrong about this? I don't have further expanation on the datasheet about how the handle NACK events unfortunately.

    Thanks for your help!

    Polimarte

  • polimarte said:
    I have modified my code with a timer in this way, introducing a timeout: 

    While this implementation might function, you have no way to know how long this loop will take to complete - the time will vary for each call to your function. This is due to the fact that your readByte function might be interrupted during this loop - which would make the "timeout" rundown take longer.
    Additionally, since WFE is being used in the timeout loop, you will not necessarily know (unless you have a periodic event happening) how long this will take. Imagine a sequence in which there is a 1 minute gap in between events - during this minute, the timeout variable will not change at all, since the device will  be waiting for an event to occur.

    polimarte said:
    What do you mean with something else? What you suggest me is to use a flag in case of NACK and restart the transition, is it correct? 

    Yes, and then have something else ( such as a retransmit ) happen if the NACK flag is set. So, the function waits for either twi_*x_done to be set, and then proceed with the function, or for another flag twi_*x_failed to be set, in which case it aborts the remaining part of the "readByte" function, and either tries again, or resolves the NACK issue in some other way.

    polimarte said:
    This is a thing that really i don't understand, as you can see from the screenshot of the Debugger  i don't receive any err_code. It gives me NRF_SUCCESS.

    It is not so easy for me to get a good understanding of exactly what is happening from screenshots, but yes, I see that the variable is 0, indicating success. Apart from the screenshot you should see a debug output if anything else than NRF_SUCCESS was returned. 

    polimarte said:
    So without the loop waiting for the NRFX_TWIM_EVT_DONE my code run over the transition without stopping at the CHECK. What am i missing here? 

    This is correct, and with the current implementation this is as expected - since the nrfx_xfer function is non-blocking, it will return before the slave device has had the chance to NACK ( or otherwise misbehave ). Therefore, you will have to add some kind of wait in between the two calls.
    If you were to change your implementation of this, you could split the function into multiple parts, that start a timer when they conclude - and when this delay timer expires, it checks if the previous transfer was successful - and then uses the result of this check to either proceeds with the transactions, or retransmit.

    polimarte said:
    I don't have further expanation on the datasheet about how the handle NACK events unfortunately.

    I did a quick search for the part number MAX32664 and found this datasheet. Is this the part you are working with?
    Please take a look at page 18 and 19, as they give a more detailed, plain-text, description of both a read and a write sequence.
    Among other things, it describes that the device needs a given CMD_DELAY to ready data for transfer - this could for example lead the device to NACK a request, since it is not yet ready to proceed with a transfer. 

    Best regards,
    Karl

  • Hi Karl! 

    While this implementation might function, you have no way to know how long this loop will take to complete - the time will vary for each call to your function.

    Yes you are right, i forgot the possibility of faulty interaction of the interrupts with timer variables.

    Yes, and then have something else ( such as a retransmit ) happen if the NACK flag is set.

    I have modified my code in order to retransmit at the arrival of the twim_tx_failed (NACK event) or at the timeout of the timer.

    void twim_handler(nrfx_twim_evt_t const * p_event, void * p_context)
    {
    
        size_t primaryBufferSize = p_event->xfer_desc.primary_length;
        uint8_t *bytesTransferred = p_event->xfer_desc.p_primary_buf;
        size_t secondaryBufferSize = p_event->xfer_desc.secondary_length;
        uint8_t *bytesRead = p_event->xfer_desc.p_secondary_buf;
    
        switch (p_event->type)
        {
            case NRFX_TWIM_EVT_DONE:
        
                NRF_LOG_INFO("STATE OF BUFFERS IN EVENT HANDLER:");
                NRF_LOG_INFO("\t Size of primary buffer: %d", primaryBufferSize);
                NRF_LOG_INFO("\t Size of secondary buffer: %d", secondaryBufferSize);
              
    					if (p_event->xfer_desc.type == NRFX_TWIM_XFER_TX)
                {
                    twim_tx_done = true;
                }
              
    					if (p_event->xfer_desc.type == NRFX_TWIM_XFER_RX)
                {
                    twim_rx_done = true;
                }
                break;
            
    		case NRFX_TWIM_EVT_ADDRESS_NACK:
    						if (p_event->xfer_desc.type == NRFX_TWIM_XFER_TX)
                {
                    twim_tx_failed = true;
                }
              
    						if (p_event->xfer_desc.type == NRFX_TWIM_XFER_RX)
                {
                     twim_rx_failed = true;
                }
                NRF_LOG_INFO("Received NACK after sending address!");
                break;
            
    		case NRFX_TWIM_EVT_DATA_NACK:
                NRF_LOG_INFO("Received NACK after sending data.");
                break;
            default:
                break;
        }
    
    	}
    	
    uint8_t readByte( uint8_t familyByte, uint8_t indexByte)
    {
        
    	
    		uint8_t flag_retry=1;
    	
    		nrfx_err_t err_code;
    
    		uint8_t StatusByte;    
            uint8_t ReturnByte;
       
            uint8_t rx_buffer[2];
    		size_t rx_lenght =2;
    
    		uint8_t tx_buffer[] = {familyByte, indexByte};    
    		size_t tx_lenght = sizeof(familyByte) + sizeof(indexByte);
    
    		while(flag_retry==1){
    		uint32_t timeout = OXI_TWI_TIMEOUT;
    		nrfx_twim_xfer_desc_t tx_xfer = NRFX_TWIM_XFER_DESC_TX(OXI_ADDRESS_W, tx_buffer, tx_lenght);
    		err_code = nrfx_twim_xfer(&twim_instance, &tx_xfer, 0);
    		nrf_delay_ms(6);
    		APP_ERROR_CHECK(err_code);
    		
    		while((!twim_tx_done) && --timeout && 	twim_tx_failed == false);
    		if(!timeout) err_code=NRF_ERROR_TIMEOUT;
    		APP_ERROR_CHECK(err_code);
    		if(twim_tx_done) flag_retry=0;
    		if(twim_tx_failed) flag_retry=1;
    		}
    		
    
    		flag_retry=1;
    		
    		
    		while(flag_retry==1){
    		uint32_t timeout = OXI_TWI_TIMEOUT;
    			
    		nrfx_twim_xfer_desc_t rx_xfer = NRFX_TWIM_XFER_DESC_TX(OXI_ADDRESS_W, rx_buffer, rx_lenght);
    		err_code = nrfx_twim_xfer(&twim_instance, &rx_xfer, 0);
    		APP_ERROR_CHECK(err_code);
    		
    		while((!twim_rx_done) && --timeout && 	twim_rx_failed == false);
    		if(!timeout) err_code=NRF_ERROR_TIMEOUT;
    		APP_ERROR_CHECK(err_code);
    		if(twim_rx_done) flag_retry=0;
    		if(twim_tx_failed) flag_retry=1;
    		}
    		
    		StatusByte = rx_buffer[0];
            NRF_LOG_INFO("Status Byte: %lu \n",StatusByte);
        
    		ReturnByte = rx_buffer[1];  
    		return ReturnByte;
    }

    In this way till flag_retry is set the transaction starts again or abort in case of timeout with APP_ERROR_CHECK. I add also a delay after the transaction but still i am stack with NACK.

    This is correct, and with the current implementation this is as expected - since the nrfx_xfer function is non-blocking, it will return before the slave device has had the chance to NACK ( or otherwise misbehave ).

    Maybe i am missing something but i have set no flags on NRFX_TWIM_XFER_TX, allowing so the STOP condition. nrfx_twim_xfer without NRFX_TWIM_FLAG_TX_NO_STOP is blocking, suitable with the waiting for the twim_tx_done to be set, correct? 

    Apart from the screenshot you should see a debug output if anything else than NRF_SUCCESS was returned. 

    I have the RTT viewer which display the NACK and the TWIM viewer where the TX starts and the AMOUNT of bytes transferred is always 0. Where i can catch and show you other useful debug outputs? 

    I did a quick search for the part number MAX32664 and found this datasheet. Is this the part you are working with?

    Yes, i am working on it!

    Among other things, it describes that the device needs a given CMD_DELAY to ready data for transfer - this could for example lead the device to NACK a request, since it is not yet ready to proceed with a transfer. 

    I have read this, i have considered the CMD_DELAY between TX and RX solved by the possibility to have a check on the transaction state, having twim_tx_done set and the STOP condition.

    it describes that the device needs a given CMD_DELAY to ready data for transfer - this could for example lead the device to NACK a request, since it is not yet ready to proceed with a transfer. 

    I tryied also to insert some delay after any twim_xfer, to give time to the transaction to be done.

     Thanks,

    polimarte

Related